mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
Compare commits
42
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3da05eff71 | ||
|
|
489d483362 | ||
|
|
a305a6c1c6 | ||
|
|
635fb8882b | ||
|
|
fe6f49632c | ||
|
|
c3546577b6 | ||
|
|
c913951f7b | ||
|
|
3902b42096 | ||
|
|
ae573f941a | ||
|
|
20ea54cf0c | ||
|
|
0b4e78c0b2 | ||
|
|
57844a07e2 | ||
|
|
ba18551d6e | ||
|
|
c7b0d77970 | ||
|
|
1af19eabf0 | ||
|
|
32ea0869e5 | ||
|
|
e04007a7c6 | ||
|
|
1a3a15e4cf | ||
|
|
679adbfa5e | ||
|
|
d285efe3b5 | ||
|
|
a64c8d3205 | ||
|
|
82c52c3fab | ||
|
|
dd1e90fcd7 | ||
|
|
ef0155636a | ||
|
|
9d550fbffa | ||
|
|
fcef691e0a | ||
|
|
839c172612 | ||
|
|
9f49ac8249 | ||
|
|
fde550ef66 | ||
|
|
2b3993a595 | ||
|
|
de3e86791c | ||
|
|
6f66f77b09 | ||
|
|
d853aba955 | ||
|
|
cd62693792 | ||
|
|
cb4c8f3878 | ||
|
|
4d371d5f5f | ||
|
|
72963b01e1 | ||
|
|
80a18e82b7 | ||
|
|
574e687734 | ||
|
|
c725ec50ff | ||
|
|
c6f6e306e0 | ||
|
|
b847135321 |
No files matched your search
@@ -567,6 +567,8 @@ if(BUILD_TESTING)
|
||||
|
||||
add_test(NAME texture_cache_layered_image
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --layered-image-only)
|
||||
add_test(NAME host_image_allocation
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --host-image-allocation-only)
|
||||
add_test(NAME texture_cache_image_views
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --image-view-cache-only)
|
||||
add_test(NAME texture_cache_storage_sampled
|
||||
|
||||
+2
-18
@@ -1,6 +1,5 @@
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "kytyGitVersion.h"
|
||||
@@ -12,25 +11,10 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && \
|
||||
KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
constexpr int PRINT_STACK_FROM = 4;
|
||||
#else
|
||||
constexpr int PRINT_STACK_FROM = 2;
|
||||
#endif
|
||||
|
||||
static std::string BuildFatalReport(const char* title, std::string_view text, const char* file,
|
||||
int line) {
|
||||
DebugStack stack;
|
||||
DebugStack::Trace(&stack);
|
||||
|
||||
std::string report = "--- Build ---\n" KYTY_BUILD_LABEL "\n--- Stack Trace ---\n";
|
||||
for (int i = PRINT_STACK_FROM; i < stack.depth; i++) {
|
||||
report += fmt::format("[{}] {:016x}\n", i - PRINT_STACK_FROM,
|
||||
static_cast<uint64_t>(stack.GetAddr(i)));
|
||||
}
|
||||
report += fmt::format("{}\n{} in {}:{}\n", title, text, file, line);
|
||||
return report;
|
||||
return fmt::format("--- Build ---\n{}\n{}\n{} in {}:{}\n", KYTY_BUILD_LABEL, title, text, file,
|
||||
line);
|
||||
}
|
||||
|
||||
static int DbgReport(const char* title, std::string_view text, const char* file, int line) {
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "common/platform/sysDbg.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void DebugStack::Print(int from, [[maybe_unused]] bool with_name) const {
|
||||
for (int i = from; i < depth; i++) {
|
||||
printf("[%d] %016" PRIx64 "\n", i - from, static_cast<uint64_t>(GetAddr(i)));
|
||||
}
|
||||
}
|
||||
|
||||
void DebugStack::Trace(DebugStack* stack) {
|
||||
stack->depth = DEBUG_MAX_STACK_DEPTH;
|
||||
SysStackWalk(stack->stack.data(), &stack->depth);
|
||||
}
|
||||
|
||||
std::string Debug::GetCompiler() {
|
||||
#if KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
return "clang";
|
||||
|
||||
+1
-26
@@ -2,42 +2,17 @@
|
||||
#define KYTY_COMMON_DEBUG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && \
|
||||
KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
constexpr int DEBUG_MAX_STACK_DEPTH = 20;
|
||||
#else
|
||||
constexpr int DEBUG_MAX_STACK_DEPTH = 15;
|
||||
#endif
|
||||
|
||||
namespace Debug {
|
||||
std::string GetCompiler();
|
||||
std::string GetLinker();
|
||||
std::string GetBitness();
|
||||
} // namespace Debug
|
||||
|
||||
struct DebugStack {
|
||||
int depth {0};
|
||||
std::array<void*, DEBUG_MAX_STACK_DEPTH> stack {};
|
||||
|
||||
[[nodiscard]] uintptr_t GetAddr(int i) const { return reinterpret_cast<uintptr_t>(stack[i]); }
|
||||
|
||||
void Print(int from, bool with_name = true) const;
|
||||
|
||||
void CopyTo(DebugStack* s) const {
|
||||
std::memcpy(s->stack.data(), stack.data(), sizeof(void*) * depth);
|
||||
s->depth = depth;
|
||||
}
|
||||
|
||||
static void Trace(DebugStack* stack);
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_DEBUG_H_ */
|
||||
@@ -30,8 +30,6 @@ struct sys_dbg_stack_info_t {
|
||||
#endif
|
||||
};
|
||||
|
||||
void SysStackWalk(void** stack, int* depth);
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s); // NOLINT(google-runtime-references)
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack); // NOLINT(google-runtime-references)
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s); // NOLINT(google-runtime-references)
|
||||
|
||||
#endif /* KYTY_COMMON_PLATFORM_SYSDBG_H_ */
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <execinfo.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
@@ -17,56 +16,6 @@
|
||||
#include <libgen.h> // POSIX basename() lives here on macOS, not in <cstring>
|
||||
#endif
|
||||
|
||||
// Avoid unwinding a guest-owned stack.
|
||||
static bool OnOwnStack() {
|
||||
const char* probe = reinterpret_cast<const char*>(&probe);
|
||||
|
||||
pthread_attr_t attr {};
|
||||
#if defined(__APPLE__)
|
||||
const auto* top = static_cast<const char*>(pthread_get_stackaddr_np(pthread_self()));
|
||||
const auto size = pthread_get_stacksize_np(pthread_self());
|
||||
(void)attr;
|
||||
return top != nullptr && size != 0 && probe < top && probe >= top - size;
|
||||
#else
|
||||
if (pthread_getattr_np(pthread_self(), &attr) != 0) {
|
||||
return false;
|
||||
}
|
||||
void* base = nullptr;
|
||||
size_t size = 0;
|
||||
const bool ok = pthread_attr_getstack(&attr, &base, &size) == 0 && base != nullptr && size != 0;
|
||||
pthread_attr_destroy(&attr);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
const auto* low = static_cast<const char*>(base);
|
||||
return probe >= low && probe < low + size;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SysStackWalk(void** stack, int* depth) {
|
||||
if (stack == nullptr || depth == nullptr || *depth <= 0) {
|
||||
if (depth != nullptr) {
|
||||
*depth = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!OnOwnStack()) {
|
||||
*depth = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const int n = ::backtrace(stack, *depth);
|
||||
*depth = (n < 0 ? 0 : n);
|
||||
}
|
||||
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack) {
|
||||
printf("stack: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast<uint64_t>(stack.commited_addr),
|
||||
static_cast<uint64_t>(stack.commited_size));
|
||||
printf("code: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast<uint64_t>(stack.code_addr),
|
||||
static_cast<uint64_t>(stack.code_size));
|
||||
}
|
||||
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s) {
|
||||
pid_t pid = getpid();
|
||||
|
||||
|
||||
@@ -15,18 +15,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/platform/sysDbg.h"
|
||||
|
||||
void SysStackWalk(void** stack, int* depth) {
|
||||
*depth = static_cast<int>(CaptureStackBackTrace(0, static_cast<DWORD>(*depth), stack, nullptr));
|
||||
}
|
||||
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack) {
|
||||
printf("stack: (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64
|
||||
", %" PRIu64 ")\n",
|
||||
static_cast<uint64_t>(stack.reserved_addr), static_cast<uint64_t>(stack.reserved_size),
|
||||
static_cast<uint64_t>(stack.guard_addr), static_cast<uint64_t>(stack.guard_size),
|
||||
static_cast<uint64_t>(stack.commited_addr), static_cast<uint64_t>(stack.commited_size));
|
||||
}
|
||||
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s) {
|
||||
MEMORY_BASIC_INFORMATION mbi {};
|
||||
[[maybe_unused]] size_t ss = VirtualQuery(&mbi, &mbi, sizeof(mbi));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "common/threads.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
@@ -315,8 +315,17 @@ static void HwCtxSetDepthBoundsRegister(CommandProcessor& cp, uint32_t cmd_offse
|
||||
}
|
||||
}
|
||||
|
||||
static void HwCtxIgnoreDepthMetadataRegister([[maybe_unused]] uint32_t cmd_offset,
|
||||
[[maybe_unused]] uint32_t value) {}
|
||||
static void HwCtxSetDepthMetadataRegister(CommandProcessor& cp, uint32_t cmd_offset,
|
||||
uint32_t value) {
|
||||
if (cmd_offset == Pm4::DB_RENDER_OVERRIDE) {
|
||||
HW::DepthRenderOverride control;
|
||||
control.force_z_valid = (value & 0x20000000u) != 0;
|
||||
control.force_z_dirty = (value & 0x08000000u) != 0;
|
||||
control.force_stencil_valid = (value & 0x40000000u) != 0;
|
||||
control.force_stencil_dirty = (value & 0x10000000u) != 0;
|
||||
cp.GetCtx().SetDepthRenderOverride(control);
|
||||
}
|
||||
}
|
||||
|
||||
static void HwCtxIgnoreFovWindow([[maybe_unused]] uint32_t value) {}
|
||||
|
||||
@@ -389,7 +398,7 @@ KYTY_HW_CTX_PARSER(HwCtxSetDepthMetadataRegisters) {
|
||||
auto num_values = KYTY_PM4_LEN(cmd_id) - 2u;
|
||||
|
||||
for (uint32_t i = 0; i < num_values; i++) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset + i, buffer[i]);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset + i, buffer[i]);
|
||||
}
|
||||
|
||||
return num_values;
|
||||
@@ -3003,28 +3012,28 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
HwCtxIgnoreCbDccControl(value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::DB_COUNT_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
for (auto cmd_offset = Pm4::DB_SRESULTS_COMPARE_STATE0;
|
||||
cmd_offset <= Pm4::DB_SRESULTS_COMPARE_STATE1; cmd_offset++) {
|
||||
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
}
|
||||
g_hw_ctx_indirect_func[Pm4::DB_RENDER_OVERRIDE] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::DB_RENDER_OVERRIDE2] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::DB_DFSM_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::DB_RMI_L2_CACHE_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::CB_RMI_GL2_CACHE_CONTROL] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDepthMetadataRegister(cmd_offset, value);
|
||||
HwCtxSetDepthMetadataRegister(cp, cmd_offset, value);
|
||||
};
|
||||
g_hw_ctx_indirect_func[Pm4::TA_BC_BASE_ADDR] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreBorderColorTableAddr(cmd_offset, value);
|
||||
|
||||
@@ -202,6 +202,13 @@ struct RenderControl {
|
||||
uint8_t copy_sample = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderOverride {
|
||||
bool force_z_valid = false;
|
||||
bool force_z_dirty = false;
|
||||
bool force_stencil_valid = false;
|
||||
bool force_stencil_dirty = false;
|
||||
};
|
||||
|
||||
struct GdsOaCounter {
|
||||
uint32_t counter = 0;
|
||||
uint32_t address = 0;
|
||||
@@ -809,6 +816,12 @@ public:
|
||||
void SetClipControl(const ClipControl& control) { m_clip_control = control; }
|
||||
[[nodiscard]] const RenderControl& GetRenderControl() const { return m_render_control; }
|
||||
void SetRenderControl(const RenderControl& control) { m_render_control = control; }
|
||||
[[nodiscard]] const DepthRenderOverride& GetDepthRenderOverride() const {
|
||||
return m_depth_render_override;
|
||||
}
|
||||
void SetDepthRenderOverride(const DepthRenderOverride& control) {
|
||||
m_depth_render_override = control;
|
||||
}
|
||||
[[nodiscard]] const DepthControl& GetDepthControl() const { return m_depth_control; }
|
||||
void SetDepthControl(const DepthControl& control) { m_depth_control = control; }
|
||||
[[nodiscard]] const ModeControl& GetModeControl() const { return m_mode_control; }
|
||||
@@ -904,15 +917,16 @@ private:
|
||||
|
||||
uint32_t m_shader_stages = 0;
|
||||
|
||||
DepthRenderTarget m_depth_render_target;
|
||||
RenderControl m_render_control;
|
||||
DepthControl m_depth_control;
|
||||
StencilControl m_stencil_control;
|
||||
StencilMask m_stencil_mask;
|
||||
float m_depth_clear_value = 0.0f;
|
||||
float m_depth_bounds_min = 0.0f;
|
||||
float m_depth_bounds_max = 1.0f;
|
||||
uint8_t m_stencil_clear_value = 0;
|
||||
DepthRenderTarget m_depth_render_target;
|
||||
RenderControl m_render_control;
|
||||
DepthRenderOverride m_depth_render_override;
|
||||
DepthControl m_depth_control;
|
||||
StencilControl m_stencil_control;
|
||||
StencilMask m_stencil_mask;
|
||||
float m_depth_clear_value = 0.0f;
|
||||
float m_depth_bounds_min = 0.0f;
|
||||
float m_depth_bounds_max = 1.0f;
|
||||
uint8_t m_stencil_clear_value = 0;
|
||||
|
||||
ModeControl m_mode_control;
|
||||
PolyOffset m_poly_offset;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct VulkanImage;
|
||||
struct VulkanMemory;
|
||||
|
||||
inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3;
|
||||
|
||||
@@ -32,6 +31,7 @@ struct GraphicContext {
|
||||
bool compute_subgroup_size_control_enabled = false;
|
||||
bool sample_rate_shading_enabled = false;
|
||||
bool attachment_feedback_loop_enabled = false;
|
||||
bool provoking_vertex_last_enabled = false;
|
||||
bool supports_block_texel_view = false;
|
||||
bool mesh_shader_enabled = false;
|
||||
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
@@ -117,12 +117,6 @@ private:
|
||||
m_image_format_properties;
|
||||
};
|
||||
|
||||
struct VulkanMemory {
|
||||
vk::MemoryPropertyFlags property = {};
|
||||
vk::MemoryPropertyFlags preferred_property = {};
|
||||
VmaAllocation allocation = nullptr;
|
||||
};
|
||||
|
||||
struct VulkanImageState {
|
||||
vk::PipelineStageFlags2 pl_stage = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
vk::AccessFlags2 access_mask = vk::AccessFlagBits2::eNone;
|
||||
@@ -144,7 +138,7 @@ struct VulkanImage {
|
||||
vk::Image image = nullptr;
|
||||
VulkanImageState state;
|
||||
std::vector<VulkanImageState> subresource_states;
|
||||
Graphics::VulkanMemory memory;
|
||||
VmaAllocation allocation = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
|
||||
#include "common/platform/sysDbg.h"
|
||||
#include "graphics/host_gpu/regionDefinitions.h"
|
||||
#include "kernel/memory.h"
|
||||
|
||||
@@ -21,10 +20,7 @@
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#elif defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@@ -53,21 +49,6 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
|
||||
std::fputs("PageManager fail-fast: ", stderr);
|
||||
std::fputs(reason != nullptr ? reason : "invalid page state", stderr);
|
||||
std::fputc('\n', stderr);
|
||||
#if !defined(__APPLE__)
|
||||
void* frames[16] {};
|
||||
int frame_count = static_cast<int>(std::size(frames));
|
||||
SysStackWalk(frames, &frame_count);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
const auto image_base = reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
||||
for (int i = 0; i < frame_count; i++) {
|
||||
const auto address = reinterpret_cast<uintptr_t>(frames[i]);
|
||||
std::fprintf(stderr, " frame[%d]=0x%016" PRIxPTR " image_rva=0x%016" PRIxPTR "\n", i,
|
||||
address, address >= image_base ? address - image_base : 0);
|
||||
}
|
||||
#else
|
||||
::backtrace_symbols_fd(frames, frame_count, STDERR_FILENO);
|
||||
#endif
|
||||
#endif
|
||||
std::fflush(stderr);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
TerminateProcess(GetCurrentProcess(), static_cast<UINT>(EXCEPTION_NONCONTINUABLE_EXCEPTION));
|
||||
|
||||
+6
-4
@@ -277,10 +277,10 @@ void BufferCache::ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write)
|
||||
"memory invalidation");
|
||||
},
|
||||
[&](uint64_t address, uint64_t bytes) noexcept {
|
||||
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
|
||||
m_gpu_modified_ranges.ForEachIntersection(address, bytes, [&](RangeSet::Range range) {
|
||||
copies.push_back(
|
||||
{&buffer, buffer.Offset(range.address), range.address, range.size});
|
||||
}
|
||||
});
|
||||
});
|
||||
if (!copies.empty()) {
|
||||
DownloadBufferMemory(copies);
|
||||
@@ -340,10 +340,12 @@ BufferCache::OverlapResult BufferCache::ResolveOverlaps(uint64_t vaddr, uint64_t
|
||||
end = std::max(end, buffer_end);
|
||||
if (!has_stream_leap && (stream_score += buffer.StreamScore()) > StreamLeapThreshold) {
|
||||
has_stream_leap = true;
|
||||
if (expands_right) {
|
||||
// Fix the shadPS4 bug that reserves space opposite to the incoming stream's growth.
|
||||
// The old buffer extending left of the request predicts growth to the right, and vice versa.
|
||||
if (expands_left) {
|
||||
end += std::min(StreamLeapSize, PageTable::kAddressSpaceSize - end);
|
||||
}
|
||||
if (expands_left) {
|
||||
if (expands_right) {
|
||||
const auto minimum = CACHING_PAGESIZE * 2;
|
||||
if (begin > minimum) {
|
||||
begin -= std::min(StreamLeapSize, begin - minimum);
|
||||
|
||||
+3
-7
@@ -187,15 +187,11 @@ TextureCache::TextureCache(GraphicContext& graphics, CommandScheduler& scheduler
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache() {
|
||||
std::vector<ImageId> registered;
|
||||
m_slot_images.ForEach([&](ImageId id, const Image& image) {
|
||||
if (image.registered) {
|
||||
registered.push_back(id);
|
||||
UnregisterImage(id);
|
||||
}
|
||||
});
|
||||
for (const auto id: registered) {
|
||||
UnregisterImage(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextureCache::SameBacking(const ImageInfo& cached, const ImageInfo& requested,
|
||||
@@ -1334,8 +1330,8 @@ ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool e
|
||||
if (!GuestRange {address, size}.Valid()) {
|
||||
return {};
|
||||
}
|
||||
std::scoped_lock lock {m_lock};
|
||||
std::vector<ImageId> matches;
|
||||
std::scoped_lock lock {m_lock};
|
||||
ImageIds matches;
|
||||
for (const auto id: FindImagesInRegion(address, size, false)) {
|
||||
auto owner = m_slot_images.try_get(id);
|
||||
if (owner == nullptr || owner->info.data.address != address) {
|
||||
|
||||
@@ -41,16 +41,13 @@ static bool DccAlphaOnMsb(const HW::ColorInfo& info) {
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderColorInfo& r,
|
||||
void RenderExecutor::ResolveRenderColorTarget(CommandBuffer& buffer, RenderColorInfo& r,
|
||||
uint32_t render_target_slice_offset,
|
||||
uint32_t render_target_slot, bool ignore_target_mask,
|
||||
uint32_t rt_slot, bool ignore_target_mask,
|
||||
bool exact_format) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
|
||||
const auto rt_slot = (render_target_slot == UINT32_MAX ? render_target_first_bound_slot(buffer)
|
||||
: render_target_slot);
|
||||
const auto& rt = hw.GetRenderTarget(rt_slot);
|
||||
auto mask = render_target_mask_slot(hw.GetRenderTargetMask(), rt_slot);
|
||||
if (ignore_target_mask && rt.base.addr != 0 && mask == 0) {
|
||||
@@ -206,17 +203,14 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
" layer=%u/%u\n",
|
||||
rt.attrib3.dimension, rt.attrib3.depth, view.base_layer, view.image_layers);
|
||||
}
|
||||
if (tile) {
|
||||
if (volume || texture_tile) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else {
|
||||
pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
|
||||
}
|
||||
if (pitch == 0) {
|
||||
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
|
||||
}
|
||||
// PPSA28068: the linear EULA target and its sampled view must share the padded pitch.
|
||||
if (!tile || volume || texture_tile) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else {
|
||||
pitch = width;
|
||||
pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
|
||||
}
|
||||
if (pitch == 0) {
|
||||
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
|
||||
}
|
||||
|
||||
TileSizeOffset mip_sizes[16] {};
|
||||
|
||||
@@ -208,13 +208,7 @@ void CommandScheduler::Wait(uint64_t tick) {
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations() {
|
||||
PopPendingOperations(true);
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations(bool refresh_gpu_tick) {
|
||||
if (refresh_gpu_tick) {
|
||||
m_master.Refresh();
|
||||
}
|
||||
m_master.Refresh();
|
||||
for (;;) {
|
||||
PendingOperation operation;
|
||||
{
|
||||
|
||||
@@ -81,7 +81,6 @@ private:
|
||||
};
|
||||
|
||||
void BeginNext();
|
||||
void PopPendingOperations(bool refresh_gpu_tick);
|
||||
void PriorityOperationsThread(std::stop_token stop);
|
||||
void RunOperation(Common::UniqueFunction<void>&& operation);
|
||||
|
||||
|
||||
@@ -478,7 +478,6 @@ static void McCheck(const HW::ModeControl& c) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(c.provoking_vtx_last != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.persp_corr_dis != false);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
@@ -96,53 +97,17 @@ static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
|
||||
return vk::Format::eUndefined;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderDepthInfo& r) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
(void)submit_id;
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& dc = hw.GetDepthControl();
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const bool depth_active = dc.z_enable || dc.depth_bounds_enable ||
|
||||
rc.depth_clear_enable || rc.copy_depth_to_color;
|
||||
const bool stencil_active =
|
||||
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
static TextureCache::ImageDesc MakeDepthTargetDesc(const CommandBuffer& buffer,
|
||||
const HW::DepthRenderTarget& z,
|
||||
bool write_buffer = false) {
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const auto depth_address = write_buffer ? z.z_write_base_addr : z.z_read_base_addr;
|
||||
const auto stencil_address =
|
||||
write_buffer ? z.stencil_write_base_addr : z.stencil_read_base_addr;
|
||||
if (!z.z_info.HasValidTextureCompatibility() ||
|
||||
!z.stencil_info.HasValidTextureCompatibility()) {
|
||||
DepthFatal("invalid PS5 depth texture-compatibility encoding");
|
||||
}
|
||||
const bool attachment_unbound =
|
||||
z.z_info.format == Prospero::DepthFormat::kInvalid &&
|
||||
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
|
||||
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
|
||||
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
|
||||
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
|
||||
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
|
||||
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
|
||||
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
|
||||
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
|
||||
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
|
||||
z.htile_data_base_addr == 0 &&
|
||||
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
|
||||
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
|
||||
// presence alone must not manufacture a depth attachment.
|
||||
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
|
||||
z.size.y_max == 0;
|
||||
if (attachment_unbound) {
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const bool has_htile = z.z_info.htile_acceleration;
|
||||
const bool unsupported_shading_rate_encoding =
|
||||
z.shading_rate_encoding > 1 || (z.shading_rate_encoding != 0 && !has_htile);
|
||||
@@ -160,23 +125,16 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
DepthFatal("invalid depth view: base=%u last=%u", z.depth_view.slice_start,
|
||||
z.depth_view.slice_max);
|
||||
}
|
||||
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
|
||||
rc.copy_sample != 0 || z.z_info.expclear_enabled ||
|
||||
z.stencil_info.expclear_enabled || z.z_info.partially_resident ||
|
||||
if (z.z_info.expclear_enabled || z.stencil_info.expclear_enabled ||
|
||||
z.z_info.partially_resident ||
|
||||
z.stencil_info.partially_resident || z.z_info.max_mip_level != 0 ||
|
||||
z.depth_view.current_mip_level != 0 || unsupported_shading_rate_encoding ||
|
||||
z.z_read_base_addr == 0 ||
|
||||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
|
||||
(z.z_read_base_addr & 0xffffu) != 0 ||
|
||||
dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways)) {
|
||||
depth_address == 0 || (depth_address & 0xffffu) != 0) {
|
||||
DepthFatal("unsupported depth register state");
|
||||
}
|
||||
if (has_stencil) {
|
||||
if (z.stencil_info.format != Prospero::StencilFormat::k8UInt || !htile_stencil_compat ||
|
||||
z.stencil_read_base_addr == 0 ||
|
||||
(!z.depth_view.stencil_write_disable &&
|
||||
z.stencil_write_base_addr != z.stencil_read_base_addr) ||
|
||||
(z.stencil_read_base_addr & 0xffffu) != 0) {
|
||||
stencil_address == 0 || (stencil_address & 0xffffu) != 0) {
|
||||
DepthFatal("unsupported stencil attachment state");
|
||||
}
|
||||
} else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0) {
|
||||
@@ -229,13 +187,93 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
const auto depth_backing_size = depth_size.size * view.image_layers;
|
||||
const auto stencil_backing_size = stencil_size.size * view.image_layers;
|
||||
const auto htile_backing_size = htile_size.size * view.image_layers;
|
||||
if (!GuestRange {z.z_read_base_addr, depth_backing_size}.Valid() ||
|
||||
(has_stencil && !GuestRange {z.stencil_read_base_addr, stencil_backing_size}.Valid()) ||
|
||||
if (!GuestRange {depth_address, depth_backing_size}.Valid() ||
|
||||
(has_stencil && !GuestRange {stencil_address, stencil_backing_size}.Valid()) ||
|
||||
(has_htile && !GuestRange {z.htile_data_base_addr, htile_backing_size}.Valid())) {
|
||||
DepthFatal("layered depth backing range is invalid");
|
||||
}
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.type = TextureCache::BindingType::DepthTarget;
|
||||
desc.info.data = {depth_address, depth_backing_size};
|
||||
desc.info.stencil =
|
||||
has_stencil ? GuestRange {stencil_address, stencil_backing_size} : GuestRange {};
|
||||
desc.info.pixel_format = format;
|
||||
desc.info.guest_format = guest_format;
|
||||
desc.info.type = Prospero::ImageType::kColor2D;
|
||||
desc.info.extent = {width, height, 1};
|
||||
desc.info.resources = {1, view.image_layers};
|
||||
desc.info.pitch = pitch;
|
||||
desc.info.bytes_per_block = bytes;
|
||||
desc.info.samples = samples;
|
||||
desc.info.tile_mode = Prospero::TileMode::kDepth;
|
||||
desc.info.mip_layout[0] = {0, depth_backing_size, pitch, height};
|
||||
desc.info.metadata.range =
|
||||
has_htile ? GuestRange {z.htile_data_base_addr, htile_backing_size} : GuestRange {};
|
||||
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
|
||||
desc.info.metadata.stencil_compressed =
|
||||
has_stencil && has_htile && !z.stencil_info.htile_stencil_disabled;
|
||||
desc.view_info.format = format;
|
||||
desc.view_info.type =
|
||||
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||
desc.view_info.aspect = ImageViewOps::DepthAspectMask(format);
|
||||
desc.view_info.base_level = 0;
|
||||
desc.view_info.level_count = 1;
|
||||
desc.view_info.base_layer = view.base_layer;
|
||||
desc.view_info.layer_count = view.layer_count;
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
return desc;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& r) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& dc = hw.GetDepthControl();
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const bool depth_active = dc.z_enable || dc.depth_bounds_enable ||
|
||||
rc.depth_clear_enable || rc.copy_depth_to_color;
|
||||
const bool stencil_active =
|
||||
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
const bool attachment_unbound =
|
||||
z.z_info.format == Prospero::DepthFormat::kInvalid &&
|
||||
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
|
||||
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
|
||||
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
|
||||
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
|
||||
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
|
||||
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
|
||||
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
|
||||
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
|
||||
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
|
||||
z.htile_data_base_addr == 0 &&
|
||||
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
|
||||
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
|
||||
// presence alone must not manufacture a depth attachment.
|
||||
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
|
||||
z.size.y_max == 0;
|
||||
if (attachment_unbound) {
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
|
||||
rc.copy_sample != 0 || dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
|
||||
(has_stencil && !z.depth_view.stencil_write_disable &&
|
||||
z.stencil_write_base_addr != z.stencil_read_base_addr)) {
|
||||
DepthFatal("unsupported depth register state");
|
||||
}
|
||||
r.desc = MakeDepthTargetDesc(buffer, z);
|
||||
r.depth_clear_enable = rc.depth_clear_enable;
|
||||
r.depth_meta_clear_enable = false;
|
||||
r.depth_load_clear_enable = r.depth_clear_enable;
|
||||
r.depth_clear_value = hw.GetDepthClearValue();
|
||||
r.depth_test_enable = dc.z_enable;
|
||||
@@ -285,41 +323,73 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
r.stencil_dynamic_back = r.stencil_dynamic_front;
|
||||
}
|
||||
}
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.type = TextureCache::BindingType::DepthTarget;
|
||||
desc.info.data = {z.z_read_base_addr, depth_backing_size};
|
||||
desc.info.stencil =
|
||||
has_stencil ? GuestRange {z.stencil_read_base_addr, stencil_backing_size} : GuestRange {};
|
||||
desc.info.pixel_format = format;
|
||||
desc.info.guest_format = guest_format;
|
||||
desc.info.type = Prospero::ImageType::kColor2D;
|
||||
desc.info.extent = {width, height, 1};
|
||||
desc.info.resources = {1, view.image_layers};
|
||||
desc.info.pitch = pitch;
|
||||
desc.info.bytes_per_block = bytes;
|
||||
desc.info.samples = samples;
|
||||
desc.info.tile_mode = Prospero::TileMode::kDepth;
|
||||
desc.info.mip_layout[0] = {0, depth_backing_size, pitch, height};
|
||||
desc.info.metadata.range =
|
||||
has_htile ? GuestRange {z.htile_data_base_addr, htile_backing_size} : GuestRange {};
|
||||
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
|
||||
desc.info.metadata.stencil_compressed =
|
||||
has_stencil && has_htile && !z.stencil_info.htile_stencil_disabled;
|
||||
desc.view_info.format = format;
|
||||
desc.view_info.type =
|
||||
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||
desc.view_info.aspect = ImageViewOps::DepthAspectMask(format);
|
||||
desc.view_info.base_level = 0;
|
||||
desc.view_info.level_count = 1;
|
||||
desc.view_info.base_layer = view.base_layer;
|
||||
desc.view_info.layer_count = view.layer_count;
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
r.desc = std::move(desc);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
r.image_id = cache.FindImage(r.desc);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
r.image_id = cache.FindImage(r.desc);
|
||||
BindRenderTarget(r.image_id);
|
||||
}
|
||||
|
||||
bool RenderExecutor::DepthStencilCopy(CommandBuffer& buffer) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& override = hw.GetDepthRenderOverride();
|
||||
if (hw.GetColorControl().mode != 0) {
|
||||
return false;
|
||||
}
|
||||
const bool depth_copy = override.force_z_dirty && override.force_z_valid &&
|
||||
z.z_info.format != Prospero::DepthFormat::kInvalid &&
|
||||
z.z_read_base_addr != 0 && z.z_write_base_addr != 0 &&
|
||||
z.z_read_base_addr != z.z_write_base_addr;
|
||||
const bool stencil_copy = override.force_stencil_dirty && override.force_stencil_valid &&
|
||||
z.stencil_info.format != Prospero::StencilFormat::kInvalid &&
|
||||
z.stencil_read_base_addr != 0 && z.stencil_write_base_addr != 0 &&
|
||||
z.stencil_read_base_addr != z.stencil_write_base_addr;
|
||||
if (!depth_copy && !stencil_copy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto read_desc = MakeDepthTargetDesc(buffer, z);
|
||||
auto write_desc = MakeDepthTargetDesc(buffer, z, true);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
const auto read_id = cache.FindImage(read_desc);
|
||||
BindRenderTarget(read_id);
|
||||
const auto write_id = cache.FindImage(write_desc);
|
||||
BindRenderTarget(write_id);
|
||||
cache.UpdateImage(read_id);
|
||||
cache.UpdateImage(write_id);
|
||||
cache.MarkGpuWritten(write_id);
|
||||
auto& source = cache.GetImage(read_id);
|
||||
auto& destination = cache.GetImage(write_id);
|
||||
EXIT_IF(read_id == write_id || source.backing.format != destination.backing.format);
|
||||
|
||||
auto& scheduler = m_context.GetCommandScheduler();
|
||||
scheduler.EndRendering();
|
||||
const auto command = scheduler.Current().Handle();
|
||||
const ImageSubresourceRange range {read_desc.view_info.base_level, 1,
|
||||
read_desc.view_info.base_layer,
|
||||
read_desc.view_info.layer_count};
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
||||
range, command);
|
||||
destination.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
||||
range, command);
|
||||
std::array<vk::ImageCopy, 2> regions {};
|
||||
uint32_t count = 0;
|
||||
for (const auto aspect: {vk::ImageAspectFlagBits::eDepth, vk::ImageAspectFlagBits::eStencil}) {
|
||||
if ((aspect == vk::ImageAspectFlagBits::eDepth && !depth_copy) ||
|
||||
(aspect == vk::ImageAspectFlagBits::eStencil && !stencil_copy)) {
|
||||
continue;
|
||||
}
|
||||
auto& region = regions[count++];
|
||||
region.srcSubresource = {aspect, range.base_level, range.base_layer,
|
||||
range.layer_count};
|
||||
region.dstSubresource = region.srcSubresource;
|
||||
region.extent = write_desc.info.extent;
|
||||
}
|
||||
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
destination.backing.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
count, regions.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
|
||||
const auto format = desc.view_info.format;
|
||||
if (format == vk::Format::eUndefined) {
|
||||
|
||||
@@ -23,7 +23,6 @@ struct RenderDepthInfo {
|
||||
TextureCache::ImageDesc desc;
|
||||
bool depth_clear_enable = false;
|
||||
bool depth_load_clear_enable = false;
|
||||
bool depth_meta_clear_enable = false;
|
||||
float depth_clear_value = 0.0f;
|
||||
bool depth_test_enable = false;
|
||||
// Effective draw writes; discovery applies test, target-write and clear controls.
|
||||
|
||||
@@ -661,26 +661,17 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
return;
|
||||
}
|
||||
|
||||
backing.format = info.pixel_format;
|
||||
backing.image_type = HostImageType(info.type);
|
||||
backing.extent = info.extent;
|
||||
backing.layers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
backing.mip_levels = info.resources.levels;
|
||||
backing.samples = info.samples;
|
||||
backing.flags = ImageCreateFlags(graphics, info);
|
||||
backing.usage = ImageUsageFlags(graphics, info);
|
||||
|
||||
vk::ImageCreateInfo create {};
|
||||
create.flags = backing.flags;
|
||||
create.imageType = backing.image_type;
|
||||
create.extent = backing.extent;
|
||||
create.mipLevels = backing.mip_levels;
|
||||
create.arrayLayers = backing.layers;
|
||||
create.format = backing.format;
|
||||
create.flags = ImageCreateFlags(graphics, info);
|
||||
create.imageType = HostImageType(info.type);
|
||||
create.extent = info.extent;
|
||||
create.mipLevels = info.resources.levels;
|
||||
create.arrayLayers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
create.format = info.pixel_format;
|
||||
create.tiling = vk::ImageTiling::eOptimal;
|
||||
create.initialLayout = backing.state.layout;
|
||||
create.usage = backing.usage;
|
||||
create.samples = vulkan_sample_count(backing.samples);
|
||||
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||
create.usage = ImageUsageFlags(graphics, info);
|
||||
create.samples = vulkan_sample_count(info.samples);
|
||||
|
||||
vk::ImageFormatProperties properties {};
|
||||
if (graphics.GetImageFormatProperties(create.format, create.imageType, create.tiling,
|
||||
@@ -691,10 +682,9 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
"flags=0x%x samples=%u\n",
|
||||
static_cast<int>(create.format), static_cast<int>(create.imageType),
|
||||
static_cast<vk::ImageUsageFlags::MaskType>(create.usage),
|
||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), backing.samples);
|
||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), info.samples);
|
||||
}
|
||||
|
||||
backing.memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
||||
if (!graphics.CreateImage(create, backing)) {
|
||||
EXIT("failed to create image: extent=%ux%ux%u format=%d layers=%u levels=%u\n",
|
||||
create.extent.width, create.extent.height, create.extent.depth,
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/host_gpu/renderer/image/tiler.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
namespace {
|
||||
@@ -27,13 +27,13 @@ constexpr Prospero::ColorComponentMapping kRenderTargetColorMappings[4][4] = {
|
||||
Prospero::ColorMappingArgb},
|
||||
};
|
||||
|
||||
struct RenderTargetHostFormatInfo {
|
||||
struct HostFormatInfo {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
Prospero::ColorComponentMapping host_to_storage;
|
||||
};
|
||||
|
||||
RenderTargetHostFormatInfo ResolveRenderTargetHostFormat(Prospero::BufferFormat guest_format,
|
||||
Prospero::ChannelOrder order) {
|
||||
HostFormatInfo ResolveHostFormat(Prospero::BufferFormat guest_format,
|
||||
Prospero::ChannelOrder order) {
|
||||
if (order == Prospero::ChannelOrder::kAlt) {
|
||||
switch (guest_format) {
|
||||
case Prospero::BufferFormat::k8_8_8_8UNorm:
|
||||
@@ -47,17 +47,77 @@ RenderTargetHostFormatInfo ResolveRenderTargetHostFormat(Prospero::BufferFormat
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
const auto format = VulkanFormat(guest_format);
|
||||
switch (guest_format) {
|
||||
case Prospero::BufferFormat::k5_5_5_1UNorm:
|
||||
return {vk::Format::eA1R5G5B5UnormPack16, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k5_5_5_1UNorm: return {format, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k1_5_5_5UNorm:
|
||||
return {vk::Format::eR5G5B5A1UnormPack16, Prospero::ColorMappingAbgr};
|
||||
case Prospero::BufferFormat::k4_4_4_4UNorm:
|
||||
return {vk::Format::eR4G4B4A4UnormPack16, Prospero::ColorMappingAbgr};
|
||||
default: return {VulkanFormat(guest_format), {}};
|
||||
case Prospero::BufferFormat::k4_4_4_4UNorm: return {format, Prospero::ColorMappingAbgr};
|
||||
default: return {format, {}};
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
return volume_texture ? std::max(depth >> level, 1u) : depth;
|
||||
}
|
||||
|
||||
size_t GetTextureRegionCount(uint32_t depth, uint32_t levels, bool volume_texture) {
|
||||
size_t count = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
count += GetTextureLevelDepth(depth, level, volume_texture);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint32_t levels,
|
||||
uint64_t total_size, uint32_t depth) {
|
||||
uint64_t stride = 0;
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
stride = std::max(stride, mips[mip_level].offset + mips[mip_level].size);
|
||||
}
|
||||
|
||||
if (depth > 1 && total_size != 0 && total_size % depth == 0) {
|
||||
stride = std::max(stride, total_size / depth);
|
||||
}
|
||||
|
||||
return stride;
|
||||
}
|
||||
|
||||
uint64_t CalcLinearUploadLevelSize(const TileTextureElementLayout& element, uint32_t pitch,
|
||||
uint32_t height) {
|
||||
const uint32_t element_columns =
|
||||
std::max((pitch + element.texel_width - 1u) / element.texel_width, 1u);
|
||||
const uint32_t element_rows =
|
||||
std::max((height + element.texel_height - 1u) / element.texel_height, 1u);
|
||||
return static_cast<uint64_t>(element_columns) * element_rows * element.bytes;
|
||||
}
|
||||
|
||||
uint64_t SetLinearUploadLevels(TextureUploadMipLayout* mips,
|
||||
const TileTextureElementLayout& element, uint32_t height,
|
||||
uint32_t levels, uint32_t base_pitch) {
|
||||
uint64_t offset = 0;
|
||||
auto mip_pitch = base_pitch;
|
||||
auto mip_height = height;
|
||||
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
const auto size = CalcLinearUploadLevelSize(element, mip_pitch, mip_height);
|
||||
mips[mip_level] = {offset, size};
|
||||
|
||||
offset += size;
|
||||
if (mip_pitch > 1) {
|
||||
mip_pitch /= 2;
|
||||
}
|
||||
if (mip_height > 1) {
|
||||
mip_height /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool FitsBufferRange(uint64_t offset, uint64_t size, uint64_t capacity) {
|
||||
return offset <= capacity && size <= capacity - offset;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
@@ -65,135 +125,61 @@ RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layo
|
||||
Prospero::ChannelOrder order) {
|
||||
const auto encoding = Prospero::ResolveRenderTargetFormat(layout, type);
|
||||
if (encoding.IsValid() && encoding.SupportsOrder(order)) {
|
||||
const auto host = ResolveRenderTargetHostFormat(encoding.buffer_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto host_format = ResolveHostFormat(encoding.buffer_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host_format.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto order_mapping =
|
||||
kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u];
|
||||
return {host.format, bytes, host.host_to_storage.Then(order_mapping)};
|
||||
return {host_format.format, bytes, host_format.host_to_storage.Then(order_mapping)};
|
||||
}
|
||||
}
|
||||
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n",
|
||||
static_cast<uint32_t>(layout), static_cast<uint32_t>(type), static_cast<uint32_t>(order));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
return volume_texture ? std::max(depth >> level, 1u) : depth;
|
||||
}
|
||||
|
||||
static size_t GetTextureRegionCount(uint32_t depth, uint64_t levels, bool volume_texture) {
|
||||
size_t count = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
count += GetTextureLevelDepth(depth, level, volume_texture);
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle,
|
||||
Prospero::ColorComponentMapping host_to_storage) {
|
||||
constexpr std::array host_components {vk::ComponentSwizzle::eR, vk::ComponentSwizzle::eG,
|
||||
vk::ComponentSwizzle::eB, vk::ComponentSwizzle::eA};
|
||||
std::array<vk::ComponentSwizzle, 4> storage_components {};
|
||||
for (uint32_t host_component = 0; host_component < host_components.size(); ++host_component) {
|
||||
storage_components[host_to_storage.Map(host_component)] = host_components[host_component];
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
vk::ComponentSwizzle TextureGetComponentSwizzle(uint8_t s) {
|
||||
switch (static_cast<Prospero::CompSwizzle>(s)) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
case Prospero::CompSwizzle::kOne: return vk::ComponentSwizzle::eOne;
|
||||
case Prospero::CompSwizzle::kRed: return vk::ComponentSwizzle::eR;
|
||||
case Prospero::CompSwizzle::kGreen: return vk::ComponentSwizzle::eG;
|
||||
case Prospero::CompSwizzle::kBlue: return vk::ComponentSwizzle::eB;
|
||||
case Prospero::CompSwizzle::kAlpha: return vk::ComponentSwizzle::eA;
|
||||
default: EXIT("unknown swizzle: %d\n", static_cast<int>(s));
|
||||
}
|
||||
return vk::ComponentSwizzle::eIdentity;
|
||||
}
|
||||
|
||||
static uint32_t TextureGetDstSel(uint32_t swizzle, uint32_t channel) {
|
||||
return (swizzle >> (channel * 3u)) & 0x7u;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle) {
|
||||
vk::ComponentMapping components {};
|
||||
components.r = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 0)));
|
||||
components.g = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 1)));
|
||||
components.b = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 2)));
|
||||
components.a = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 3)));
|
||||
return components;
|
||||
const auto resolve = [&](uint32_t component) {
|
||||
const auto selector = static_cast<Prospero::CompSwizzle>(GetDstSel(swizzle, component));
|
||||
switch (selector) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
case Prospero::CompSwizzle::kOne: return vk::ComponentSwizzle::eOne;
|
||||
case Prospero::CompSwizzle::kRed: return storage_components[0];
|
||||
case Prospero::CompSwizzle::kGreen: return storage_components[1];
|
||||
case Prospero::CompSwizzle::kBlue: return storage_components[2];
|
||||
case Prospero::CompSwizzle::kAlpha: return storage_components[3];
|
||||
default: EXIT("unknown swizzle: %u\n", static_cast<uint32_t>(selector));
|
||||
}
|
||||
};
|
||||
return {resolve(0), resolve(1), resolve(2), resolve(3)};
|
||||
}
|
||||
|
||||
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format) {
|
||||
const auto backing_format = Prospero::RemapTextureFormat(format);
|
||||
const auto vk_format = VulkanFormat(backing_format);
|
||||
const auto backing_format = Prospero::RemapTextureFormat(format);
|
||||
const auto host_format = ResolveHostFormat(backing_format, Prospero::ChannelOrder::kStandard);
|
||||
const auto conversion_format =
|
||||
backing_format != format ? format : Prospero::BufferFormat::kInvalid;
|
||||
if (vk_format != vk::Format::eUndefined) {
|
||||
return SurfaceFormatInfo(vk_format, conversion_format);
|
||||
if (host_format.format != vk::Format::eUndefined) {
|
||||
return {host_format.format, conversion_format, host_format.host_to_storage};
|
||||
}
|
||||
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
|
||||
return SurfaceFormatInfo(vk::Format::eUndefined, Prospero::BufferFormat::kInvalid);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint64_t levels,
|
||||
uint64_t total_size, uint32_t depth) {
|
||||
uint64_t stride = 0;
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
stride = std::max(stride, mips[i].offset + mips[i].size);
|
||||
}
|
||||
|
||||
if (depth > 1 && total_size != 0 && total_size % depth == 0) {
|
||||
const auto guest_stride = total_size / depth;
|
||||
if (guest_stride >= stride) {
|
||||
stride = guest_stride;
|
||||
}
|
||||
}
|
||||
|
||||
return stride;
|
||||
}
|
||||
|
||||
uint64_t CalcLinearUploadLevelSize(const TileTextureElementLayout& element, uint32_t pitch,
|
||||
uint32_t height) {
|
||||
const uint32_t elements_w =
|
||||
std::max((pitch + element.texel_width - 1u) / element.texel_width, 1u);
|
||||
const uint32_t elements_h =
|
||||
std::max((height + element.texel_height - 1u) / element.texel_height, 1u);
|
||||
return static_cast<uint64_t>(elements_w) * elements_h * element.bytes;
|
||||
}
|
||||
|
||||
uint64_t SetLinearUploadLevels(TextureUploadMipLayout* mips,
|
||||
const TileTextureElementLayout& element, uint64_t height,
|
||||
uint64_t levels, uint32_t base_pitch) {
|
||||
uint64_t offset = 0;
|
||||
auto pitch = base_pitch;
|
||||
auto h = static_cast<uint32_t>(height);
|
||||
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
const auto size = CalcLinearUploadLevelSize(element, pitch, h);
|
||||
mips[i].size = size;
|
||||
mips[i].offset = offset;
|
||||
|
||||
offset += size;
|
||||
if (pitch > 1) {
|
||||
pitch /= 2;
|
||||
}
|
||||
if (h > 1) {
|
||||
h /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile, uint64_t upload_size,
|
||||
Prospero::TileMode tile_mode, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner) {
|
||||
TextureUploadLayout layout {};
|
||||
layout.surface.description = {
|
||||
format,
|
||||
tile,
|
||||
tile_mode,
|
||||
volume_texture ? TileSurfaceDimension::Dim3D : TileSurfaceDimension::Dim2D,
|
||||
width,
|
||||
height,
|
||||
@@ -202,7 +188,6 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
volume_texture ? 1u : depth,
|
||||
};
|
||||
const auto& description = layout.surface.description;
|
||||
const auto tile_mode = description.tile_mode;
|
||||
TileTextureElementLayout element {};
|
||||
|
||||
if (format == Prospero::BufferFormat::kInvalid) {
|
||||
@@ -212,34 +197,19 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
levels);
|
||||
}
|
||||
|
||||
switch (tile_mode) {
|
||||
case Prospero::TileMode::kLinear:
|
||||
if (!TileGetTextureElementLayout(format, element)) {
|
||||
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kDepth:
|
||||
if (!allow_depth_tile || !TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format),
|
||||
static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
|
||||
levels);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format),
|
||||
static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
|
||||
levels);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tile_mode != Prospero::TileMode::kLinear) {
|
||||
if (tile_mode == Prospero::TileMode::kLinear) {
|
||||
if (!TileGetTextureElementLayout(format, element)) {
|
||||
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
} else {
|
||||
if ((tile_mode == Prospero::TileMode::kDepth && !allow_depth_tile) ||
|
||||
!TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format), static_cast<uint32_t>(tile_mode),
|
||||
upload_size, width, height, levels);
|
||||
}
|
||||
element = {layout.surface.texture.block.bytes_per_element,
|
||||
layout.surface.texture.texel_width, layout.surface.texture.texel_height};
|
||||
}
|
||||
@@ -272,40 +242,35 @@ TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint3
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const bool linear_texture = description.tile_mode == Prospero::TileMode::kLinear;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
uint32_t mip_width = description.width;
|
||||
uint32_t mip_height = description.height;
|
||||
uint32_t mip_pitch = volume_texture && description.tile_mode != Prospero::TileMode::kLinear
|
||||
? description.width
|
||||
: layout.pitch;
|
||||
uint32_t mip_pitch = volume_texture && !linear_texture ? description.width : layout.pitch;
|
||||
|
||||
std::vector<vk::BufferImageCopy> regions;
|
||||
regions.reserve(GetTextureRegionCount(depth, description.levels, volume_texture));
|
||||
for (uint32_t i = 0; i < description.levels; i++) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.mips[i].size == 0);
|
||||
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, i, volume_texture);
|
||||
|
||||
for (uint32_t z = 0; z < mip_depth; z++) {
|
||||
const auto slice_offset = z * layout.slice_stride;
|
||||
vk::BufferImageCopy region {};
|
||||
region.bufferOffset = layout.mips[i].offset + slice_offset;
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, i, volume_texture ? 0u : z,
|
||||
1};
|
||||
region.imageOffset.z = volume_texture ? static_cast<int>(z) : 0;
|
||||
region.imageExtent = {mip_width, mip_height, 1};
|
||||
const bool linear = description.tile_mode == Prospero::TileMode::kLinear;
|
||||
if (linear) {
|
||||
region.bufferRowLength = layout.mips[i].row_length;
|
||||
region.bufferImageHeight = layout.mips[i].image_height;
|
||||
} else {
|
||||
const auto texel_width = layout.surface.texture.texel_width;
|
||||
const auto align = [](uint32_t value, uint32_t block) {
|
||||
return ((value + block - 1u) / block) * block;
|
||||
};
|
||||
const auto pitch = align(mip_pitch, texel_width);
|
||||
region.bufferRowLength = pitch > align(mip_width, texel_width) ? pitch : 0;
|
||||
}
|
||||
for (uint32_t mip_level = 0; mip_level < description.levels; ++mip_level) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.mips[mip_level].size == 0);
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, mip_level, volume_texture);
|
||||
vk::BufferImageCopy region {};
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, mip_level, 0, 1};
|
||||
region.imageExtent = {mip_width, mip_height, 1};
|
||||
if (linear_texture) {
|
||||
region.bufferRowLength = layout.mips[mip_level].row_length;
|
||||
region.bufferImageHeight = layout.mips[mip_level].image_height;
|
||||
} else {
|
||||
const auto texel_width = layout.surface.texture.texel_width;
|
||||
const auto align_to_texel = [texel_width](uint32_t dimension) {
|
||||
return ((dimension + texel_width - 1u) / texel_width) * texel_width;
|
||||
};
|
||||
const auto aligned_pitch = align_to_texel(mip_pitch);
|
||||
region.bufferRowLength = aligned_pitch > align_to_texel(mip_width) ? aligned_pitch : 0;
|
||||
}
|
||||
for (uint32_t slice_index = 0; slice_index < mip_depth; ++slice_index) {
|
||||
region.bufferOffset = layout.mips[mip_level].offset + slice_index * layout.slice_stride;
|
||||
region.imageSubresource.baseArrayLayer = volume_texture ? 0u : slice_index;
|
||||
region.imageOffset.z = volume_texture ? static_cast<int>(slice_index) : 0;
|
||||
regions.push_back(region);
|
||||
}
|
||||
|
||||
@@ -323,17 +288,9 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
|
||||
return regions;
|
||||
}
|
||||
|
||||
static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity, uint64_t& size) {
|
||||
if (offset > capacity || length > capacity - offset) {
|
||||
return false;
|
||||
}
|
||||
size = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& out_infos) {
|
||||
std::vector<GpuTileInfo>& out_tile_infos) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
@@ -351,111 +308,69 @@ bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferI
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<GpuTileInfo> infos;
|
||||
infos.reserve(regions.size());
|
||||
if (volume_texture) {
|
||||
size_t region_base = 0;
|
||||
for (uint32_t level = 0; level < levels; ++level) {
|
||||
const uint32_t mip_depth = GetTextureLevelDepth(depth, level, true);
|
||||
const bool tail = level >= surface.first_tail_level;
|
||||
const uint64_t linear_stride = layout.slice_stride;
|
||||
for (uint32_t z = 0; z < mip_depth; z += block.block_depth) {
|
||||
const uint32_t copy_depth = std::min(block.block_depth, mip_depth - z);
|
||||
const auto& region = regions[region_base + z];
|
||||
const auto pitch =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto logical_height = region.bufferImageHeight != 0
|
||||
? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
GpuTileInfo info {};
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
info.tiled_offset =
|
||||
static_cast<uint64_t>(z / block.block_depth) * surface.block_slice_size +
|
||||
surface.mips[level].offset;
|
||||
const uint64_t linear_span =
|
||||
static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
|
||||
layout.mips[level].size;
|
||||
if (!SetGpuTileSize(info.linear_offset, linear_span, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, surface.mips[level].size, tiled_size,
|
||||
info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
info.linear_slice_stride = linear_stride;
|
||||
info.width = std::max((region.imageExtent.width + texture.texel_width - 1u) /
|
||||
texture.texel_width,
|
||||
1u);
|
||||
info.height = std::max(
|
||||
(logical_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
info.depth = copy_depth;
|
||||
info.surface_z =
|
||||
std::vector<GpuTileInfo> tile_infos;
|
||||
tile_infos.reserve(regions.size());
|
||||
const auto slices_per_copy = volume_texture ? block.block_depth : 1u;
|
||||
size_t region_base = 0;
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
const auto& mip = surface.mips[mip_level];
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, mip_level, volume_texture);
|
||||
for (uint32_t slice_index = 0; slice_index < mip_depth; slice_index += slices_per_copy) {
|
||||
const auto& region = regions[region_base + slice_index];
|
||||
GpuTileInfo tile_info {};
|
||||
tile_info.family = block.family;
|
||||
tile_info.bytes_per_element = block.bytes_per_element;
|
||||
tile_info.linear_offset = region.bufferOffset;
|
||||
tile_info.linear_size = layout.mips[mip_level].size;
|
||||
tile_info.tiled_size = mip.size;
|
||||
if (volume_texture) {
|
||||
tile_info.depth = std::min(slices_per_copy, mip_depth - slice_index);
|
||||
tile_info.linear_slice_stride = layout.slice_stride;
|
||||
tile_info.linear_size += (tile_info.depth - 1u) * layout.slice_stride;
|
||||
tile_info.tiled_offset =
|
||||
mip.offset +
|
||||
static_cast<uint64_t>(slice_index / slices_per_copy) * surface.block_slice_size;
|
||||
tile_info.surface_z =
|
||||
block.block_depth == 1 ? static_cast<uint32_t>(region.imageOffset.z) : 0;
|
||||
info.pitch = std::max((pitch + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
info.tail_x = tail ? surface.mips[level].tail_x : 0;
|
||||
info.tail_y = tail ? surface.mips[level].tail_y : 0;
|
||||
info.tail = tail;
|
||||
info.tiled_width = surface.mips[level].padded_width;
|
||||
info.tiled_height = surface.mips[level].padded_height;
|
||||
infos.push_back(info);
|
||||
}
|
||||
region_base += mip_depth;
|
||||
}
|
||||
} else {
|
||||
size_t region_index = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
const auto& level_size = layout.mips[level];
|
||||
const auto& mip = surface.mips[level];
|
||||
const bool tail = level >= surface.first_tail_level;
|
||||
const auto level_depth = GetTextureLevelDepth(depth, level, false);
|
||||
for (uint32_t z = 0; z < level_depth; z++) {
|
||||
const auto& region = regions[region_index++];
|
||||
const auto pitch =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto logical_height = region.bufferImageHeight != 0
|
||||
? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
GpuTileInfo info {};
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
} else {
|
||||
const auto source_stride = layout.source_slice_stride != 0
|
||||
? layout.source_slice_stride
|
||||
: surface.block_slice_size;
|
||||
if (z > (UINT64_MAX - mip.offset) / source_stride) {
|
||||
if (slice_index > (UINT64_MAX - mip.offset) / source_stride) {
|
||||
return false;
|
||||
}
|
||||
info.tiled_offset = mip.offset + static_cast<uint64_t>(z) * source_stride;
|
||||
if (!SetGpuTileSize(info.linear_offset, level_size.size, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, mip.size, tiled_size, info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
info.width = std::max((region.imageExtent.width + texture.texel_width - 1u) /
|
||||
texture.texel_width,
|
||||
1u);
|
||||
info.height = std::max(
|
||||
(logical_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
info.surface_z = block.family == TileBlockFamily::RenderTarget64KB ||
|
||||
block.family == TileBlockFamily::Depth64KB
|
||||
? region.imageSubresource.baseArrayLayer
|
||||
: 0;
|
||||
info.pitch = std::max((pitch + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
info.tail = tail;
|
||||
info.tail_x = tail ? mip.tail_x : 0;
|
||||
info.tail_y = tail ? mip.tail_y : 0;
|
||||
info.tiled_width = mip.padded_width;
|
||||
info.tiled_height = mip.padded_height;
|
||||
infos.push_back(info);
|
||||
tile_info.tiled_offset =
|
||||
mip.offset + static_cast<uint64_t>(slice_index) * source_stride;
|
||||
tile_info.surface_z = block.family == TileBlockFamily::RenderTarget64KB ||
|
||||
block.family == TileBlockFamily::Depth64KB
|
||||
? region.imageSubresource.baseArrayLayer
|
||||
: 0;
|
||||
}
|
||||
if (!FitsBufferRange(tile_info.linear_offset, tile_info.linear_size, UINT64_MAX) ||
|
||||
!FitsBufferRange(tile_info.tiled_offset, tile_info.tiled_size, tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
const auto row_length =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto image_height = region.bufferImageHeight != 0 ? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
tile_info.width = std::max(
|
||||
(region.imageExtent.width + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
tile_info.height =
|
||||
std::max((image_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
tile_info.pitch =
|
||||
std::max((row_length + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
tile_info.tail = mip_level >= surface.first_tail_level;
|
||||
tile_info.tail_x = tile_info.tail ? mip.tail_x : 0;
|
||||
tile_info.tail_y = tile_info.tail ? mip.tail_y : 0;
|
||||
tile_info.tiled_width = mip.padded_width;
|
||||
tile_info.tiled_height = mip.padded_height;
|
||||
tile_infos.push_back(tile_info);
|
||||
}
|
||||
region_base += mip_depth;
|
||||
}
|
||||
|
||||
if (infos.empty()) {
|
||||
return false;
|
||||
}
|
||||
out_infos = std::move(infos);
|
||||
out_tile_infos = std::move(tile_infos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
@@ -20,12 +18,9 @@ struct RenderTargetFormatInfo {
|
||||
};
|
||||
|
||||
struct SurfaceFormatInfo {
|
||||
explicit constexpr SurfaceFormatInfo(vk::Format format,
|
||||
Prospero::BufferFormat conversion_format)
|
||||
: vk_format(format), conversion_format(conversion_format) {}
|
||||
|
||||
vk::Format vk_format;
|
||||
Prospero::BufferFormat conversion_format;
|
||||
vk::Format vk_format;
|
||||
Prospero::BufferFormat conversion_format;
|
||||
Prospero::ColorComponentMapping host_to_storage;
|
||||
};
|
||||
|
||||
struct TextureUploadMipLayout {
|
||||
@@ -43,20 +38,21 @@ struct TextureUploadLayout {
|
||||
TextureUploadMipLayout mips[16] = {};
|
||||
};
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle);
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle,
|
||||
Prospero::ColorComponentMapping host_to_storage);
|
||||
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format);
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
Prospero::ChannelType type,
|
||||
Prospero::ChannelOrder order);
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile, uint64_t upload_size,
|
||||
Prospero::TileMode tile_mode, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner);
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout);
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& infos);
|
||||
std::vector<GpuTileInfo>& out_tile_infos);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -404,30 +404,22 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]);
|
||||
}
|
||||
|
||||
struct NullImageSpec {
|
||||
vk::Format format;
|
||||
Prospero::BufferFormat guest_format;
|
||||
};
|
||||
|
||||
static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) {
|
||||
switch (resource.numeric_class) {
|
||||
case Prospero::TextureNumericClass::Float:
|
||||
return {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float};
|
||||
case Prospero::TextureNumericClass::Uint:
|
||||
return {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt};
|
||||
case Prospero::TextureNumericClass::Sint:
|
||||
return {vk::Format::eR32Sint, Prospero::BufferFormat::k32SInt};
|
||||
case Prospero::TextureNumericClass::Unsupported: break;
|
||||
}
|
||||
EXIT("null image has unsupported numeric class\n");
|
||||
}
|
||||
|
||||
static TextureCache::ImageDesc NullTextureDesc(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
TextureCache::BindingType binding) {
|
||||
const auto spec = NullTextureSpec(resource);
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.info.pixel_format = spec.format;
|
||||
desc.info.guest_format = spec.guest_format;
|
||||
switch (resource.numeric_class) {
|
||||
case Prospero::TextureNumericClass::Float:
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32Float;
|
||||
break;
|
||||
case Prospero::TextureNumericClass::Uint:
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32UInt;
|
||||
break;
|
||||
case Prospero::TextureNumericClass::Sint:
|
||||
desc.info.guest_format = Prospero::BufferFormat::k32SInt;
|
||||
break;
|
||||
default: EXIT("null image has unsupported numeric class\n");
|
||||
}
|
||||
desc.info.pixel_format = VulkanFormat(desc.info.guest_format);
|
||||
desc.info.type = Prospero::ImageType::kColor2D;
|
||||
desc.info.extent = {1, 1, 1};
|
||||
desc.info.resources = {1, 1};
|
||||
@@ -491,17 +483,18 @@ static void PopulateTextureMipLayout(ImageInfo& info) {
|
||||
|
||||
static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderTextureResource& descriptor, vk::Format format,
|
||||
bool shader_conversion, bool storage, uint32_t view_levels,
|
||||
uint32_t image_layers) {
|
||||
const SurfaceFormatInfo& surface_format, bool storage,
|
||||
uint32_t view_levels, uint32_t image_layers) {
|
||||
ImageViewInfo view {};
|
||||
view.format = format;
|
||||
view.aspect = vk::ImageAspectFlagBits::eColor;
|
||||
view.base_level = descriptor.BaseLevel();
|
||||
view.level_count = view_levels;
|
||||
view.usage = storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlagBits::eSampled;
|
||||
view.mapping = storage || shader_conversion
|
||||
? vk::ComponentMapping {}
|
||||
: TextureGetComponentMapping(descriptor.DstSelXYZW());
|
||||
view.usage = storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlagBits::eSampled;
|
||||
view.mapping =
|
||||
storage || surface_format.conversion_format != Prospero::BufferFormat::kInvalid
|
||||
? vk::ComponentMapping {}
|
||||
: TextureGetComponentMapping(descriptor.DstSelXYZW(), surface_format.host_to_storage);
|
||||
switch (resource.dimension) {
|
||||
case ShaderRecompiler::Decoder::ImageDimension::Dim1D:
|
||||
view.type = vk::ImageViewType::e1D;
|
||||
@@ -680,7 +673,7 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
} else {
|
||||
PopulateTextureMipLayout(desc.info);
|
||||
}
|
||||
desc.view_info = TextureViewInfo(resource, descriptor, view_format, shader_conversion, storage,
|
||||
desc.view_info = TextureViewInfo(resource, descriptor, view_format, surface_format, storage,
|
||||
view_levels, desc.info.resources.layers);
|
||||
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
|
||||
|
||||
@@ -769,8 +762,7 @@ PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runti
|
||||
const auto& program = *runtime.program;
|
||||
const auto& snapshot = runtime.resources;
|
||||
PreparedBindings prepared;
|
||||
prepared.program = runtime.program;
|
||||
prepared.snapshot = &runtime.resources;
|
||||
prepared.runtime = &runtime;
|
||||
prepared.images.reserve(program.info.images.size());
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
auto binding = ResolveTexture(program.info.images[i], snapshot.images[i]);
|
||||
@@ -795,9 +787,9 @@ PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runti
|
||||
|
||||
void RenderExecutor::FindBuffers(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
const auto& snapshot = *prepared.snapshot;
|
||||
EXIT_IF(prepared.runtime == nullptr || !*prepared.runtime);
|
||||
const auto& program = *prepared.runtime->program;
|
||||
const auto& snapshot = prepared.runtime->resources;
|
||||
auto& cache = m_context.GetBufferCache();
|
||||
|
||||
prepared.buffer_sources.clear();
|
||||
@@ -820,9 +812,9 @@ void RenderExecutor::FindBuffers(PreparedBindings& prepared) {
|
||||
|
||||
void RenderExecutor::RebindBuffers(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
const auto& snapshot = *prepared.snapshot;
|
||||
EXIT_IF(prepared.runtime == nullptr || !*prepared.runtime);
|
||||
const auto& program = *prepared.runtime->program;
|
||||
const auto& snapshot = prepared.runtime->resources;
|
||||
const auto& layout = program.bindings;
|
||||
EXIT_IF(prepared.buffer_sources.size() != program.info.buffers.size());
|
||||
|
||||
@@ -855,9 +847,9 @@ void RenderExecutor::RebindBuffers(PreparedBindings& prepared) {
|
||||
|
||||
void RenderExecutor::RebindImages(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
const auto& snapshot = *prepared.snapshot;
|
||||
EXIT_IF(prepared.runtime == nullptr || !*prepared.runtime);
|
||||
const auto& program = *prepared.runtime->program;
|
||||
const auto& snapshot = prepared.runtime->resources;
|
||||
auto& images = prepared.images;
|
||||
EXIT_IF(images.size() != program.info.images.size());
|
||||
auto& texture_cache = m_context.GetTextureCache();
|
||||
@@ -915,8 +907,8 @@ RenderExecutor::PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||
if (bindings.pixel) {
|
||||
FindBuffers(*bindings.pixel);
|
||||
}
|
||||
if (bindings.vertex.program->info.uses_dma ||
|
||||
(bindings.pixel && bindings.pixel->program->info.uses_dma)) {
|
||||
if (bindings.vertex.runtime->program->info.uses_dma ||
|
||||
(bindings.pixel && bindings.pixel->runtime->program->info.uses_dma)) {
|
||||
m_context.GetGpuResources().PrepareBda();
|
||||
}
|
||||
RebindBuffers(bindings.vertex);
|
||||
@@ -947,13 +939,13 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||
? vk::ShaderStageFlagBits::eFragment
|
||||
: vk::ShaderStageFlags {};
|
||||
for (const auto* prepared: prepared_bindings) {
|
||||
EXIT_IF(prepared == nullptr || prepared->program == nullptr ||
|
||||
prepared->snapshot == nullptr);
|
||||
write_count += prepared->program->bindings.descriptors.size();
|
||||
for (const auto& binding: prepared->program->bindings.descriptors) {
|
||||
EXIT_IF(prepared == nullptr || prepared->runtime == nullptr || !*prepared->runtime);
|
||||
const auto& program = *prepared->runtime->program;
|
||||
write_count += program.bindings.descriptors.size();
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
descriptor_count += NativeDescriptorCount(binding);
|
||||
}
|
||||
const auto shader_stage = NativeShaderStage(prepared->program->stage);
|
||||
const auto shader_stage = NativeShaderStage(program.stage);
|
||||
push_stages |= shader_stage;
|
||||
EXIT_IF((pipeline_bind_point == vk::PipelineBindPoint::eGraphics &&
|
||||
(shader_stage & GraphicsStages) == vk::ShaderStageFlags {}) ||
|
||||
@@ -968,7 +960,7 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||
m_descriptor_writes.reserve(write_count);
|
||||
|
||||
for (auto* prepared: prepared_bindings) {
|
||||
const auto& program = *prepared->program;
|
||||
const auto& program = *prepared->runtime->program;
|
||||
auto& descriptors = *prepared;
|
||||
const auto shader_stage = NativeShaderStage(program.stage);
|
||||
const auto shader_stages = ShaderPipelineStages(shader_stage);
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace ShaderRecompiler::IR {
|
||||
struct ResourceSnapshot;
|
||||
}
|
||||
struct ShaderStageRuntime;
|
||||
|
||||
struct TextureBinding {
|
||||
ImageId image_id;
|
||||
@@ -35,8 +33,8 @@ struct PreparedBindings {
|
||||
BufferId id;
|
||||
};
|
||||
|
||||
const ShaderRecompiler::IR::CompiledShaderInfo* program = nullptr;
|
||||
const ShaderRecompiler::IR::ResourceSnapshot* snapshot = nullptr;
|
||||
// The draw owns the immutable compiled-program/runtime-snapshot association through commit.
|
||||
const ShaderStageRuntime* runtime = nullptr;
|
||||
// Keep the resolved guest range through cache preparation; only the host buffer ID may
|
||||
// become stale and need resolving again when bindings are rebound.
|
||||
std::vector<BufferSource> buffer_sources;
|
||||
|
||||
@@ -728,6 +728,7 @@ PipelineCache::Pipeline& PipelineCache::CreateGraphicsPipeline(
|
||||
static_params.cull_back = !rect_list && mc.cull_back;
|
||||
static_params.cull_front = !rect_list && mc.cull_front;
|
||||
static_params.face = mc.face;
|
||||
static_params.provoking_vtx_last = mc.provoking_vtx_last;
|
||||
static_params.polygon_mode =
|
||||
ResolvePolygonMode(mc, static_params.cull_front, static_params.cull_back);
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ struct PipelineStaticParameters {
|
||||
bool cull_front = false;
|
||||
bool cull_back = false;
|
||||
bool face = false;
|
||||
bool provoking_vtx_last = false;
|
||||
vk::PolygonMode polygon_mode = vk::PolygonMode::eFill;
|
||||
uint8_t color_srcblend[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||
uint8_t color_comb_fcn[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
||||
@@ -68,7 +69,7 @@ struct PipelineStaticParameters {
|
||||
static_assert(std::is_trivially_copyable_v<PipelineStaticParameters>);
|
||||
static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
|
||||
static_assert(alignof(PipelineStaticParameters) == 1);
|
||||
static_assert(sizeof(PipelineStaticParameters) == 165);
|
||||
static_assert(sizeof(PipelineStaticParameters) == 166);
|
||||
|
||||
struct PipelineRenderingState {
|
||||
std::array<vk::Format, RENDER_COLOR_ATTACHMENTS_MAX> color_formats {};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/recompiler/BufferFormat.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/rectListShader.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
@@ -122,220 +123,10 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format,
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fmt) {
|
||||
case Prospero::BufferFormat::k32_32_32_32Float:
|
||||
format = vk::Format::eR32G32B32A32Sfloat;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32_32_32_32SInt:
|
||||
format = vk::Format::eR32G32B32A32Sint;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32_32_32_32UInt:
|
||||
format = vk::Format::eR32G32B32A32Uint;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32_32_32Float:
|
||||
format = vk::Format::eR32G32B32Sfloat;
|
||||
size = 3;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32_32_32SInt:
|
||||
format = vk::Format::eR32G32B32Sint;
|
||||
size = 3;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32_32_32UInt:
|
||||
format = vk::Format::eR32G32B32Uint;
|
||||
size = 3;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16_16_16Float:
|
||||
format = vk::Format::eR16G16B16A16Sfloat;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16_16_16SInt:
|
||||
format = vk::Format::eR16G16B16A16Sint;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16_16_16UInt:
|
||||
format = vk::Format::eR16G16B16A16Uint;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16_16_16SScaled:
|
||||
format = vk::Format::eR16G16B16A16Sscaled;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16_16_16UScaled:
|
||||
format = vk::Format::eR16G16B16A16Uscaled;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16_16_16SNorm:
|
||||
format = vk::Format::eR16G16B16A16Snorm;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16_16_16UNorm:
|
||||
format = vk::Format::eR16G16B16A16Unorm;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32_32Float:
|
||||
format = vk::Format::eR32G32Sfloat;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32_32SInt:
|
||||
format = vk::Format::eR32G32Sint;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32_32UInt:
|
||||
format = vk::Format::eR32G32Uint;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8_8_8UInt:
|
||||
format = vk::Format::eR8G8B8A8Uint;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8_8_8SScaled:
|
||||
format = vk::Format::eR8G8B8A8Sscaled;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8_8_8UScaled:
|
||||
format = vk::Format::eR8G8B8A8Uscaled;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8_8_8SNorm:
|
||||
format = vk::Format::eR8G8B8A8Snorm;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8_8_8UNorm:
|
||||
format = vk::Format::eR8G8B8A8Unorm;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k10_10_10_2UNorm:
|
||||
format = vk::Format::eA2B10G10R10UnormPack32;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k10_10_10_2SNorm:
|
||||
format = vk::Format::eA2B10G10R10SnormPack32;
|
||||
size = 4;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16Float:
|
||||
format = vk::Format::eR16G16Sfloat;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16SInt:
|
||||
format = vk::Format::eR16G16Sint;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16UInt:
|
||||
format = vk::Format::eR16G16Uint;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16SScaled:
|
||||
format = vk::Format::eR16G16Sscaled;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16UScaled:
|
||||
format = vk::Format::eR16G16Uscaled;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16SNorm:
|
||||
format = vk::Format::eR16G16Snorm;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16_16UNorm:
|
||||
format = vk::Format::eR16G16Unorm;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32Float:
|
||||
format = vk::Format::eR32Sfloat;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32SInt:
|
||||
format = vk::Format::eR32Sint;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k32UInt:
|
||||
format = vk::Format::eR32Uint;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8SInt:
|
||||
format = vk::Format::eR8G8Sint;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8UInt:
|
||||
format = vk::Format::eR8G8Uint;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8SScaled:
|
||||
format = vk::Format::eR8G8Sscaled;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8UScaled:
|
||||
format = vk::Format::eR8G8Uscaled;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8SNorm:
|
||||
format = vk::Format::eR8G8Snorm;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8_8UNorm:
|
||||
format = vk::Format::eR8G8Unorm;
|
||||
size = 2;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16Float:
|
||||
format = vk::Format::eR16Sfloat;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16SInt:
|
||||
format = vk::Format::eR16Sint;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16UInt:
|
||||
format = vk::Format::eR16Uint;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16SScaled:
|
||||
format = vk::Format::eR16Sscaled;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16UScaled:
|
||||
format = vk::Format::eR16Uscaled;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16SNorm:
|
||||
format = vk::Format::eR16Snorm;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k16UNorm:
|
||||
format = vk::Format::eR16Unorm;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8SInt:
|
||||
format = vk::Format::eR8Sint;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8UInt:
|
||||
format = vk::Format::eR8Uint;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8SScaled:
|
||||
format = vk::Format::eR8Sscaled;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8UScaled:
|
||||
format = vk::Format::eR8Uscaled;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8SNorm:
|
||||
format = vk::Format::eR8Snorm;
|
||||
size = 1;
|
||||
break;
|
||||
case Prospero::BufferFormat::k8UNorm:
|
||||
format = vk::Format::eR8Unorm;
|
||||
size = 1;
|
||||
break;
|
||||
default:
|
||||
EXIT("unknown format: fmt = %u\n", raw_format);
|
||||
format = vk::Format::eUndefined;
|
||||
size = 4;
|
||||
break;
|
||||
format = VulkanFormat(fmt);
|
||||
size = ShaderRecompiler::Format::GetFormatInfo(fmt).component_count;
|
||||
if (format == vk::Format::eUndefined || size == 0) {
|
||||
EXIT("unknown vertex format: fmt = %u\n", raw_format);
|
||||
}
|
||||
|
||||
if (NarrowInputFormat(format, size, used_components)) {
|
||||
@@ -595,6 +386,15 @@ void CreatePipelineInternal(
|
||||
#if !defined(__APPLE__)
|
||||
rasterizer.pNext = &clip_ext;
|
||||
#endif
|
||||
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex {};
|
||||
EXIT_NOT_IMPLEMENTED(static_params.provoking_vtx_last &&
|
||||
!graphics.provoking_vertex_last_enabled);
|
||||
if (graphics.provoking_vertex_last_enabled) {
|
||||
provoking_vertex.provokingVertexMode = static_params.provoking_vtx_last
|
||||
? vk::ProvokingVertexModeEXT::eLastVertex : vk::ProvokingVertexModeEXT::eFirstVertex;
|
||||
provoking_vertex.pNext = rasterizer.pNext;
|
||||
rasterizer.pNext = &provoking_vertex;
|
||||
}
|
||||
rasterizer.cullMode = cull_mode;
|
||||
rasterizer.frontFace = front_face;
|
||||
rasterizer.polygonMode = static_params.polygon_mode;
|
||||
|
||||
@@ -179,13 +179,12 @@ private:
|
||||
[[nodiscard]] GraphicsBindings PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||
const ShaderStageRuntime& pixel,
|
||||
bool pixel_active);
|
||||
void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderColorInfo& target, uint32_t render_target_slice_offset = 0,
|
||||
uint32_t render_target_slot = UINT32_MAX,
|
||||
void ResolveRenderColorTarget(CommandBuffer& buffer, RenderColorInfo& target,
|
||||
uint32_t render_target_slice_offset, uint32_t render_target_slot,
|
||||
bool ignore_target_mask = false, bool exact_format = false);
|
||||
void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer& buffer,
|
||||
RenderDepthInfo& target);
|
||||
[[nodiscard]] bool PrepareDrawRenderState(uint64_t submit_id, CommandBuffer& buffer,
|
||||
void ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& target);
|
||||
[[nodiscard]] bool DepthStencilCopy(CommandBuffer& buffer);
|
||||
[[nodiscard]] bool PrepareDrawRenderState(CommandBuffer& buffer,
|
||||
const DrawCallInfo& draw,
|
||||
uint32_t render_target_slice_offset,
|
||||
bool log_setup_phases, DrawRenderState& state);
|
||||
@@ -197,7 +196,7 @@ private:
|
||||
[[nodiscard]] RenderState AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
|
||||
uint32_t color_count, RenderDepthInfo& depth,
|
||||
const std::optional<PreparedBindings>& pixel = std::nullopt);
|
||||
[[nodiscard]] bool ResolveColorTargets(uint64_t submit_id, CommandBuffer& buffer,
|
||||
[[nodiscard]] bool ResolveColorTargets(CommandBuffer& buffer,
|
||||
uint32_t render_target_slice_offset);
|
||||
void BindImage(ImageId id, bool storage);
|
||||
void BindRenderTarget(ImageId id);
|
||||
|
||||
@@ -544,11 +544,11 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
!cache.ClearMeta(metadata.range.address)) {
|
||||
EXIT("failed to acquire HTile metadata for a depth clear\n");
|
||||
}
|
||||
depth.depth_meta_clear_enable =
|
||||
const bool meta_clear =
|
||||
metadata.kind == ImageMetadataKind::Htile &&
|
||||
cache.IsMetaCleared(metadata.range.address, depth.desc.view_info.base_layer);
|
||||
depth.depth_load_clear_enable = depth.depth_clear_enable || depth.depth_meta_clear_enable;
|
||||
if (depth.depth_meta_clear_enable &&
|
||||
depth.depth_load_clear_enable = depth.depth_clear_enable || meta_clear;
|
||||
if (meta_clear &&
|
||||
!cache.TouchMeta(metadata.range.address, depth.desc.view_info.base_layer, false)) {
|
||||
EXIT("failed to consume HTile clear state\n");
|
||||
}
|
||||
@@ -863,8 +863,8 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, vk::PrimitiveTopology topology,
|
||||
uint32_t index_type_and_size) {
|
||||
static bool ResolvePrimitiveRestart(const CommandBuffer& buffer,
|
||||
const DrawIndexBufferSource& source) {
|
||||
const auto control = buffer.GetUserConfig().GetPrimitiveResetControl();
|
||||
EXIT_NOT_IMPLEMENTED((control & ~0x3u) != 0);
|
||||
if ((control & 0x1u) == 0) {
|
||||
@@ -876,35 +876,38 @@ static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, vk::PrimitiveTo
|
||||
case Prospero::PrimitiveType::kTriStrip: break;
|
||||
default: return false;
|
||||
}
|
||||
if (topology != vk::PrimitiveTopology::eLineStrip &&
|
||||
topology != vk::PrimitiveTopology::eTriangleStrip &&
|
||||
topology != vk::PrimitiveTopology::eTriangleFan) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t index_mask = 0;
|
||||
switch (static_cast<Prospero::IndexType>(index_type_and_size)) {
|
||||
case Prospero::IndexType::kIndex8: index_mask = 0xffu; break;
|
||||
case Prospero::IndexType::kIndex16: index_mask = 0xffffu; break;
|
||||
case Prospero::IndexType::kIndex32: index_mask = 0xffffffffu; break;
|
||||
default: EXIT("unknown index_type_and_size: %u\n", index_type_and_size);
|
||||
}
|
||||
|
||||
const auto reset_index = buffer.GetRegisters().GetPrimitiveResetIndex();
|
||||
const auto element_size = source.guest_element_size;
|
||||
const auto index_mask = UINT32_MAX >> ((4 - element_size) * 8);
|
||||
const auto reset_index = buffer.GetRegisters().GetPrimitiveResetIndex();
|
||||
if ((control & 0x2u) != 0 && (reset_index & ~index_mask) != 0) {
|
||||
return false;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED((reset_index & index_mask) != index_mask);
|
||||
return true;
|
||||
const auto restart_index = reset_index & index_mask;
|
||||
if (restart_index == index_mask) {
|
||||
// Use native restart; the 8-bit path widens its marker to 0xffff.
|
||||
return true;
|
||||
}
|
||||
|
||||
// A game can set a custom reset value without using it in the index buffer.
|
||||
// Keep restart off in that case; fail if we actually find the value.
|
||||
// Scan before preparing draw resources: readback can restart the command buffer.
|
||||
EXIT_NOT_IMPLEMENTED(source.address == 0);
|
||||
const auto* indices = reinterpret_cast<const uint8_t*>(source.address);
|
||||
for (uint64_t offset = 0; offset < source.size; offset += element_size) {
|
||||
uint32_t index = 0;
|
||||
std::memcpy(&index, indices + offset, element_size);
|
||||
EXIT_NOT_IMPLEMENTED(index == restart_index);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, CommandBuffer& buffer,
|
||||
const DrawCallInfo& draw,
|
||||
bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCallInfo& draw,
|
||||
uint32_t render_target_slice_offset,
|
||||
bool log_setup_phases, DrawRenderState& state) {
|
||||
auto& ctx = buffer.GetRegisters();
|
||||
|
||||
if (ResolveColorTargets(submit_id, buffer, render_target_slice_offset)) {
|
||||
if (ResolveColorTargets(buffer, render_target_slice_offset)) {
|
||||
return false;
|
||||
}
|
||||
if (log_setup_phases) {
|
||||
@@ -913,7 +916,7 @@ bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, CommandBuffer& b
|
||||
for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) {
|
||||
if (slot == 0 || (render_target_mask_slot(ctx.GetRenderTargetMask(), slot) != 0 &&
|
||||
ctx.GetRenderTarget(slot).base.addr != 0)) {
|
||||
ResolveRenderColorTarget(submit_id, buffer, state.color_info[state.color_count],
|
||||
ResolveRenderColorTarget(buffer, state.color_info[state.color_count],
|
||||
render_target_slice_offset, slot);
|
||||
if (state.color_info[state.color_count].image_id) {
|
||||
state.color_count++;
|
||||
@@ -923,7 +926,7 @@ bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, CommandBuffer& b
|
||||
if (log_setup_phases) {
|
||||
LogDrawPhase(draw.name, "ResolveRenderDepthTarget");
|
||||
}
|
||||
ResolveRenderDepthTarget(submit_id, buffer, state.depth_info);
|
||||
ResolveRenderDepthTarget(buffer, state.depth_info);
|
||||
|
||||
state.ps_active = DrawHasActivePixelShader(buffer);
|
||||
if (state.color_count == 0 && !state.depth_info.image_id && !state.ps_active) {
|
||||
@@ -1235,7 +1238,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConsumeMetadataColorOperation(buffer)) {
|
||||
if (ConsumeMetadataColorOperation(buffer) || DepthStencilCopy(buffer)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
}
|
||||
@@ -1270,53 +1273,41 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
vk::IndexType index_type = vk::IndexType::eUint16;
|
||||
uint64_t index_size = 0;
|
||||
bool expand_index8_to_u16 = false;
|
||||
const bool primitive_restart =
|
||||
ResolvePrimitiveRestart(buffer, topology, args.index_type_and_size);
|
||||
|
||||
DrawIndexBufferSource index_source {};
|
||||
index_source.address = reinterpret_cast<uint64_t>(args.index_addr);
|
||||
switch (static_cast<Prospero::IndexType>(args.index_type_and_size)) {
|
||||
case Prospero::IndexType::kIndex16:
|
||||
index_type = vk::IndexType::eUint16;
|
||||
index_size = 2 * static_cast<uint64_t>(args.index_count);
|
||||
index_source.type = vk::IndexType::eUint16;
|
||||
index_source.guest_element_size = 2;
|
||||
break;
|
||||
case Prospero::IndexType::kIndex32:
|
||||
index_type = vk::IndexType::eUint32;
|
||||
index_size = 4 * static_cast<uint64_t>(args.index_count);
|
||||
index_source.type = vk::IndexType::eUint32;
|
||||
index_source.guest_element_size = 4;
|
||||
break;
|
||||
// Some games use it - need vulkan extension
|
||||
case Prospero::IndexType::kIndex8:
|
||||
index_type = vk::IndexType::eUint16;
|
||||
index_size = static_cast<uint64_t>(args.index_count);
|
||||
expand_index8_to_u16 = true;
|
||||
index_source.guest_element_size = 1;
|
||||
break;
|
||||
default: EXIT("unknown index_type_and_size: %u\n", args.index_type_and_size);
|
||||
}
|
||||
index_source.size = static_cast<uint64_t>(args.index_count) * index_source.guest_element_size;
|
||||
const bool primitive_restart = ResolvePrimitiveRestart(buffer, index_source);
|
||||
|
||||
const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex, args.index_count,
|
||||
args.instance_count, args.first_instance};
|
||||
std::vector<uint16_t> expanded_indices;
|
||||
if (expand_index8_to_u16) {
|
||||
if (index_source.guest_element_size == 1) {
|
||||
EXIT_NOT_IMPLEMENTED(args.index_addr == nullptr);
|
||||
const auto* src = static_cast<const uint8_t*>(args.index_addr);
|
||||
expanded_indices.resize(args.index_count);
|
||||
for (uint32_t i = 0; i < args.index_count; i++) {
|
||||
expanded_indices[i] = primitive_restart && src[i] == 0xffu ? 0xffffu : src[i];
|
||||
}
|
||||
index_source.host_data = expanded_indices.data();
|
||||
index_source.size = expanded_indices.size() * sizeof(uint16_t);
|
||||
}
|
||||
|
||||
DrawIndexBufferSource index_source {};
|
||||
index_source.address = reinterpret_cast<uint64_t>(args.index_addr);
|
||||
index_source.host_data =
|
||||
expanded_indices.empty() ? nullptr : static_cast<const void*>(expanded_indices.data());
|
||||
index_source.size =
|
||||
expanded_indices.empty() ? index_size : expanded_indices.size() * sizeof(uint16_t);
|
||||
index_source.type = index_type;
|
||||
index_source.guest_element_size = static_cast<uint32_t>(index_size / args.index_count);
|
||||
|
||||
const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex, args.index_count,
|
||||
args.instance_count, args.first_instance};
|
||||
DrawRenderState state {};
|
||||
if (!PrepareDrawRenderState(submit_id, buffer, draw, args.render_target_slice_offset, true,
|
||||
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, true,
|
||||
state)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
@@ -1363,7 +1354,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConsumeMetadataColorOperation(buffer)) {
|
||||
if (ConsumeMetadataColorOperation(buffer) || DepthStencilCopy(buffer)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
}
|
||||
@@ -1393,7 +1384,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
|
||||
args.vertex_count, args.instance_count, args.first_instance};
|
||||
|
||||
DrawRenderState state {};
|
||||
if (!PrepareDrawRenderState(submit_id, buffer, draw, args.render_target_slice_offset, false,
|
||||
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, false,
|
||||
state)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
@@ -1440,8 +1431,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D
|
||||
ResetBindings();
|
||||
}
|
||||
|
||||
bool RenderExecutor::ResolveColorTargets(uint64_t submit_id, CommandBuffer& buffer,
|
||||
uint32_t render_target_slice_offset) {
|
||||
bool RenderExecutor::ResolveColorTargets(CommandBuffer& buffer, uint32_t render_target_slice_offset) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
if (hw.GetColorControl().mode != 3) {
|
||||
return false;
|
||||
@@ -1455,8 +1445,8 @@ bool RenderExecutor::ResolveColorTargets(uint64_t submit_id, CommandBuffer& buff
|
||||
|
||||
RenderColorInfo src {};
|
||||
RenderColorInfo dst {};
|
||||
ResolveRenderColorTarget(submit_id, buffer, src, render_target_slice_offset, 0, true, true);
|
||||
ResolveRenderColorTarget(submit_id, buffer, dst, render_target_slice_offset, 1, true, true);
|
||||
ResolveRenderColorTarget(buffer, src, render_target_slice_offset, 0, true, true);
|
||||
ResolveRenderColorTarget(buffer, dst, render_target_slice_offset, 1, true, true);
|
||||
if (!src.image_id || !dst.image_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -130,35 +130,42 @@ uint64_t GraphicContext::GetTotalMemoryBudget() const {
|
||||
|
||||
bool GraphicContext::CreateImage(const vk::ImageCreateInfo& image_info, VulkanImage& image) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(allocator == nullptr || image.image != nullptr || image.memory.allocation != nullptr);
|
||||
EXIT_IF(allocator == nullptr || image.image != nullptr || image.allocation != nullptr);
|
||||
|
||||
auto& memory = image.memory;
|
||||
VmaAllocationCreateInfo alloc_info {};
|
||||
alloc_info.requiredFlags = static_cast<vk::MemoryPropertyFlags::MaskType>(memory.property);
|
||||
alloc_info.preferredFlags =
|
||||
static_cast<vk::MemoryPropertyFlags::MaskType>(memory.preferred_property);
|
||||
alloc_info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
vk::Image::CType native_image = VK_NULL_HANDLE;
|
||||
const auto result = static_cast<vk::Result>(
|
||||
vmaCreateImage(allocator, static_cast<const vk::ImageCreateInfo::NativeType*>(image_info),
|
||||
&alloc_info, &native_image, &memory.allocation, nullptr));
|
||||
&alloc_info, &native_image, &image.allocation, nullptr));
|
||||
image.image = native_image;
|
||||
if (result != vk::Result::eSuccess) {
|
||||
LogMemoryBudget();
|
||||
return false;
|
||||
}
|
||||
|
||||
image.format = image_info.format;
|
||||
image.image_type = image_info.imageType;
|
||||
image.extent = image_info.extent;
|
||||
image.layers = image_info.arrayLayers;
|
||||
image.mip_levels = image_info.mipLevels;
|
||||
image.samples = static_cast<uint32_t>(image_info.samples);
|
||||
image.usage = image_info.usage;
|
||||
image.flags = image_info.flags;
|
||||
image.state = {.layout = image_info.initialLayout};
|
||||
image.subresource_states.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicContext::DeleteImage(VulkanImage& image) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(allocator == nullptr || image.image == nullptr || image.memory.allocation == nullptr);
|
||||
EXIT_IF(allocator == nullptr || image.image == nullptr || image.allocation == nullptr);
|
||||
|
||||
auto& memory = image.memory;
|
||||
vmaDestroyImage(allocator, image.image, memory.allocation);
|
||||
image.image = nullptr;
|
||||
memory.allocation = nullptr;
|
||||
vmaDestroyImage(allocator, image.image, image.allocation);
|
||||
image.image = nullptr;
|
||||
image.allocation = nullptr;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -17,14 +17,22 @@ struct FormatMapping {
|
||||
|
||||
constexpr FormatMapping kFormatMappings[] = {
|
||||
{Prospero::BufferFormat::k8UNorm, vk::Format::eR8Unorm},
|
||||
{Prospero::BufferFormat::k8SNorm, vk::Format::eR8Snorm},
|
||||
{Prospero::BufferFormat::k8UScaled, vk::Format::eR8Uscaled},
|
||||
{Prospero::BufferFormat::k8SScaled, vk::Format::eR8Sscaled},
|
||||
{Prospero::BufferFormat::k8UInt, vk::Format::eR8Uint},
|
||||
{Prospero::BufferFormat::k8SInt, vk::Format::eR8Sint},
|
||||
{Prospero::BufferFormat::k16UNorm, vk::Format::eR16Unorm},
|
||||
{Prospero::BufferFormat::k16SNorm, vk::Format::eR16Snorm},
|
||||
{Prospero::BufferFormat::k16UScaled, vk::Format::eR16Uscaled},
|
||||
{Prospero::BufferFormat::k16SScaled, vk::Format::eR16Sscaled},
|
||||
{Prospero::BufferFormat::k16UInt, vk::Format::eR16Uint},
|
||||
{Prospero::BufferFormat::k16SInt, vk::Format::eR16Sint},
|
||||
{Prospero::BufferFormat::k16Float, vk::Format::eR16Sfloat},
|
||||
{Prospero::BufferFormat::k8_8UNorm, vk::Format::eR8G8Unorm},
|
||||
{Prospero::BufferFormat::k8_8SNorm, vk::Format::eR8G8Snorm},
|
||||
{Prospero::BufferFormat::k8_8UScaled, vk::Format::eR8G8Uscaled},
|
||||
{Prospero::BufferFormat::k8_8SScaled, vk::Format::eR8G8Sscaled},
|
||||
{Prospero::BufferFormat::k8_8UInt, vk::Format::eR8G8Uint},
|
||||
{Prospero::BufferFormat::k8_8SInt, vk::Format::eR8G8Sint},
|
||||
{Prospero::BufferFormat::k32UInt, vk::Format::eR32Uint},
|
||||
@@ -32,14 +40,19 @@ constexpr FormatMapping kFormatMappings[] = {
|
||||
{Prospero::BufferFormat::k32Float, vk::Format::eR32Sfloat},
|
||||
{Prospero::BufferFormat::k16_16UNorm, vk::Format::eR16G16Unorm},
|
||||
{Prospero::BufferFormat::k16_16SNorm, vk::Format::eR16G16Snorm},
|
||||
{Prospero::BufferFormat::k16_16UScaled, vk::Format::eR16G16Uscaled},
|
||||
{Prospero::BufferFormat::k16_16SScaled, vk::Format::eR16G16Sscaled},
|
||||
{Prospero::BufferFormat::k16_16UInt, vk::Format::eR16G16Uint},
|
||||
{Prospero::BufferFormat::k16_16SInt, vk::Format::eR16G16Sint},
|
||||
{Prospero::BufferFormat::k16_16Float, vk::Format::eR16G16Sfloat},
|
||||
{Prospero::BufferFormat::k11_11_10Float, vk::Format::eB10G11R11UfloatPack32},
|
||||
{Prospero::BufferFormat::k10_10_10_2UNorm, vk::Format::eA2B10G10R10UnormPack32},
|
||||
{Prospero::BufferFormat::k10_10_10_2SNorm, vk::Format::eA2B10G10R10SnormPack32},
|
||||
{Prospero::BufferFormat::k10_10_10_2UInt, vk::Format::eA2B10G10R10UintPack32},
|
||||
{Prospero::BufferFormat::k8_8_8_8UNorm, vk::Format::eR8G8B8A8Unorm},
|
||||
{Prospero::BufferFormat::k8_8_8_8SNorm, vk::Format::eR8G8B8A8Snorm},
|
||||
{Prospero::BufferFormat::k8_8_8_8UScaled, vk::Format::eR8G8B8A8Uscaled},
|
||||
{Prospero::BufferFormat::k8_8_8_8SScaled, vk::Format::eR8G8B8A8Sscaled},
|
||||
{Prospero::BufferFormat::k8_8_8_8UInt, vk::Format::eR8G8B8A8Uint},
|
||||
{Prospero::BufferFormat::k8_8_8_8SInt, vk::Format::eR8G8B8A8Sint},
|
||||
{Prospero::BufferFormat::k32_32UInt, vk::Format::eR32G32Uint},
|
||||
@@ -47,6 +60,8 @@ constexpr FormatMapping kFormatMappings[] = {
|
||||
{Prospero::BufferFormat::k32_32Float, vk::Format::eR32G32Sfloat},
|
||||
{Prospero::BufferFormat::k16_16_16_16UNorm, vk::Format::eR16G16B16A16Unorm},
|
||||
{Prospero::BufferFormat::k16_16_16_16SNorm, vk::Format::eR16G16B16A16Snorm},
|
||||
{Prospero::BufferFormat::k16_16_16_16UScaled, vk::Format::eR16G16B16A16Uscaled},
|
||||
{Prospero::BufferFormat::k16_16_16_16SScaled, vk::Format::eR16G16B16A16Sscaled},
|
||||
{Prospero::BufferFormat::k16_16_16_16UInt, vk::Format::eR16G16B16A16Uint},
|
||||
{Prospero::BufferFormat::k16_16_16_16SInt, vk::Format::eR16G16B16A16Sint},
|
||||
{Prospero::BufferFormat::k16_16_16_16Float, vk::Format::eR16G16B16A16Sfloat},
|
||||
@@ -63,7 +78,8 @@ constexpr FormatMapping kFormatMappings[] = {
|
||||
{Prospero::BufferFormat::k8_8_8_8Srgb, vk::Format::eR8G8B8A8Srgb},
|
||||
{Prospero::BufferFormat::k9_9_9_5Float, vk::Format::eE5B9G9R9UfloatPack32},
|
||||
{Prospero::BufferFormat::k5_6_5UNorm, vk::Format::eB5G6R5UnormPack16},
|
||||
{Prospero::BufferFormat::k5_5_5_1UNorm, vk::Format::eR5G5B5A1UnormPack16},
|
||||
{Prospero::BufferFormat::k5_5_5_1UNorm, vk::Format::eA1R5G5B5UnormPack16},
|
||||
{Prospero::BufferFormat::k1_5_5_5UNorm, vk::Format::eR5G5B5A1UnormPack16},
|
||||
{Prospero::BufferFormat::k4_4_4_4UNorm, vk::Format::eR4G4B4A4UnormPack16},
|
||||
{Prospero::BufferFormat::kBc1UNorm, vk::Format::eBc1RgbaUnormBlock},
|
||||
{Prospero::BufferFormat::kBc1Srgb, vk::Format::eBc1RgbaSrgbBlock},
|
||||
|
||||
@@ -191,24 +191,15 @@ void Presenter::Frame::Configure(GraphicContext& graphics, vk::Extent2D extent,
|
||||
}
|
||||
if (dst.image != nullptr) {
|
||||
graphics.DeleteImage(dst);
|
||||
dst.memory = {};
|
||||
}
|
||||
|
||||
dst.extent = {extent.width, extent.height, 1};
|
||||
dst.format = format;
|
||||
dst.layers = 1;
|
||||
dst.mip_levels = 1;
|
||||
dst.state = {};
|
||||
dst.subresource_states.clear();
|
||||
dst.memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
||||
|
||||
vk::ImageCreateInfo create {};
|
||||
create.sType = vk::StructureType::eImageCreateInfo;
|
||||
create.imageType = vk::ImageType::e2D;
|
||||
create.extent = {dst.extent.width, dst.extent.height, 1};
|
||||
create.extent = {extent.width, extent.height, 1};
|
||||
create.mipLevels = 1;
|
||||
create.arrayLayers = 1;
|
||||
create.format = dst.format;
|
||||
create.format = format;
|
||||
create.tiling = vk::ImageTiling::eOptimal;
|
||||
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||
create.usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
||||
@@ -216,7 +207,7 @@ void Presenter::Frame::Configure(GraphicContext& graphics, vk::Extent2D extent,
|
||||
create.samples = vk::SampleCountFlagBits::e1;
|
||||
if (!graphics.CreateImage(create, dst)) {
|
||||
EXIT("failed to allocate prepared presentation image, extent=%ux%u format=%d\n",
|
||||
dst.extent.width, dst.extent.height, static_cast<int>(dst.format));
|
||||
extent.width, extent.height, static_cast<int>(format));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -637,7 +637,15 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
feedback_layout.pNext = &feedback_dynamic;
|
||||
supported_features2.pNext = &feedback_layout;
|
||||
}
|
||||
const bool provoking_extension =
|
||||
HasExtension(device_extensions, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
|
||||
vk::PhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex {};
|
||||
if (provoking_extension) {
|
||||
provoking_vertex.pNext = supported_features2.pNext;
|
||||
supported_features2.pNext = &provoking_vertex;
|
||||
}
|
||||
physical_device.getFeatures2(&supported_features2);
|
||||
graphics.provoking_vertex_last_enabled = provoking_extension && provoking_vertex.provokingVertexLast;
|
||||
graphics.attachment_feedback_loop_enabled =
|
||||
feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout &&
|
||||
feedback_dynamic.attachmentFeedbackLoopDynamicState;
|
||||
@@ -753,6 +761,11 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
create_info.pNext = graphics.attachment_feedback_loop_enabled
|
||||
? static_cast<void*>(&feedback_layout)
|
||||
: feedback_dynamic.pNext;
|
||||
if (graphics.provoking_vertex_last_enabled) {
|
||||
provoking_vertex.pNext = const_cast<void*>(create_info.pNext);
|
||||
provoking_vertex.transformFeedbackPreservesProvokingVertex = VK_FALSE;
|
||||
create_info.pNext = &provoking_vertex;
|
||||
}
|
||||
create_info.flags = {};
|
||||
create_info.pQueueCreateInfos = &queue_create_info;
|
||||
create_info.queueCreateInfoCount = 1;
|
||||
@@ -1145,6 +1158,7 @@ void WindowContext::CreateVulkan() {
|
||||
graphic_ctx.memory_budget_ext_enabled = true;
|
||||
}
|
||||
for (const auto* extension: {VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
|
||||
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME,
|
||||
VK_EXT_MESH_SHADER_EXTENSION_NAME,
|
||||
VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME}) {
|
||||
if (HasExtension(available_extensions, extension)) {
|
||||
|
||||
@@ -1016,7 +1016,7 @@ void WindowContext::UpdateTitle() {
|
||||
|
||||
const auto* device_name = graphic_ctx.GetPhysicalDeviceProperties().deviceName.data();
|
||||
auto text = fmt::format(
|
||||
"[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", KYTY_BUILD_LABEL, build_type,
|
||||
"[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:.0f}", KYTY_BUILD_LABEL, build_type,
|
||||
(has_title ? title : ""), (has_title ? ", " : ""), (has_title_id ? title_id : ""),
|
||||
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""),
|
||||
device_name, processor_name, frame_num, current_fps);
|
||||
|
||||
@@ -238,28 +238,13 @@ constexpr BufferFormatInfo GetFormatInfo(Prospero::BufferFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsKnownFormat(Prospero::BufferFormat format) {
|
||||
return GetFormatInfo(format).type != ComponentType::Unknown;
|
||||
}
|
||||
|
||||
constexpr uint32_t GetFormatComponentCount(Prospero::BufferFormat format) {
|
||||
return GetFormatInfo(format).component_count;
|
||||
}
|
||||
|
||||
constexpr uint32_t GetFormatComponentByteOffset(Prospero::BufferFormat format, uint32_t component) {
|
||||
const auto info = GetFormatInfo(format);
|
||||
constexpr uint32_t GetFormatComponentByteOffset(const BufferFormatInfo& info, uint32_t component) {
|
||||
if (component >= info.component_count) {
|
||||
return 0;
|
||||
}
|
||||
return info.packed_bitfield ? 0u : info.component_bit_offset[component] / 8u;
|
||||
}
|
||||
|
||||
constexpr bool CanUseTypedBufferLoad(Prospero::BufferFormat format) {
|
||||
const auto info = GetFormatInfo(format);
|
||||
return info.type != ComponentType::Unknown && !info.packed_bitfield &&
|
||||
info.component_bits[0] == 32u;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Format
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_BUFFERFORMAT_H_ */
|
||||
@@ -77,8 +77,7 @@ enum class EmbeddedFetchValueType {
|
||||
AttribTable,
|
||||
Attrib,
|
||||
BufferTable,
|
||||
Buffer,
|
||||
Index
|
||||
Buffer
|
||||
};
|
||||
|
||||
struct EmbeddedFetchSgprInfo {
|
||||
@@ -88,10 +87,6 @@ struct EmbeddedFetchSgprInfo {
|
||||
std::vector<uint32_t> prolog_loads;
|
||||
};
|
||||
|
||||
struct EmbeddedFetchVgprInfo {
|
||||
EmbeddedFetchValueType type = EmbeddedFetchValueType::Unknown;
|
||||
};
|
||||
|
||||
using EmbeddedFetchVectorLanes = std::map<uint64_t, EmbeddedFetchSgprInfo>;
|
||||
|
||||
uint64_t EmbeddedFetchVectorLaneKey(uint32_t reg, uint32_t lane) {
|
||||
@@ -108,7 +103,6 @@ void ClearEmbeddedFetchVectorLanes(EmbeddedFetchVectorLanes* lanes, uint32_t reg
|
||||
lanes->erase(first, last);
|
||||
}
|
||||
|
||||
using EmbeddedFetchLoad = Frontend::EmbeddedFetchLoad;
|
||||
using EmbeddedFetchData = Frontend::EmbeddedFetchPlan;
|
||||
|
||||
bool IsDecodedSgpr(const Decoder::Operand& op) {
|
||||
@@ -136,20 +130,6 @@ uint32_t EmbeddedFetchDstSize(const Decoder::Instruction& inst) {
|
||||
return inst.opcode == Decoder::Opcode::V_MAD_U64_U32 ? 2u : DecodedDstSize(inst);
|
||||
}
|
||||
|
||||
bool EmbeddedFetchHasBranch(Decoder::Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Decoder::Opcode::S_SETPC_B64:
|
||||
case Decoder::Opcode::S_BRANCH:
|
||||
case Decoder::Opcode::S_CBRANCH_SCC0:
|
||||
case Decoder::Opcode::S_CBRANCH_SCC1:
|
||||
case Decoder::Opcode::S_CBRANCH_VCCZ:
|
||||
case Decoder::Opcode::S_CBRANCH_VCCNZ:
|
||||
case Decoder::Opcode::S_CBRANCH_EXECZ:
|
||||
case Decoder::Opcode::S_CBRANCH_EXECNZ: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ClearEmbeddedFetchSgprs(std::array<EmbeddedFetchSgprInfo, 108>& sgprs,
|
||||
const Decoder::Operand& dst, uint32_t size) {
|
||||
if (!IsDecodedSgpr(dst)) {
|
||||
@@ -244,11 +224,14 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
const int buffer_reg = input_info->fetch_buffer_reg + shift_regs;
|
||||
|
||||
std::array<EmbeddedFetchSgprInfo, 108> sgprs {};
|
||||
std::array<EmbeddedFetchVgprInfo, 256> vgprs {};
|
||||
std::array<bool, 256> vgpr_is_index {};
|
||||
EmbeddedFetchVectorLanes vector_lanes;
|
||||
const bool track_vector_lanes =
|
||||
std::none_of(decoded.instructions.begin(), decoded.instructions.end(),
|
||||
[](const auto& inst) { return EmbeddedFetchHasBranch(inst.opcode); });
|
||||
[](const auto& inst) {
|
||||
return Decoder::IsDirectBranch(inst.opcode) ||
|
||||
inst.opcode == Decoder::Opcode::S_SETPC_B64;
|
||||
});
|
||||
|
||||
if (attrib_reg >= 0 && attrib_reg < static_cast<int>(sgprs.size())) {
|
||||
sgprs[attrib_reg].type = EmbeddedFetchValueType::AttribTable;
|
||||
@@ -299,8 +282,8 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_WRITELANE_B32: {
|
||||
uint32_t lane = 0;
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgprs.size()) {
|
||||
vgprs[inst.dst.reg] = {};
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgpr_is_index.size()) {
|
||||
vgpr_is_index[inst.dst.reg] = false;
|
||||
}
|
||||
if (track_vector_lanes && IsDecodedVgpr(inst.dst) && IsDecodedSgpr(inst.src0) &&
|
||||
DecodedSgprReg(inst.src0) < sgprs.size() &&
|
||||
@@ -405,13 +388,13 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
ClearEmbeddedFetchSgprs(sgprs, inst.dst, DecodedDstSize(inst));
|
||||
}
|
||||
} else if (inst.opcode == Decoder::Opcode::V_CNDMASK_B32) {
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgprs.size()) {
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgpr_is_index.size()) {
|
||||
ClearEmbeddedFetchVectorLanes(&vector_lanes, inst.dst.reg);
|
||||
}
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgprs.size() &&
|
||||
if (IsDecodedVgpr(inst.dst) && inst.dst.reg < vgpr_is_index.size() &&
|
||||
IsDecodedVgpr(inst.src0) && inst.src0.reg == 8 &&
|
||||
IsDecodedVgpr(inst.src1) && inst.src1.reg == 5) {
|
||||
vgprs[inst.dst.reg].type = EmbeddedFetchValueType::Index;
|
||||
vgpr_is_index[inst.dst.reg] = true;
|
||||
}
|
||||
} else if (IsEmbeddedFetchAttribPropagationAlu(inst)) {
|
||||
if (IsDecodedSgpr(inst.dst) && IsDecodedSgpr(inst.src0) &&
|
||||
@@ -441,16 +424,11 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
}
|
||||
}
|
||||
} else if (IsEmbeddedFetchBufferLoad(inst)) {
|
||||
if (IsDecodedVgpr(inst.src0) && inst.src0.reg < vgprs.size() &&
|
||||
vgprs[inst.src0.reg].type == EmbeddedFetchValueType::Index &&
|
||||
if (IsDecodedVgpr(inst.src0) && inst.src0.reg < vgpr_is_index.size() &&
|
||||
vgpr_is_index[inst.src0.reg] &&
|
||||
IsDecodedSgpr(inst.src1) && DecodedSgprReg(inst.src1) < sgprs.size() &&
|
||||
sgprs[DecodedSgprReg(inst.src1)].type == EmbeddedFetchValueType::Buffer) {
|
||||
const auto& buffer = sgprs[DecodedSgprReg(inst.src1)];
|
||||
EmbeddedFetchLoad load;
|
||||
load.pc = inst.pc;
|
||||
load.attrib_id = buffer.attrib_id;
|
||||
load.components = DecodedDstSize(inst);
|
||||
load.prolog_loads = buffer.prolog_loads;
|
||||
const auto& buffer = sgprs[DecodedSgprReg(inst.src1)];
|
||||
if (data.loads.empty()) {
|
||||
if (!vertex_offset_conflict) {
|
||||
data.vertex_offset_sgpr = vertex_offset_candidate;
|
||||
@@ -459,7 +437,11 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
data.instance_offset_sgpr = instance_offset_candidate;
|
||||
}
|
||||
}
|
||||
data.loads.push_back(load);
|
||||
auto& load = data.loads.emplace_back();
|
||||
load.pc = inst.pc;
|
||||
load.attrib_id = buffer.attrib_id;
|
||||
load.components = DecodedDstSize(inst);
|
||||
load.prolog_loads = buffer.prolog_loads;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -467,7 +449,8 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
if (inst.opcode == Decoder::Opcode::V_MOVRELD_B32) {
|
||||
vector_lanes.clear();
|
||||
} else if (inst.opcode != Decoder::Opcode::V_WRITELANE_B32 && IsDecodedVgpr(inst.dst)) {
|
||||
for (uint32_t i = 0; i < EmbeddedFetchDstSize(inst) && inst.dst.reg + i < vgprs.size();
|
||||
for (uint32_t i = 0;
|
||||
i < EmbeddedFetchDstSize(inst) && inst.dst.reg + i < vgpr_is_index.size();
|
||||
i++) {
|
||||
ClearEmbeddedFetchVectorLanes(&vector_lanes, inst.dst.reg + i);
|
||||
}
|
||||
@@ -541,7 +524,7 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
|
||||
|
||||
Decoder::Program decoded;
|
||||
std::vector<uint32_t> joined_code;
|
||||
if (options.stage == ShaderType::Mesh) {
|
||||
if (!options.back_code.empty()) {
|
||||
decoded = DecodeFusedProgram(code, options.back_code, joined_code);
|
||||
} else {
|
||||
Decoder::DecodeProgram(code, decoded);
|
||||
@@ -664,11 +647,6 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
IR::TrackResources(ir);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
if (options.stage == ShaderType::Vertex) {
|
||||
ir.info.vertex_offset_sgpr = embedded_fetch.vertex_offset_sgpr;
|
||||
ir.info.instance_offset_sgpr = embedded_fetch.instance_offset_sgpr;
|
||||
}
|
||||
|
||||
TranslateResult result;
|
||||
result.program = std::move(ir);
|
||||
if (options.dump_ir) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
@@ -319,8 +319,7 @@ std::vector<uint32_t> EmitProgram(const IR::Program& program,
|
||||
ValidateNativeProgram(program);
|
||||
IR::ValidateProgram(program, true);
|
||||
EmitterState state(program, input_info);
|
||||
state.stage = program.stage;
|
||||
state.wave_size = program.wave_size;
|
||||
state.stage = program.stage;
|
||||
const auto* workgroup = ShaderWorkgroupInput(program.stage, input_info);
|
||||
state.lane_count =
|
||||
workgroup != nullptr && program.wave_size == 64u && workgroup->host_subgroup_size == 32u
|
||||
@@ -333,7 +332,7 @@ std::vector<uint32_t> EmitProgram(const IR::Program& program,
|
||||
AllocateInputVariables(state);
|
||||
AllocateOutputVariables(state);
|
||||
DefineModule(state);
|
||||
EmitProgram(state, program);
|
||||
EmitProgram(state);
|
||||
state.builder.AddEntryPoint(ExecutionModelForStage(state.stage), state.main_func, "main",
|
||||
state.interface_variables);
|
||||
|
||||
|
||||
@@ -10,26 +10,6 @@ struct Pair {
|
||||
uint32_t high = 0;
|
||||
};
|
||||
|
||||
uint32_t NewUnary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, value});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t NewBinary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t lhs,
|
||||
uint32_t rhs) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, lhs, rhs});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t NewSelect(EmitterState& state, uint32_t type, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpSelect, type, result, condition, true_value, false_value});
|
||||
return result;
|
||||
}
|
||||
|
||||
Pair ExtractPair(EmitterState& state, uint32_t value) {
|
||||
Pair result {state.builder.AllocateId(), state.builder.AllocateId()};
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), result.low, value, 0});
|
||||
@@ -45,20 +25,20 @@ uint32_t MakePair(EmitterState& state, uint32_t low, uint32_t high) {
|
||||
|
||||
uint32_t CompareEqual64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
|
||||
bool not_equal) {
|
||||
const auto compare = NewBinary(state, not_equal ? OpINotEqual : OpIEqual,
|
||||
const auto compare = Binary(state, not_equal ? OpINotEqual : OpIEqual,
|
||||
TypeBoolVector(state, 2), lhs_value, rhs_value);
|
||||
return NewUnary(state, not_equal ? OpAny : OpAll, TypeBool(state), compare);
|
||||
return Unary(state, not_equal ? OpAny : OpAll, TypeBool(state), compare);
|
||||
}
|
||||
|
||||
uint32_t CompareOrdered64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
|
||||
uint32_t high_compare, uint32_t low_compare) {
|
||||
const auto lhs = ExtractPair(state, lhs_value);
|
||||
const auto rhs = ExtractPair(state, rhs_value);
|
||||
const auto high_equal = NewBinary(state, OpIEqual, TypeBool(state), lhs.high, rhs.high);
|
||||
const auto high_result = NewBinary(state, high_compare, TypeBool(state), lhs.high, rhs.high);
|
||||
const auto low_result = NewBinary(state, low_compare, TypeBool(state), lhs.low, rhs.low);
|
||||
const auto low_path = NewBinary(state, OpLogicalAnd, TypeBool(state), high_equal, low_result);
|
||||
return NewBinary(state, OpLogicalOr, TypeBool(state), high_result, low_path);
|
||||
const auto high_equal = Binary(state, OpIEqual, TypeBool(state), lhs.high, rhs.high);
|
||||
const auto high_result = Binary(state, high_compare, TypeBool(state), lhs.high, rhs.high);
|
||||
const auto low_result = Binary(state, low_compare, TypeBool(state), lhs.low, rhs.low);
|
||||
const auto low_path = Binary(state, OpLogicalAnd, TypeBool(state), high_equal, low_result);
|
||||
return Binary(state, OpLogicalOr, TypeBool(state), high_result, low_path);
|
||||
}
|
||||
|
||||
uint32_t EmitAdd64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
|
||||
@@ -70,20 +50,20 @@ uint32_t EmitAdd64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value)
|
||||
state.builder.AddFunction({OpIAddCarry, TypeU32Pair(state), low_pair, lhs.low, rhs.low});
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, low_pair, 0});
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), carry, low_pair, 1});
|
||||
const auto high0 = NewBinary(state, OpIAdd, TypeU32(state), lhs.high, rhs.high);
|
||||
const auto high = NewBinary(state, OpIAdd, TypeU32(state), high0, carry);
|
||||
const auto high0 = Binary(state, OpIAdd, TypeU32(state), lhs.high, rhs.high);
|
||||
const auto high = Binary(state, OpIAdd, TypeU32(state), high0, carry);
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
|
||||
uint32_t EmitSub64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
|
||||
const auto lhs = ExtractPair(state, lhs_value);
|
||||
const auto rhs = ExtractPair(state, rhs_value);
|
||||
const auto low = NewBinary(state, OpISub, TypeU32(state), lhs.low, rhs.low);
|
||||
const auto borrow = NewBinary(state, OpULessThan, TypeBool(state), lhs.low, rhs.low);
|
||||
const auto low = Binary(state, OpISub, TypeU32(state), lhs.low, rhs.low);
|
||||
const auto borrow = Binary(state, OpULessThan, TypeBool(state), lhs.low, rhs.low);
|
||||
const auto borrow_u32 =
|
||||
NewSelect(state, TypeU32(state), borrow, ConstantU32(state, 1), ConstantU32(state, 0));
|
||||
const auto high0 = NewBinary(state, OpISub, TypeU32(state), lhs.high, rhs.high);
|
||||
const auto high = NewBinary(state, OpISub, TypeU32(state), high0, borrow_u32);
|
||||
Select(state, TypeU32(state), borrow, ConstantU32(state, 1), ConstantU32(state, 0));
|
||||
const auto high0 = Binary(state, OpISub, TypeU32(state), lhs.high, rhs.high);
|
||||
const auto high = Binary(state, OpISub, TypeU32(state), high0, borrow_u32);
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
|
||||
@@ -93,27 +73,27 @@ uint32_t EmitMulHigh(EmitterState& state, uint32_t lhs, uint32_t rhs, bool signe
|
||||
uint32_t lhs_operand = lhs;
|
||||
uint32_t rhs_operand = rhs;
|
||||
if (signed_value) {
|
||||
lhs_operand = NewUnary(state, OpBitcast, TypeI32(state), lhs);
|
||||
rhs_operand = NewUnary(state, OpBitcast, TypeI32(state), rhs);
|
||||
lhs_operand = Unary(state, OpBitcast, TypeI32(state), lhs);
|
||||
rhs_operand = Unary(state, OpBitcast, TypeI32(state), rhs);
|
||||
}
|
||||
const auto extended = state.builder.AllocateId();
|
||||
state.builder.AddFunction({signed_value ? OpSMulExtended : OpUMulExtended, pair_type, extended,
|
||||
lhs_operand, rhs_operand});
|
||||
const auto high = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, operand_type, high, extended, 1});
|
||||
return signed_value ? NewUnary(state, OpBitcast, TypeU32(state), high) : high;
|
||||
return signed_value ? Unary(state, OpBitcast, TypeU32(state), high) : high;
|
||||
}
|
||||
|
||||
uint32_t EmitMul64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) {
|
||||
const auto lhs = ExtractPair(state, lhs_value);
|
||||
const auto rhs = ExtractPair(state, rhs_value);
|
||||
const auto low = NewBinary(state, OpIMul, TypeU32(state), lhs.low, rhs.low);
|
||||
const auto low = Binary(state, OpIMul, TypeU32(state), lhs.low, rhs.low);
|
||||
const auto high0 = EmitMulHigh(state, lhs.low, rhs.low, false);
|
||||
const auto high1 = NewBinary(state, OpIMul, TypeU32(state), lhs.low, rhs.high);
|
||||
const auto high2 = NewBinary(state, OpIMul, TypeU32(state), lhs.high, rhs.low);
|
||||
const auto high1 = Binary(state, OpIMul, TypeU32(state), lhs.low, rhs.high);
|
||||
const auto high2 = Binary(state, OpIMul, TypeU32(state), lhs.high, rhs.low);
|
||||
return MakePair(state, low,
|
||||
NewBinary(state, OpIAdd, TypeU32(state),
|
||||
NewBinary(state, OpIAdd, TypeU32(state), high0, high1), high2));
|
||||
Binary(state, OpIAdd, TypeU32(state),
|
||||
Binary(state, OpIAdd, TypeU32(state), high0, high1), high2));
|
||||
}
|
||||
|
||||
uint32_t EmitShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint32_t shift) {
|
||||
@@ -126,33 +106,33 @@ uint32_t EmitShift64(EmitterState& state, uint32_t opcode, uint32_t value, uint3
|
||||
EmitShiftRightLogicalU64Values(state, pair.low, pair.high, shift, low, high);
|
||||
} else {
|
||||
const auto amount =
|
||||
NewBinary(state, OpBitwiseAnd, TypeU32(state), shift, ConstantU32(state, 63));
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state), shift, ConstantU32(state, 63));
|
||||
const auto word_shift =
|
||||
NewBinary(state, OpBitwiseAnd, TypeU32(state), amount, ConstantU32(state, 31));
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state), amount, ConstantU32(state, 31));
|
||||
const auto at_least_32 =
|
||||
NewBinary(state, OpUGreaterThanEqual, TypeBool(state), amount, ConstantU32(state, 32));
|
||||
Binary(state, OpUGreaterThanEqual, TypeBool(state), amount, ConstantU32(state, 32));
|
||||
const auto nonzero =
|
||||
NewBinary(state, OpINotEqual, TypeBool(state), amount, ConstantU32(state, 0));
|
||||
Binary(state, OpINotEqual, TypeBool(state), amount, ConstantU32(state, 0));
|
||||
const auto high_shifted =
|
||||
NewBinary(state, OpShiftRightArithmetic, TypeU32(state), pair.high, word_shift);
|
||||
Binary(state, OpShiftRightArithmetic, TypeU32(state), pair.high, word_shift);
|
||||
const auto low_shifted =
|
||||
NewBinary(state, OpShiftRightLogical, TypeU32(state), pair.low, word_shift);
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state), pair.low, word_shift);
|
||||
const auto carry_count =
|
||||
NewBinary(state, OpBitwiseAnd, TypeU32(state),
|
||||
NewBinary(state, OpISub, TypeU32(state), ConstantU32(state, 32), word_shift),
|
||||
Binary(state, OpBitwiseAnd, TypeU32(state),
|
||||
Binary(state, OpISub, TypeU32(state), ConstantU32(state, 32), word_shift),
|
||||
ConstantU32(state, 31));
|
||||
const auto carry =
|
||||
NewSelect(state, TypeU32(state), nonzero,
|
||||
NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.high, carry_count),
|
||||
Select(state, TypeU32(state), nonzero,
|
||||
Binary(state, OpShiftLeftLogical, TypeU32(state), pair.high, carry_count),
|
||||
ConstantU32(state, 0));
|
||||
const auto low_below_32 = NewBinary(state, OpBitwiseOr, TypeU32(state), low_shifted, carry);
|
||||
const auto low_below_32 = Binary(state, OpBitwiseOr, TypeU32(state), low_shifted, carry);
|
||||
const auto negative =
|
||||
NewBinary(state, OpSLessThan, TypeBool(state), pair.high, ConstantU32(state, 0));
|
||||
const auto sign_fill = NewSelect(state, TypeU32(state), negative,
|
||||
Binary(state, OpSLessThan, TypeBool(state), pair.high, ConstantU32(state, 0));
|
||||
const auto sign_fill = Select(state, TypeU32(state), negative,
|
||||
ConstantU32(state, 0xffffffffu), ConstantU32(state, 0));
|
||||
return MakePair(state,
|
||||
NewSelect(state, TypeU32(state), at_least_32, high_shifted, low_below_32),
|
||||
NewSelect(state, TypeU32(state), at_least_32, sign_fill, high_shifted));
|
||||
Select(state, TypeU32(state), at_least_32, high_shifted, low_below_32),
|
||||
Select(state, TypeU32(state), at_least_32, sign_fill, high_shifted));
|
||||
}
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
@@ -165,36 +145,36 @@ uint32_t EmitConstantShift64(EmitterState& state, uint32_t opcode, uint32_t valu
|
||||
const auto pair = ExtractPair(state, value);
|
||||
if (opcode == OpShiftLeftLogical) {
|
||||
if (shift < 32u) {
|
||||
const auto low = NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.low,
|
||||
const auto low = Binary(state, OpShiftLeftLogical, TypeU32(state), pair.low,
|
||||
ConstantU32(state, shift));
|
||||
const auto high = NewBinary(state, OpBitwiseOr, TypeU32(state),
|
||||
NewBinary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
const auto high = Binary(state, OpBitwiseOr, TypeU32(state),
|
||||
Binary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
pair.high, ConstantU32(state, shift)),
|
||||
NewBinary(state, OpShiftRightLogical, TypeU32(state),
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state),
|
||||
pair.low, ConstantU32(state, 32u - shift)));
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
return MakePair(state, ConstantU32(state, 0),
|
||||
shift == 32u ? pair.low
|
||||
: NewBinary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
: Binary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
pair.low, ConstantU32(state, shift - 32u)));
|
||||
}
|
||||
if (shift < 32u) {
|
||||
const auto low = NewBinary(state, OpBitwiseOr, TypeU32(state),
|
||||
NewBinary(state, OpShiftRightLogical, TypeU32(state), pair.low,
|
||||
const auto low = Binary(state, OpBitwiseOr, TypeU32(state),
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state), pair.low,
|
||||
ConstantU32(state, shift)),
|
||||
NewBinary(state, OpShiftLeftLogical, TypeU32(state), pair.high,
|
||||
Binary(state, OpShiftLeftLogical, TypeU32(state), pair.high,
|
||||
ConstantU32(state, 32u - shift)));
|
||||
const auto high =
|
||||
NewBinary(state, opcode, TypeU32(state), pair.high, ConstantU32(state, shift));
|
||||
Binary(state, opcode, TypeU32(state), pair.high, ConstantU32(state, shift));
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
const auto high = opcode == OpShiftRightArithmetic
|
||||
? NewBinary(state, OpShiftRightArithmetic, TypeU32(state), pair.high,
|
||||
? Binary(state, OpShiftRightArithmetic, TypeU32(state), pair.high,
|
||||
ConstantU32(state, 31u))
|
||||
: ConstantU32(state, 0);
|
||||
const auto low = shift == 32u ? pair.high
|
||||
: NewBinary(state, opcode, TypeU32(state), pair.high,
|
||||
: Binary(state, opcode, TypeU32(state), pair.high,
|
||||
ConstantU32(state, shift - 32u));
|
||||
return MakePair(state, low, high);
|
||||
}
|
||||
@@ -207,12 +187,12 @@ uint32_t EmitFindMsb64(EmitterState& state, uint32_t value) {
|
||||
{OpExtInst, TypeI32(state), high_i, GlslStd450(state), GlslFindUMsb, pair.high});
|
||||
state.builder.AddFunction(
|
||||
{OpExtInst, TypeI32(state), low_i, GlslStd450(state), GlslFindUMsb, pair.low});
|
||||
const auto high = NewUnary(state, OpBitcast, TypeU32(state), high_i);
|
||||
const auto low = NewUnary(state, OpBitcast, TypeU32(state), low_i);
|
||||
const auto high = Unary(state, OpBitcast, TypeU32(state), high_i);
|
||||
const auto low = Unary(state, OpBitcast, TypeU32(state), low_i);
|
||||
const auto high_nonzero =
|
||||
NewBinary(state, OpINotEqual, TypeBool(state), pair.high, ConstantU32(state, 0));
|
||||
return NewSelect(state, TypeU32(state), high_nonzero,
|
||||
NewBinary(state, OpIAdd, TypeU32(state), high, ConstantU32(state, 32)), low);
|
||||
Binary(state, OpINotEqual, TypeBool(state), pair.high, ConstantU32(state, 0));
|
||||
return Select(state, TypeU32(state), high_nonzero,
|
||||
Binary(state, OpIAdd, TypeU32(state), high, ConstantU32(state, 32)), low);
|
||||
}
|
||||
|
||||
uint32_t EmitMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool signed_value,
|
||||
@@ -226,10 +206,10 @@ uint32_t EmitMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bo
|
||||
uint32_t EmitMed3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool signed_value) {
|
||||
const auto minimum = EmitMinMax3(state, a, b, c, signed_value, false);
|
||||
const auto maximum = EmitMinMax3(state, a, b, c, signed_value, true);
|
||||
const auto ab = NewBinary(state, OpIAdd, TypeU32(state), a, b);
|
||||
const auto abc = NewBinary(state, OpIAdd, TypeU32(state), ab, c);
|
||||
return NewBinary(state, OpISub, TypeU32(state),
|
||||
NewBinary(state, OpISub, TypeU32(state), abc, minimum), maximum);
|
||||
const auto ab = Binary(state, OpIAdd, TypeU32(state), a, b);
|
||||
const auto abc = Binary(state, OpIAdd, TypeU32(state), ab, c);
|
||||
return Binary(state, OpISub, TypeU32(state),
|
||||
Binary(state, OpISub, TypeU32(state), abc, minimum), maximum);
|
||||
}
|
||||
|
||||
uint32_t EmitFMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool max_value) {
|
||||
@@ -242,11 +222,11 @@ uint32_t EmitFMed3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
|
||||
const auto max_ab = EmitMinMaxF32Value(state, a, b, true);
|
||||
const auto high_min = EmitMinMaxF32Value(state, max_ab, c, false);
|
||||
const auto median = EmitMinMaxF32Value(state, min_ab, high_min, true);
|
||||
const auto nan_ab = NewBinary(state, OpLogicalOr, TypeBool(state),
|
||||
const auto nan_ab = Binary(state, OpLogicalOr, TypeBool(state),
|
||||
EmitClassifyF32(state, a).nan, EmitClassifyF32(state, b).nan);
|
||||
const auto any_nan =
|
||||
NewBinary(state, OpLogicalOr, TypeBool(state), nan_ab, EmitClassifyF32(state, c).nan);
|
||||
return NewSelect(state, TypeF32(state), any_nan, min3, median);
|
||||
Binary(state, OpLogicalOr, TypeBool(state), nan_ab, EmitClassifyF32(state, c).nan);
|
||||
return Select(state, TypeF32(state), any_nan, min3, median);
|
||||
}
|
||||
|
||||
uint32_t EmitExt(EmitterState& state, uint32_t type, uint32_t opcode,
|
||||
@@ -270,37 +250,30 @@ uint32_t EmitF32ToU32(EmitterState& state, uint32_t src, bool signed_value) {
|
||||
}
|
||||
const auto nan = EmitClassifyF32(state, src).nan;
|
||||
if (signed_value) {
|
||||
const auto below = NewBinary(state, OpFOrdLessThanEqual, TypeBool(state), src,
|
||||
const auto below = Binary(state, OpFOrdLessThanEqual, TypeBool(state), src,
|
||||
ConstantF32(state, 0xcf000000u));
|
||||
const auto above = NewBinary(state, OpFOrdGreaterThanEqual, TypeBool(state), src,
|
||||
const auto above = Binary(state, OpFOrdGreaterThanEqual, TypeBool(state), src,
|
||||
ConstantF32(state, 0x4f000000u));
|
||||
const auto high =
|
||||
NewSelect(state, TypeU32(state), above, ConstantU32(state, 0x7fffffffu), converted_raw);
|
||||
Select(state, TypeU32(state), above, ConstantU32(state, 0x7fffffffu), converted_raw);
|
||||
const auto low =
|
||||
NewSelect(state, TypeU32(state), below, ConstantU32(state, 0x80000000u), high);
|
||||
return NewSelect(state, TypeU32(state), nan, ConstantU32(state, 0), low);
|
||||
Select(state, TypeU32(state), below, ConstantU32(state, 0x80000000u), high);
|
||||
return Select(state, TypeU32(state), nan, ConstantU32(state, 0), low);
|
||||
}
|
||||
const auto below =
|
||||
NewBinary(state, OpFOrdLessThanEqual, TypeBool(state), src, ConstantF32(state, 0));
|
||||
const auto above = NewBinary(state, OpFOrdGreaterThanEqual, TypeBool(state), src,
|
||||
Binary(state, OpFOrdLessThanEqual, TypeBool(state), src, ConstantF32(state, 0));
|
||||
const auto above = Binary(state, OpFOrdGreaterThanEqual, TypeBool(state), src,
|
||||
ConstantF32(state, 0x4f800000u));
|
||||
const auto zero = NewBinary(state, OpLogicalOr, TypeBool(state), nan, below);
|
||||
const auto zero = Binary(state, OpLogicalOr, TypeBool(state), nan, below);
|
||||
const auto high =
|
||||
NewSelect(state, TypeU32(state), above, ConstantU32(state, 0xffffffffu), converted_raw);
|
||||
return NewSelect(state, TypeU32(state), zero, ConstantU32(state, 0), high);
|
||||
Select(state, TypeU32(state), above, ConstantU32(state, 0xffffffffu), converted_raw);
|
||||
return Select(state, TypeU32(state), zero, ConstantU32(state, 0), high);
|
||||
}
|
||||
|
||||
uint32_t EmitPackHalf(EmitterState& state, uint32_t src) {
|
||||
return EmitExt(state, TypeU32(state), GlslPackHalf2x16, {src});
|
||||
}
|
||||
|
||||
uint32_t EmitUnpackHalf(EmitterState& state, uint32_t bits) {
|
||||
const auto pair = EmitExt(state, TypeF32Vector(state, 2), GlslUnpackHalf2x16, {bits});
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeF32(state), result, pair, 0});
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
@@ -341,7 +314,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::ConvertF32F16:
|
||||
ctx.Define(inst, EmitUnpackHalf(state, ctx.Arg(inst, 0)));
|
||||
ctx.Define(inst, EmitF16BitsToF32(state, ctx.Arg(inst, 0)));
|
||||
return true;
|
||||
case IR::ValueOpcode::ConvertS32F32:
|
||||
ctx.Define(inst, EmitF32ToU32(state, ctx.Arg(inst, 0), true));
|
||||
@@ -350,7 +323,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
ctx.Define(inst, EmitF32ToU32(state, ctx.Arg(inst, 0), false));
|
||||
return true;
|
||||
case IR::ValueOpcode::ConvertF32S32: {
|
||||
const auto signed_value = NewUnary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 0));
|
||||
const auto signed_value = Unary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 0));
|
||||
ctx.Emit(inst, OpConvertSToF, IR::Type::F32, {signed_value});
|
||||
return true;
|
||||
}
|
||||
@@ -394,9 +367,9 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
case IR::ValueOpcode::PackFloat2x16Rtz: {
|
||||
const auto low = EmitF32ToF16RtzBits(state, ctx.Arg(inst, 0));
|
||||
const auto high =
|
||||
NewBinary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
Binary(state, OpShiftLeftLogical, TypeU32(state),
|
||||
EmitF32ToF16RtzBits(state, ctx.Arg(inst, 1)), ConstantU32(state, 16));
|
||||
ctx.Define(inst, NewBinary(state, OpBitwiseOr, TypeU32(state), low, high));
|
||||
ctx.Define(inst, Binary(state, OpBitwiseOr, TypeU32(state), low, high));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FPAbs32:
|
||||
@@ -458,10 +431,10 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return true;
|
||||
case IR::ValueOpcode::IAbs32: {
|
||||
const auto value = ctx.Arg(inst, 0);
|
||||
const auto neg = NewUnary(state, OpSNegate, TypeU32(state), value);
|
||||
const auto neg = Unary(state, OpSNegate, TypeU32(state), value);
|
||||
const auto negative =
|
||||
NewBinary(state, OpSLessThan, TypeBool(state), value, ConstantU32(state, 0));
|
||||
ctx.Define(inst, NewSelect(state, TypeU32(state), negative, neg, value));
|
||||
Binary(state, OpSLessThan, TypeBool(state), value, ConstantU32(state, 0));
|
||||
ctx.Define(inst, Select(state, TypeU32(state), negative, neg, value));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::ShiftLeftLogical32: return binary(OpShiftLeftLogical, IR::Type::U32);
|
||||
@@ -496,25 +469,25 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
case IR::ValueOpcode::BitwiseXor32: return binary(OpBitwiseXor, IR::Type::U32);
|
||||
case IR::ValueOpcode::BitwiseNot32: return unary(OpNot, IR::Type::U32);
|
||||
case IR::ValueOpcode::BitwiseAnd64:
|
||||
ctx.Define(inst, NewBinary(state, OpBitwiseAnd, TypeU64(state), ctx.Arg(inst, 0),
|
||||
ctx.Define(inst, Binary(state, OpBitwiseAnd, TypeU64(state), ctx.Arg(inst, 0),
|
||||
ctx.Arg(inst, 1)));
|
||||
return true;
|
||||
case IR::ValueOpcode::BitReverse32: return unary(OpBitReverse, IR::Type::U32);
|
||||
case IR::ValueOpcode::BitCount32: return unary(OpBitCount, IR::Type::U32);
|
||||
case IR::ValueOpcode::BitCount64: {
|
||||
const auto pair =
|
||||
ExtractPair(state, NewUnary(state, OpBitCount, TypeU64(state), ctx.Arg(inst, 0)));
|
||||
ctx.Define(inst, NewBinary(state, OpIAdd, TypeU32(state), pair.low, pair.high));
|
||||
ExtractPair(state, Unary(state, OpBitCount, TypeU64(state), ctx.Arg(inst, 0)));
|
||||
ctx.Define(inst, Binary(state, OpIAdd, TypeU32(state), pair.low, pair.high));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FindILsb32: {
|
||||
const auto value = EmitExt(state, TypeI32(state), GlslFindILsb, {ctx.Arg(inst, 0)});
|
||||
ctx.Define(inst, NewUnary(state, OpBitcast, TypeU32(state), value));
|
||||
ctx.Define(inst, Unary(state, OpBitcast, TypeU32(state), value));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FindUMsb32: {
|
||||
const auto value = EmitExt(state, TypeI32(state), GlslFindUMsb, {ctx.Arg(inst, 0)});
|
||||
ctx.Define(inst, NewUnary(state, OpBitcast, TypeU32(state), value));
|
||||
ctx.Define(inst, Unary(state, OpBitcast, TypeU32(state), value));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FindUMsb64:
|
||||
@@ -638,13 +611,13 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return true;
|
||||
case IR::ValueOpcode::FPRecip32: {
|
||||
const auto source = EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0));
|
||||
ctx.Define(inst, NewBinary(state, OpFDiv, TypeF32(state),
|
||||
ctx.Define(inst, Binary(state, OpFDiv, TypeF32(state),
|
||||
ConstantF32(state, 0x3f800000u), source));
|
||||
return true;
|
||||
}
|
||||
case IR::ValueOpcode::FPRecipIFlag32:
|
||||
// Integer-to-float inputs used by IFLAG cannot be denormal.
|
||||
ctx.Define(inst, NewBinary(state, OpFDiv, TypeF32(state),
|
||||
ctx.Define(inst, Binary(state, OpFDiv, TypeF32(state),
|
||||
ConstantF32(state, 0x3f800000u), ctx.Arg(inst, 0)));
|
||||
return true;
|
||||
case IR::ValueOpcode::FPRecipSqrt32:
|
||||
@@ -659,7 +632,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
case IR::ValueOpcode::FPCos: {
|
||||
auto source = EmitTrigCycleF32(state, ctx.Arg(inst, 0), op == IR::ValueOpcode::FPSin);
|
||||
source =
|
||||
NewBinary(state, OpFMul, TypeF32(state), source, ConstantF32(state, 0x40c90fdbu));
|
||||
Binary(state, OpFMul, TypeF32(state), source, ConstantF32(state, 0x40c90fdbu));
|
||||
ctx.Define(inst, EmitExt(state, TypeF32(state),
|
||||
op == IR::ValueOpcode::FPSin ? GlslSin : GlslCos, {source}));
|
||||
return true;
|
||||
@@ -673,7 +646,7 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
{EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))}));
|
||||
return true;
|
||||
case IR::ValueOpcode::FPLdexp: {
|
||||
const auto exponent = NewUnary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 1));
|
||||
const auto exponent = Unary(state, OpBitcast, TypeI32(state), ctx.Arg(inst, 1));
|
||||
ctx.Define(inst,
|
||||
EmitExt(state, TypeF32(state), GlslLdexp, {ctx.Arg(inst, 0), exponent}));
|
||||
return true;
|
||||
|
||||
@@ -215,9 +215,9 @@ uint32_t EmitLogicalNotBool(EmitterState& state, uint32_t value) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
F32Class EmitClassifyF32(EmitterState& state, uint32_t value) {
|
||||
F32Class EmitClassifyF32Bits(EmitterState& state, uint32_t bits) {
|
||||
F32Class cls;
|
||||
cls.bits = EmitBitcastF32ToU32(state, value);
|
||||
cls.bits = bits;
|
||||
const auto abs_bits = EmitAndConstant(state, cls.bits, 0x7fffffffu);
|
||||
const auto exponent_bits = EmitAndConstant(state, abs_bits, 0x7f800000u);
|
||||
const auto mantissa_bits = EmitAndConstant(state, abs_bits, 0x007fffffu);
|
||||
@@ -228,6 +228,10 @@ F32Class EmitClassifyF32(EmitterState& state, uint32_t value) {
|
||||
return cls;
|
||||
}
|
||||
|
||||
F32Class EmitClassifyF32(EmitterState& state, uint32_t value) {
|
||||
return EmitClassifyF32Bits(state, EmitBitcastF32ToU32(state, value));
|
||||
}
|
||||
|
||||
uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit,
|
||||
uint32_t class_match) {
|
||||
const auto selected =
|
||||
|
||||
@@ -6,13 +6,6 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr) {
|
||||
if (state.stage != ShaderType::Pixel) {
|
||||
return attr;
|
||||
}
|
||||
return ShaderPixelParameterMappedLocation(*state.input_info.pixel, attr);
|
||||
}
|
||||
|
||||
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr) {
|
||||
std::array<uint32_t, 32> active_inputs {};
|
||||
uint32_t active_count = 0;
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
uint32_t EmitTrueBool(EmitterState& state) {
|
||||
return ConstantBool(state, true);
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control) {
|
||||
const auto quad_base = state.builder.AllocateId();
|
||||
const auto lane = state.builder.AllocateId();
|
||||
@@ -23,7 +19,7 @@ DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uin
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseAnd, TypeU32(state), selected, selected0, ConstantU32(state, 3)});
|
||||
state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, quad_base, selected});
|
||||
return {target, EmitTrueBool(state)};
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount,
|
||||
@@ -70,7 +66,7 @@ DppTargetLane EmitDppRowRotateRightTargetLane(EmitterState& state, uint32_t subi
|
||||
{OpIAdd, TypeU32(state), plus, lane, ConstantU32(state, 16u - amount)});
|
||||
state.builder.AddFunction({OpSelect, TypeU32(state), selected, in_high, minus, plus});
|
||||
state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, row, selected});
|
||||
return {target, EmitTrueBool(state)};
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool half_row) {
|
||||
@@ -87,7 +83,7 @@ DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool
|
||||
state.builder.AddFunction(
|
||||
{OpISub, TypeU32(state), mirrored, ConstantU32(state, lane_mask), lane});
|
||||
state.builder.AddFunction({OpBitwiseOr, TypeU32(state), target, base, mirrored});
|
||||
return {target, EmitTrueBool(state)};
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control) {
|
||||
@@ -114,9 +110,9 @@ DppTargetLane EmitDppTargetLane(EmitterState& state, uint32_t control) {
|
||||
const auto target = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseXor, TypeU32(state), target, subid, ConstantU32(state, control & 0xfu)});
|
||||
return {target, EmitTrueBool(state)};
|
||||
return {target, ConstantBool(state, true)};
|
||||
}
|
||||
return {subid, EmitTrueBool(state)};
|
||||
return {subid, ConstantBool(state, true)};
|
||||
}
|
||||
|
||||
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state) {
|
||||
@@ -184,7 +180,7 @@ uint32_t EmitVertexParameterComponentU32(EmitterState& state, const InputBinding
|
||||
if (count == 1u) {
|
||||
state.builder.AddFunction({OpLoad, scalar_type, raw, input.variable_id});
|
||||
} else {
|
||||
const auto pointer_type = VertexParameterScalarPointerType(state, kind);
|
||||
const auto pointer_type = TypePointer(state, StorageClassInput, scalar_type);
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, pointer_type, pointer, input.variable_id,
|
||||
ConstantU32(state, component)});
|
||||
@@ -203,14 +199,14 @@ uint32_t EmitVertexParameterComponentU32(EmitterState& state, const InputBinding
|
||||
uint32_t EmitSubgroupLaneActiveBool(EmitterState& state, uint32_t lane) {
|
||||
const auto active_ballot = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpGroupNonUniformBallot, TypeU32Vector(state, 4), active_ballot,
|
||||
ConstantU32(state, ScopeSubgroup), EmitTrueBool(state)});
|
||||
ConstantU32(state, ScopeSubgroup), ConstantBool(state, true)});
|
||||
return EmitBallotLaneActiveBool(state, active_ballot, lane);
|
||||
}
|
||||
uint32_t EmitBallotLaneActiveBool(EmitterState& state, uint32_t active_ballot, uint32_t lane) {
|
||||
const auto low = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, active_ballot, 0});
|
||||
uint32_t mask = low;
|
||||
if (state.wave_size == 64u) {
|
||||
if (state.program.wave_size == 64u) {
|
||||
const auto high = state.builder.AllocateId();
|
||||
const auto in_high = state.builder.AllocateId();
|
||||
const auto selected = state.builder.AllocateId();
|
||||
@@ -235,7 +231,7 @@ uint32_t EmitBallotLaneActiveBool(EmitterState& state, uint32_t active_ballot, u
|
||||
state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), hit, mask, bit});
|
||||
state.builder.AddFunction({OpINotEqual, TypeBool(state), active, hit, ConstantU32(state, 0)});
|
||||
state.builder.AddFunction(
|
||||
{OpULessThan, TypeBool(state), in_range, lane, ConstantU32(state, state.wave_size)});
|
||||
{OpULessThan, TypeBool(state), in_range, lane, ConstantU32(state, state.program.wave_size)});
|
||||
state.builder.AddFunction({OpLogicalAnd, TypeBool(state), ret, active, in_range});
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -32,18 +32,6 @@ uint32_t ImageGatherComponent(uint32_t dmask) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Unary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, value});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Binary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t lhs, uint32_t rhs) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, lhs, rhs});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HasFlag(const IR::MemoryInfo& mem, uint32_t flag) {
|
||||
return (mem.image_sample_flags & flag) != 0u;
|
||||
}
|
||||
@@ -768,8 +756,8 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return true;
|
||||
}
|
||||
const auto* handle = image_arg.ResolveInstruction();
|
||||
const auto* source = image.source < ctx.program.descriptor_sources.size()
|
||||
? &ctx.program.descriptor_sources[image.source]
|
||||
const auto* source = image.source < state.program.descriptor_sources.size()
|
||||
? &state.program.descriptor_sources[image.source]
|
||||
: nullptr;
|
||||
if (handle == nullptr || source == nullptr || !source->indirect_image.has_value() ||
|
||||
source->indirect_image->key_arg >= handle->NumArgs()) {
|
||||
|
||||
@@ -360,7 +360,6 @@ struct EmitterState {
|
||||
ShaderStageInputInfo input_info;
|
||||
const IR::SpirvRequirements& requirements;
|
||||
ShaderType stage = ShaderType::Unknown;
|
||||
uint32_t wave_size = 64;
|
||||
uint32_t lane_count = 1;
|
||||
uint32_t lane_half = 0;
|
||||
uint32_t storage_buffer_variable = 0;
|
||||
@@ -386,6 +385,7 @@ struct EmitterState {
|
||||
uint32_t mesh_cull = 0;
|
||||
uint32_t entry_label = 0;
|
||||
uint32_t current_label = 0;
|
||||
const IR::Block* current_block = nullptr;
|
||||
uint32_t pixel_valid_mask_variable = 0;
|
||||
uint32_t subgroup_local_invocation_id_variable = 0;
|
||||
uint32_t per_vertex_variable = 0;
|
||||
@@ -401,6 +401,7 @@ struct EmitterState {
|
||||
std::vector<InputBinding> inputs;
|
||||
std::vector<OutputBinding> outputs;
|
||||
std::vector<uint32_t> interface_variables;
|
||||
std::unordered_map<const IR::Block*, uint32_t> labels;
|
||||
};
|
||||
|
||||
uint32_t TypeVoid(EmitterState& state);
|
||||
@@ -436,9 +437,28 @@ inline void EmitLabel(EmitterState& state, uint32_t label) {
|
||||
state.builder.AddFunction({OpLabel, label});
|
||||
}
|
||||
|
||||
inline uint32_t Unary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, value});
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint32_t Binary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t lhs,
|
||||
uint32_t rhs) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, lhs, rhs});
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint32_t Select(EmitterState& state, uint32_t type, uint32_t condition,
|
||||
uint32_t true_value, uint32_t false_value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpSelect, type, result, condition, true_value, false_value});
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ValueEmitContext {
|
||||
ValueEmitContext(EmitterState& state_, const IR::Program& program_)
|
||||
: state(state_), program(program_) {}
|
||||
explicit ValueEmitContext(EmitterState& state_): state(state_) {}
|
||||
|
||||
uint32_t Def(IR::Value value);
|
||||
uint32_t Arg(const IR::Inst& inst, size_t index);
|
||||
@@ -460,12 +480,9 @@ struct ValueEmitContext {
|
||||
[[noreturn]] void Fail(const IR::Inst& inst, const char* reason) const;
|
||||
|
||||
EmitterState& state;
|
||||
const IR::Program& program;
|
||||
std::unordered_map<const IR::Inst*, uint32_t> definitions;
|
||||
std::unordered_map<const IR::Block*, uint32_t> labels;
|
||||
const std::unordered_map<const IR::Inst*, uint32_t>* dispatcher_spills = nullptr;
|
||||
std::unordered_map<const IR::Inst*, std::pair<uint32_t, uint32_t>> dispatcher_block_loads;
|
||||
const IR::Block* current_block = nullptr;
|
||||
uint32_t scratch_u32_variable = 0;
|
||||
ValueEmitContext* other_half = nullptr;
|
||||
uint32_t half = 0;
|
||||
@@ -496,8 +513,6 @@ struct F32Class {
|
||||
uint32_t zero = 0;
|
||||
};
|
||||
|
||||
uint32_t PixelParameterMappedLocation(const EmitterState& state, uint32_t attr);
|
||||
|
||||
uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr);
|
||||
|
||||
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr);
|
||||
@@ -510,11 +525,6 @@ uint32_t VertexParameterComponentCount(const InputBinding& input);
|
||||
|
||||
uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind kind);
|
||||
|
||||
uint32_t VertexParameterScalarPointerType(EmitterState& state, VertexInputScalarKind kind);
|
||||
|
||||
uint32_t VertexParameterVectorOrScalarType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components);
|
||||
|
||||
uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components);
|
||||
|
||||
@@ -602,8 +612,6 @@ void EmitMeshAllocate(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
uint32_t MeshOutputPointer(EmitterState& state, IR::StageOutputKind kind, uint32_t index = 0);
|
||||
uint32_t MeshPrimitivePointer(EmitterState& state);
|
||||
|
||||
uint32_t EmitTrueBool(EmitterState& state);
|
||||
|
||||
DppTargetLane EmitDppQuadPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control);
|
||||
|
||||
DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount,
|
||||
@@ -675,22 +683,13 @@ uint32_t EmitStorageBufferElementPointer(EmitterState& state,
|
||||
const MemoryResourceAccess& access, uint32_t index,
|
||||
uint32_t pointer_type);
|
||||
|
||||
uint32_t EmitTBufferBitcastF32ToU32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitTBufferBitcastU32ToF32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitTBufferCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value,
|
||||
uint32_t constant);
|
||||
|
||||
uint32_t EmitTBufferSelectF32(EmitterState& state, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value);
|
||||
|
||||
bool IsSignedFormatComponent(Format::ComponentType type);
|
||||
|
||||
uint32_t EmitHalfToF32Bits(EmitterState& state, uint32_t raw);
|
||||
|
||||
uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits);
|
||||
|
||||
uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
@@ -741,6 +740,8 @@ uint32_t EmitLogicalOrBool(EmitterState& state, uint32_t lhs, uint32_t rhs);
|
||||
|
||||
uint32_t EmitLogicalNotBool(EmitterState& state, uint32_t value);
|
||||
|
||||
F32Class EmitClassifyF32Bits(EmitterState& state, uint32_t bits);
|
||||
|
||||
F32Class EmitClassifyF32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit,
|
||||
@@ -770,7 +771,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
|
||||
bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
|
||||
void EmitProgram(EmitterState& state, const IR::Program& program);
|
||||
void EmitProgram(EmitterState& state);
|
||||
|
||||
void DefineGetBdaPointer(EmitterState& state);
|
||||
|
||||
|
||||
@@ -7,25 +7,6 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
namespace {
|
||||
|
||||
uint32_t Unary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, value});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Binary(EmitterState& state, uint32_t opcode, uint32_t type, uint32_t lhs, uint32_t rhs) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, type, result, lhs, rhs});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Select(EmitterState& state, uint32_t type, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
const auto result = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpSelect, type, result, condition, true_value, false_value});
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t AndCondition(EmitterState& state, uint32_t lhs, uint32_t rhs) {
|
||||
return Binary(state, OpLogicalAnd, TypeBool(state), lhs, rhs);
|
||||
}
|
||||
@@ -290,12 +271,6 @@ PreparedMemoryElement PrepareMemoryElement(ValueEmitContext& ctx, const IR::Memo
|
||||
return {.resource = resource, .index = index};
|
||||
}
|
||||
|
||||
PreparedMemoryElement PrepareMemoryElement(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource,
|
||||
uint32_t raw_index) {
|
||||
return {.resource = resource, .index = EmitMemoryElementIndex(ctx.state, resource, raw_index)};
|
||||
}
|
||||
|
||||
uint32_t LoadWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource,
|
||||
uint32_t index);
|
||||
|
||||
@@ -304,10 +279,10 @@ uint32_t LoadSubwordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess&
|
||||
|
||||
uint32_t LoadWordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource) {
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, DwordIndex(ctx, inst, mem));
|
||||
const auto index = EmitMemoryElementIndex(ctx.state, resource, DwordIndex(ctx, inst, mem));
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index),
|
||||
[&]() { return LoadWordInBounds(ctx, access.resource, access.index); });
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index),
|
||||
[&]() { return LoadWordInBounds(ctx, resource, index); });
|
||||
}
|
||||
|
||||
uint32_t LoadWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem) {
|
||||
@@ -323,11 +298,10 @@ uint32_t LoadSubwordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const
|
||||
const auto address = ByteAddress(ctx, inst, mem);
|
||||
const auto raw_index = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), address,
|
||||
ConstantU32(ctx.state, 2));
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
const auto index = EmitMemoryElementIndex(ctx.state, resource, raw_index);
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
return LoadSubwordInBounds(ctx, access.resource, address, access.index, bits,
|
||||
sign_extend);
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index), [&]() {
|
||||
return LoadSubwordInBounds(ctx, resource, address, index, bits, sign_extend);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -365,15 +339,14 @@ uint32_t LoadSubword(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo
|
||||
});
|
||||
}
|
||||
|
||||
Prospero::BufferFormat BufferFormat(const ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
const IR::MemoryInfo& mem) {
|
||||
Prospero::BufferFormat BufferFormat(const ValueEmitContext& ctx, const IR::MemoryInfo& mem) {
|
||||
return mem.typed ? Format::DecodeTBufferFormat(mem.data_format, mem.number_format)
|
||||
: StorageBufferFormat(ctx.state, mem);
|
||||
}
|
||||
|
||||
IR::MemoryInfo RebaseFormattedComponent(IR::MemoryInfo mem, Prospero::BufferFormat format,
|
||||
IR::MemoryInfo RebaseFormattedComponent(IR::MemoryInfo mem, const Format::BufferFormatInfo& info,
|
||||
uint32_t component) {
|
||||
mem.offset += Format::GetFormatComponentByteOffset(format, component);
|
||||
mem.offset += Format::GetFormatComponentByteOffset(info, component);
|
||||
mem.data_dwords = 1u;
|
||||
mem.component_index = component;
|
||||
return mem;
|
||||
@@ -414,7 +387,7 @@ uint32_t FormattedConstant(ValueEmitContext& ctx, const Format::BufferFormatInfo
|
||||
|
||||
template <typename LoadWordFn, typename LoadSubwordFn>
|
||||
uint32_t LoadFormattedComponent(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
Prospero::BufferFormat format, const Format::BufferFormatInfo& info,
|
||||
const Format::BufferFormatInfo& info,
|
||||
uint32_t output_component, LoadWordFn&& load_word,
|
||||
LoadSubwordFn&& load_subword) {
|
||||
const auto source = ResolveFormattedSource(ctx, mem, info, output_component);
|
||||
@@ -449,19 +422,18 @@ uint32_t LoadFormattedComponent(ValueEmitContext& ctx, const IR::MemoryInfo& mem
|
||||
uint32_t FormattedLoadPrepared(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
const IR::MemoryInfo& mem, uint32_t output_component,
|
||||
const MemoryResourceAccess& resource) {
|
||||
const auto format = BufferFormat(ctx, inst, mem);
|
||||
if (!Format::IsKnownFormat(format)) {
|
||||
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
|
||||
if (info.type == Format::ComponentType::Unknown) {
|
||||
return LoadWordPrepared(ctx, inst, RebaseRawComponent(mem, output_component), resource);
|
||||
}
|
||||
const auto info = Format::GetFormatInfo(format);
|
||||
return LoadFormattedComponent(
|
||||
ctx, mem, format, info, output_component,
|
||||
ctx, mem, info, output_component,
|
||||
[&](uint32_t component) {
|
||||
return LoadWordPrepared(ctx, inst, RebaseFormattedComponent(mem, format, component),
|
||||
return LoadWordPrepared(ctx, inst, RebaseFormattedComponent(mem, info, component),
|
||||
resource);
|
||||
},
|
||||
[&](uint32_t component, uint32_t bits, bool sign_extend) {
|
||||
return LoadSubwordPrepared(ctx, inst, RebaseFormattedComponent(mem, format, component),
|
||||
return LoadSubwordPrepared(ctx, inst, RebaseFormattedComponent(mem, info, component),
|
||||
resource, bits, sign_extend);
|
||||
});
|
||||
}
|
||||
@@ -507,10 +479,10 @@ void StoreSubwordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR:
|
||||
const auto address = ByteAddress(ctx, inst, mem);
|
||||
const auto raw_index = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state), address,
|
||||
ConstantU32(ctx.state, 2));
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
const auto index = EmitMemoryElementIndex(ctx.state, resource, raw_index);
|
||||
EmitIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
StoreSubwordInBounds(ctx, mem, access.resource, address, access.index, bits, data);
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index), [&]() {
|
||||
StoreSubwordInBounds(ctx, mem, resource, address, index, bits, data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -523,12 +495,11 @@ void StoreSubword(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo me
|
||||
|
||||
void StoreWordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource, uint32_t data) {
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, DwordIndex(ctx, inst, mem));
|
||||
const auto index = EmitMemoryElementIndex(ctx.state, resource, DwordIndex(ctx, inst, mem));
|
||||
EmitIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, resource, index), [&]() {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpStore, EmitMemoryElementPointer(ctx.state, access.resource, access.index),
|
||||
data});
|
||||
{OpStore, EmitMemoryElementPointer(ctx.state, resource, index), data});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -548,15 +519,14 @@ void StoreWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem)
|
||||
void FormattedStorePrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
uint32_t component, const MemoryResourceAccess& resource,
|
||||
uint32_t data) {
|
||||
const auto format = BufferFormat(ctx, inst, mem);
|
||||
if (!Format::IsKnownFormat(format)) {
|
||||
const auto info = Format::GetFormatInfo(BufferFormat(ctx, mem));
|
||||
if (info.type == Format::ComponentType::Unknown) {
|
||||
StoreWordPrepared(ctx, inst, RebaseRawComponent(mem, component), resource, data);
|
||||
return;
|
||||
}
|
||||
const auto info = Format::GetFormatInfo(format);
|
||||
if (component >= info.component_count) return;
|
||||
const auto bits = info.component_bits[component];
|
||||
const auto component_mem = RebaseFormattedComponent(mem, format, component);
|
||||
const auto component_mem = RebaseFormattedComponent(mem, info, component);
|
||||
if (bits == 8u || bits == 16u) {
|
||||
StoreSubwordPrepared(ctx, inst, component_mem, resource, bits, data);
|
||||
} else {
|
||||
@@ -619,31 +589,58 @@ uint32_t EmitAtomicOperation(ValueEmitContext& ctx, const IR::Inst& inst, uint32
|
||||
return old;
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
uint32_t EmitAtomicAccess(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
const IR::MemoryInfo& mem, Fn&& operation) {
|
||||
return EmitValueOrZeroIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
|
||||
const auto access = PrepareMemoryElement(ctx, mem, DwordIndex(ctx, inst, mem));
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
return operation(EmitMemoryElementPointer(ctx.state, access.resource, access.index));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t EmitAtomic(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
|
||||
const auto result =
|
||||
EmitValueOrZeroIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
|
||||
const auto access = PrepareMemoryElement(ctx, mem, DwordIndex(ctx, inst, mem));
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index),
|
||||
[&]() {
|
||||
const auto scope =
|
||||
mem.kind == IR::ResourceKind::Lds ? ScopeWorkgroup : ScopeDevice;
|
||||
const auto pointer =
|
||||
EmitMemoryElementPointer(ctx.state, access.resource, access.index);
|
||||
const auto old = EmitAtomicOperation(ctx, inst, pointer, scope);
|
||||
if (mem.kind == IR::ResourceKind::Lds) {
|
||||
const auto semantics =
|
||||
MemorySemanticsAcquireRelease | MemorySemanticsWorkgroupMemory;
|
||||
ctx.state.builder.AddFunction({OpMemoryBarrier,
|
||||
ConstantU32(ctx.state, ScopeWorkgroup),
|
||||
ConstantU32(ctx.state, semantics)});
|
||||
} else {
|
||||
EmitDeviceAtomicMemoryBarrier(ctx.state);
|
||||
}
|
||||
return old;
|
||||
});
|
||||
});
|
||||
return result;
|
||||
return EmitAtomicAccess(ctx, inst, mem, [&](uint32_t pointer) {
|
||||
const auto scope = mem.kind == IR::ResourceKind::Lds ? ScopeWorkgroup : ScopeDevice;
|
||||
const auto old = EmitAtomicOperation(ctx, inst, pointer, scope);
|
||||
if (mem.kind == IR::ResourceKind::Lds) {
|
||||
const auto semantics = MemorySemanticsAcquireRelease | MemorySemanticsWorkgroupMemory;
|
||||
ctx.state.builder.AddFunction({OpMemoryBarrier, ConstantU32(ctx.state, scope),
|
||||
ConstantU32(ctx.state, semantics)});
|
||||
} else {
|
||||
EmitDeviceAtomicMemoryBarrier(ctx.state);
|
||||
}
|
||||
return old;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
uint32_t EmitAtomicUpdate(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
const IR::MemoryInfo& mem, Fn&& replacement) {
|
||||
const auto value = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
return EmitAtomicAccess(ctx, inst, mem, [&](uint32_t pointer) {
|
||||
return AtomicUpdate(ctx.state, pointer, mem.kind, [&](uint32_t old) {
|
||||
return replacement(ctx.state, old, value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t AtomicIncrement(EmitterState& state, uint32_t old, uint32_t limit) {
|
||||
// old >= limit ? 0 : old + 1 (unsigned).
|
||||
const auto wrap = Binary(state, OpUGreaterThanEqual, TypeBool(state), old, limit);
|
||||
const auto next = Binary(state, OpIAdd, TypeU32(state), old, ConstantU32(state, 1));
|
||||
return Select(state, TypeU32(state), wrap, ConstantU32(state, 0), next);
|
||||
}
|
||||
|
||||
uint32_t AtomicDecrement(EmitterState& state, uint32_t old, uint32_t limit) {
|
||||
// old == 0 || old > limit ? limit : old - 1 (unsigned).
|
||||
const auto zero = Binary(state, OpIEqual, TypeBool(state), old, ConstantU32(state, 0));
|
||||
const auto above = Binary(state, OpUGreaterThan, TypeBool(state), old, limit);
|
||||
const auto wrap = Binary(state, OpLogicalOr, TypeBool(state), zero, above);
|
||||
const auto next = Binary(state, OpISub, TypeU32(state), old, ConstantU32(state, 1));
|
||||
return Select(state, TypeU32(state), wrap, limit, next);
|
||||
}
|
||||
|
||||
uint32_t EmitBufferAtomic64(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
@@ -677,17 +674,9 @@ uint32_t EmitBufferAtomic64(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
|
||||
uint32_t FloatAtomic(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
|
||||
bool max_value) {
|
||||
return EmitValueOrZeroIfCondition(ctx.state, ctx.Arg(inst, inst.NumArgs() - 1), [&]() {
|
||||
const auto access = PrepareMemoryElement(ctx, mem, DwordIndex(ctx, inst, mem));
|
||||
return EmitValueOrZeroIfCondition(
|
||||
ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() {
|
||||
const auto pointer =
|
||||
EmitMemoryElementPointer(ctx.state, access.resource, access.index);
|
||||
const auto source = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
return AtomicUpdate(ctx.state, pointer, mem.kind, [&](uint32_t old) {
|
||||
return EmitFloatAtomicReplacement(ctx.state, old, source, max_value);
|
||||
});
|
||||
});
|
||||
return EmitAtomicUpdate(ctx, inst, mem, [max_value](EmitterState& state, uint32_t old,
|
||||
uint32_t value) {
|
||||
return EmitFloatAtomicReplacement(state, old, value, max_value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -781,7 +770,6 @@ uint32_t AppendConsume(ValueEmitContext& ctx, const IR::Inst& inst, bool append)
|
||||
}
|
||||
|
||||
struct PreparedFormattedMemory {
|
||||
Prospero::BufferFormat format = Prospero::BufferFormat::kInvalid;
|
||||
Format::BufferFormatInfo info;
|
||||
MemoryResourceAccess resource;
|
||||
std::array<uint32_t, 4> addresses {};
|
||||
@@ -794,10 +782,10 @@ enum class FormattedAccess { Load, Store };
|
||||
PreparedFormattedMemory PrepareFormattedMemory(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
const IR::MemoryInfo& mem,
|
||||
const MemoryResourceAccess& resource,
|
||||
const Format::BufferFormatInfo& info,
|
||||
uint32_t components, FormattedAccess access) {
|
||||
PreparedFormattedMemory plan;
|
||||
plan.format = BufferFormat(ctx, inst, mem);
|
||||
plan.info = Format::GetFormatInfo(plan.format);
|
||||
plan.info = info;
|
||||
plan.resource = resource;
|
||||
std::array<bool, 4> required_components {};
|
||||
if (access == FormattedAccess::Load) {
|
||||
@@ -816,11 +804,11 @@ PreparedFormattedMemory PrepareFormattedMemory(ValueEmitContext& ctx, const IR::
|
||||
bool first_bound = true;
|
||||
for (uint32_t component = 0; component < plan.info.component_count; component++) {
|
||||
if (!required_components[component]) continue;
|
||||
const auto byte_offset = Format::GetFormatComponentByteOffset(plan.format, component);
|
||||
const auto byte_offset = Format::GetFormatComponentByteOffset(plan.info, component);
|
||||
bool reused = false;
|
||||
for (uint32_t previous = 0; previous < component; previous++) {
|
||||
if (required_components[previous] &&
|
||||
Format::GetFormatComponentByteOffset(plan.format, previous) == byte_offset) {
|
||||
Format::GetFormatComponentByteOffset(plan.info, previous) == byte_offset) {
|
||||
plan.addresses[component] = plan.addresses[previous];
|
||||
plan.indices[component] = plan.indices[previous];
|
||||
reused = true;
|
||||
@@ -828,7 +816,7 @@ PreparedFormattedMemory PrepareFormattedMemory(ValueEmitContext& ctx, const IR::
|
||||
}
|
||||
}
|
||||
if (reused) continue;
|
||||
const auto component_mem = RebaseFormattedComponent(mem, plan.format, component);
|
||||
const auto component_mem = RebaseFormattedComponent(mem, plan.info, component);
|
||||
plan.addresses[component] = ByteAddress(ctx, inst, component_mem);
|
||||
const auto raw_index = Binary(ctx.state, OpShiftRightLogical, TypeU32(ctx.state),
|
||||
plan.addresses[component], ConstantU32(ctx.state, 2));
|
||||
@@ -849,7 +837,7 @@ PreparedFormattedMemory PrepareFormattedMemory(ValueEmitContext& ctx, const IR::
|
||||
uint32_t LoadFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem,
|
||||
const PreparedFormattedMemory& plan, uint32_t output_component) {
|
||||
return LoadFormattedComponent(
|
||||
ctx, mem, plan.format, plan.info, output_component,
|
||||
ctx, mem, plan.info, output_component,
|
||||
[&](uint32_t component) {
|
||||
return LoadWordInBounds(ctx, plan.resource, plan.indices[component]);
|
||||
},
|
||||
@@ -898,10 +886,10 @@ uint32_t LoadWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t co
|
||||
ConstantU32CompositeZero(state, components), [&]() {
|
||||
const auto mem = ctx.Memory(inst);
|
||||
const auto resource = PrepareMemoryResourceAccess(state, mem);
|
||||
const auto format =
|
||||
mem.formatted ? BufferFormat(ctx, inst, mem) : Prospero::BufferFormat::kInvalid;
|
||||
if (Format::IsKnownFormat(format)) {
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, components,
|
||||
const auto info = Format::GetFormatInfo(
|
||||
mem.formatted ? BufferFormat(ctx, mem) : Prospero::BufferFormat::kInvalid);
|
||||
if (info.type != Format::ComponentType::Unknown) {
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, components,
|
||||
FormattedAccess::Load);
|
||||
return EmitValueOrDefaultIfCondition(
|
||||
state, plan.in_bounds, TypeU32Composite(state, components),
|
||||
@@ -928,10 +916,10 @@ void StoreWideBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compo
|
||||
const auto mem = ctx.Memory(inst);
|
||||
const auto resource = PrepareMemoryResourceAccess(state, mem);
|
||||
const auto composite = ctx.Arg(inst, inst.NumArgs() - 2);
|
||||
const auto format =
|
||||
mem.formatted ? BufferFormat(ctx, inst, mem) : Prospero::BufferFormat::kInvalid;
|
||||
if (Format::IsKnownFormat(format)) {
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, components,
|
||||
const auto info = Format::GetFormatInfo(
|
||||
mem.formatted ? BufferFormat(ctx, mem) : Prospero::BufferFormat::kInvalid);
|
||||
if (info.type != Format::ComponentType::Unknown) {
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info, components,
|
||||
FormattedAccess::Store);
|
||||
EmitIfCondition(state, plan.in_bounds, [&]() {
|
||||
for (uint32_t component = 0; component < components; component++) {
|
||||
@@ -967,10 +955,10 @@ uint32_t LoadWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t co
|
||||
ConstantU32(state, component * 4u));
|
||||
const auto raw_index = Binary(state, OpShiftRightLogical, TypeU32(state), address,
|
||||
ConstantU32(state, 2));
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
const auto index = EmitMemoryElementIndex(state, resource, raw_index);
|
||||
values[component] = EmitValueOrZeroIfCondition(
|
||||
state, EmitMemoryElementInBounds(state, access.resource, access.index),
|
||||
[&]() { return LoadWordInBounds(ctx, access.resource, access.index); });
|
||||
state, EmitMemoryElementInBounds(state, resource, index),
|
||||
[&]() { return LoadWordInBounds(ctx, resource, index); });
|
||||
}
|
||||
return ConstructU32Composite(state, components, values);
|
||||
});
|
||||
@@ -988,11 +976,10 @@ void StoreWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compo
|
||||
ConstantU32(state, component * 4u));
|
||||
const auto raw_index =
|
||||
Binary(state, OpShiftRightLogical, TypeU32(state), address, ConstantU32(state, 2));
|
||||
const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index);
|
||||
EmitIfCondition(state, EmitMemoryElementInBounds(state, access.resource, access.index),
|
||||
const auto index = EmitMemoryElementIndex(state, resource, raw_index);
|
||||
EmitIfCondition(state, EmitMemoryElementInBounds(state, resource, index),
|
||||
[&]() {
|
||||
StoreWordInBounds(ctx, access.resource, access.index,
|
||||
ctx.Arg(inst, component + 1u));
|
||||
StoreWordInBounds(ctx, resource, index, ctx.Arg(inst, component + 1u));
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1170,6 +1157,12 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
op == IR::ValueOpcode::BufferAtomicFMax32));
|
||||
return true;
|
||||
}
|
||||
if (op == IR::ValueOpcode::SharedAtomicInc32 || op == IR::ValueOpcode::SharedAtomicDec32) {
|
||||
const auto replacement = op == IR::ValueOpcode::SharedAtomicInc32
|
||||
? AtomicIncrement : AtomicDecrement;
|
||||
ctx.Define(inst, EmitAtomicUpdate(ctx, inst, ctx.Memory(inst), replacement));
|
||||
return true;
|
||||
}
|
||||
if (op == IR::ValueOpcode::SharedAtomicFMin32 || op == IR::ValueOpcode::SharedAtomicFMax32) {
|
||||
SharedFloatAtomic(ctx, inst, ctx.Memory(inst), op == IR::ValueOpcode::SharedAtomicFMax32);
|
||||
return true;
|
||||
|
||||
@@ -188,31 +188,12 @@ uint32_t EmitStorageBufferElementPointer(EmitterState& state,
|
||||
return pointer;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferBitcastF32ToU32(EmitterState& state, uint32_t value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, TypeU32(state), ret, value});
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferBitcastU32ToF32(EmitterState& state, uint32_t value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, TypeF32(state), ret, value});
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferBitcastU32ToI32(EmitterState& state, uint32_t value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpBitcast, TypeI32(state), ret, value});
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferCompareU32Constant(EmitterState& state, uint32_t opcode, uint32_t value,
|
||||
uint32_t constant) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({opcode, TypeBool(state), ret, value, ConstantU32(state, constant)});
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t EmitTBufferSelectF32(EmitterState& state, uint32_t condition, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
@@ -225,15 +206,6 @@ bool IsSignedFormatComponent(Format::ComponentType type) {
|
||||
type == Format::ComponentType::Sscaled;
|
||||
}
|
||||
|
||||
uint32_t EmitHalfToF32Bits(EmitterState& state, uint32_t raw) {
|
||||
const auto unpacked = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpExtInst, TypeF32Vector(state, 2), unpacked, GlslStd450(state), GlslUnpackHalf2x16, raw});
|
||||
state.builder.AddFunction({OpCompositeExtract, TypeF32(state), value, unpacked, 0});
|
||||
return EmitTBufferBitcastF32ToU32(state, value);
|
||||
}
|
||||
|
||||
uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
|
||||
const auto mantissa_bits = bits == 11u ? 6u : 5u;
|
||||
const auto mantissa_mask = (1u << mantissa_bits) - 1u;
|
||||
@@ -250,11 +222,11 @@ uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
|
||||
const auto mantissa_bits_32 =
|
||||
EmitBinaryU32(state, OpShiftLeftLogical, mantissa, ConstantU32(state, 23u - mantissa_bits));
|
||||
const auto normal_bits = EmitBinaryU32(state, OpBitwiseOr, exponent_bits, mantissa_bits_32);
|
||||
const auto normal = EmitTBufferBitcastU32ToF32(state, normal_bits);
|
||||
const auto normal = EmitBitcastU32ToF32(state, normal_bits);
|
||||
|
||||
const auto special_bits =
|
||||
EmitBinaryU32(state, OpBitwiseOr, ConstantU32(state, 0x7f800000u), mantissa_bits_32);
|
||||
const auto special = EmitTBufferBitcastU32ToF32(state, special_bits);
|
||||
const auto special = EmitBitcastU32ToF32(state, special_bits);
|
||||
|
||||
const auto mantissa_f32 = state.builder.AllocateId();
|
||||
const auto subnormal = state.builder.AllocateId();
|
||||
@@ -263,11 +235,11 @@ uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits) {
|
||||
{OpFMul, TypeF32(state), subnormal, mantissa_f32,
|
||||
ConstantF32Value(state, std::ldexp(1.0f, 1 - 15 - static_cast<int>(mantissa_bits)))});
|
||||
|
||||
const auto zero_exp = EmitTBufferCompareU32Constant(state, OpIEqual, exponent, 0);
|
||||
const auto special_exp = EmitTBufferCompareU32Constant(state, OpIEqual, exponent, 31);
|
||||
const auto zero_exp = EmitCompareU32Constant(state, OpIEqual, exponent, 0);
|
||||
const auto special_exp = EmitCompareU32Constant(state, OpIEqual, exponent, 31);
|
||||
const auto finite = EmitTBufferSelectF32(state, zero_exp, subnormal, normal);
|
||||
const auto result = EmitTBufferSelectF32(state, special_exp, special, finite);
|
||||
return EmitTBufferBitcastF32ToU32(state, result);
|
||||
return EmitBitcastF32ToU32(state, result);
|
||||
}
|
||||
|
||||
uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
@@ -279,13 +251,13 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
case Format::ComponentType::Uscaled: {
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpConvertUToF, TypeF32(state), value, raw});
|
||||
return EmitTBufferBitcastF32ToU32(state, value);
|
||||
return EmitBitcastF32ToU32(state, value);
|
||||
}
|
||||
case Format::ComponentType::Sscaled: {
|
||||
const auto signed_raw = EmitTBufferBitcastU32ToI32(state, raw);
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpConvertSToF, TypeF32(state), value, signed_raw});
|
||||
return EmitTBufferBitcastF32ToU32(state, value);
|
||||
return EmitBitcastF32ToU32(state, value);
|
||||
}
|
||||
case Format::ComponentType::Unorm: {
|
||||
const auto value = state.builder.AllocateId();
|
||||
@@ -294,7 +266,7 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
state.builder.AddFunction({OpConvertUToF, TypeF32(state), value, raw});
|
||||
state.builder.AddFunction(
|
||||
{OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, max_value)});
|
||||
return EmitTBufferBitcastF32ToU32(state, normalized);
|
||||
return EmitBitcastF32ToU32(state, normalized);
|
||||
}
|
||||
case Format::ComponentType::Snorm: {
|
||||
const auto signed_raw = EmitTBufferBitcastU32ToI32(state, raw);
|
||||
@@ -307,14 +279,14 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
{OpFDiv, TypeF32(state), normalized, value, ConstantF32Value(state, max_value)});
|
||||
state.builder.AddFunction({OpExtInst, TypeF32(state), clamped, GlslStd450(state),
|
||||
GlslFMax, normalized, ConstantF32Value(state, -1.0f)});
|
||||
return EmitTBufferBitcastF32ToU32(state, clamped);
|
||||
return EmitBitcastF32ToU32(state, clamped);
|
||||
}
|
||||
case Format::ComponentType::Float:
|
||||
if (bits == 32u) {
|
||||
return raw;
|
||||
}
|
||||
if (bits == 16u) {
|
||||
return EmitHalfToF32Bits(state, raw);
|
||||
return EmitBitcastF32ToU32(state, EmitF16BitsToF32(state, raw));
|
||||
}
|
||||
return EmitUFloatToF32Bits(state, raw, bits);
|
||||
default: return raw;
|
||||
@@ -335,8 +307,7 @@ uint32_t EmitFloatAtomicReplacement(EmitterState& state, uint32_t old, uint32_t
|
||||
uint32_t key;
|
||||
};
|
||||
const auto classify = [&](uint32_t bits) {
|
||||
const auto value = EmitBitcastU32ToF32(state, bits);
|
||||
const auto cls = EmitClassifyF32(state, value);
|
||||
const auto cls = EmitClassifyF32Bits(state, bits);
|
||||
const auto negative = EmitCompareU32Constant(
|
||||
state, OpINotEqual, EmitAndConstant(state, bits, 0x80000000u), 0u);
|
||||
const auto negative_key = state.builder.AllocateId();
|
||||
|
||||
@@ -349,45 +349,6 @@ uint32_t VertexParameterScalarType(EmitterState& state, VertexInputScalarKind ki
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VertexParameterScalarPointerType(EmitterState& state, VertexInputScalarKind kind) {
|
||||
switch (kind) {
|
||||
case VertexInputScalarKind::Sint:
|
||||
return TypePointer(state, StorageClassInput, TypeI32(state));
|
||||
case VertexInputScalarKind::Uint:
|
||||
return TypePointer(state, StorageClassInput, TypeU32(state));
|
||||
case VertexInputScalarKind::Float:
|
||||
default: return TypePointer(state, StorageClassInput, TypeF32(state));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VertexParameterVectorOrScalarType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components) {
|
||||
switch (kind) {
|
||||
case VertexInputScalarKind::Sint:
|
||||
switch (components) {
|
||||
case 1: return TypeI32(state);
|
||||
case 2: return TypeI32Vector(state, 2);
|
||||
case 3: return TypeI32Vector(state, 3);
|
||||
default: return TypeI32Vector(state, 4);
|
||||
}
|
||||
case VertexInputScalarKind::Uint:
|
||||
switch (components) {
|
||||
case 1: return TypeU32(state);
|
||||
case 2: return TypeU32Vector(state, 2);
|
||||
case 3: return TypeU32Vector(state, 3);
|
||||
default: return TypeU32Vector(state, 4);
|
||||
}
|
||||
case VertexInputScalarKind::Float:
|
||||
default:
|
||||
switch (components) {
|
||||
case 1: return TypeF32(state);
|
||||
case 2: return TypeF32Vector(state, 2);
|
||||
case 3: return TypeF32Vector(state, 3);
|
||||
default: return TypeF32Vector(state, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components) {
|
||||
switch (kind) {
|
||||
|
||||
@@ -96,7 +96,9 @@ void EmitReturn(ValueEmitContext& ctx) {
|
||||
}
|
||||
|
||||
uint32_t BranchCondition(ValueEmitContext& ctx, const IR::BlockInfo& info) {
|
||||
// Scalar-instruction conditions already test the full wave's raw register values.
|
||||
if (ctx.other_half == nullptr ||
|
||||
info.terminator.condition == CFG::BranchCondition::ScalarInstruction ||
|
||||
info.terminator.condition == CFG::BranchCondition::GotoVariable) {
|
||||
return ctx.Def(info.condition);
|
||||
}
|
||||
@@ -118,18 +120,19 @@ uint32_t BranchCondition(ValueEmitContext& ctx, const IR::BlockInfo& info) {
|
||||
|
||||
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
const IR::BlockInfo& info) {
|
||||
const auto& program = ctx.state.program;
|
||||
const auto& term = info.terminator;
|
||||
const auto emit_merge = [&]() {
|
||||
if (term.loop_header) {
|
||||
const auto* merge = TargetBlock(ctx.program, term.merge_block);
|
||||
const auto* cont = TargetBlock(ctx.program, term.continue_block);
|
||||
const auto* merge = TargetBlock(program, term.merge_block);
|
||||
const auto* cont = TargetBlock(program, term.continue_block);
|
||||
if (merge != nullptr && cont != nullptr) {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpLoopMerge, ctx.Label(merge), ctx.Label(cont), LoopControlNone});
|
||||
}
|
||||
} else if (term.kind == CFG::TerminatorKind::ConditionalBranch &&
|
||||
term.merge_block != UINT32_MAX) {
|
||||
if (const auto* merge = TargetBlock(ctx.program, term.merge_block); merge != nullptr) {
|
||||
if (const auto* merge = TargetBlock(program, term.merge_block); merge != nullptr) {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpSelectionMerge, ctx.Label(merge), SelectionControlNone});
|
||||
}
|
||||
@@ -138,7 +141,7 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
|
||||
switch (term.kind) {
|
||||
case CFG::TerminatorKind::Branch: {
|
||||
const auto* target = TargetBlock(ctx.program, term.true_block);
|
||||
const auto* target = TargetBlock(program, term.true_block);
|
||||
if (target == nullptr) {
|
||||
EmitReturn(ctx);
|
||||
return;
|
||||
@@ -148,8 +151,8 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
return;
|
||||
}
|
||||
case CFG::TerminatorKind::ConditionalBranch: {
|
||||
const auto* true_block = TargetBlock(ctx.program, term.true_block);
|
||||
const auto* false_block = TargetBlock(ctx.program, term.false_block);
|
||||
const auto* true_block = TargetBlock(program, term.true_block);
|
||||
const auto* false_block = TargetBlock(program, term.false_block);
|
||||
if (true_block == nullptr || false_block == nullptr || info.condition.IsEmpty()) {
|
||||
EmitReturn(ctx);
|
||||
return;
|
||||
@@ -166,7 +169,7 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
|
||||
void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
|
||||
const IR::Block* from, uint32_t target) {
|
||||
const auto* block = TargetBlock(ctx.program, target);
|
||||
const auto* block = TargetBlock(ctx.state.program, target);
|
||||
if (block != nullptr) {
|
||||
StoreDispatcherPhiEdge(ctx, dispatcher, from, block);
|
||||
if (ctx.other_half != nullptr) {
|
||||
@@ -239,7 +242,7 @@ void EmitStructuredInstruction(ValueEmitContext& ctx, StructuredFunctionState& s
|
||||
}
|
||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||
const auto* predecessor = inst.PhiBlock(index);
|
||||
if (predecessor == nullptr || !ctx.labels.contains(predecessor)) {
|
||||
if (predecessor == nullptr || !ctx.state.labels.contains(predecessor)) {
|
||||
ctx.Fail(inst, "has a predecessor outside the structured function");
|
||||
}
|
||||
}
|
||||
@@ -272,10 +275,7 @@ void EmitDispatcherInstruction(ValueEmitContext& ctx, const DispatcherFunctionSt
|
||||
|
||||
template <typename EmitInstruction>
|
||||
void EmitBlock(ValueEmitContext& ctx, const IR::Block* block, EmitInstruction&& emit_instruction) {
|
||||
ctx.current_block = block;
|
||||
if (ctx.other_half != nullptr) {
|
||||
ctx.other_half->current_block = block;
|
||||
}
|
||||
ctx.state.current_block = block;
|
||||
EmitLabel(ctx.state, ctx.Label(block));
|
||||
bool emitted_non_phi = false;
|
||||
for (const auto& inst: *block) {
|
||||
@@ -288,7 +288,6 @@ void EmitBlock(ValueEmitContext& ctx, const IR::Block* block, EmitInstruction&&
|
||||
}
|
||||
for (uint32_t half = 0; half < ctx.state.lane_count; half++) {
|
||||
auto& lane = half == 0 ? ctx : *ctx.other_half;
|
||||
lane.current_block = block;
|
||||
ctx.state.lane_half = half;
|
||||
if (half == 0 || (inst.GetOpcode() != IR::ValueOpcode::Barrier &&
|
||||
inst.GetOpcode() != IR::ValueOpcode::MeshAllocate)) {
|
||||
@@ -315,28 +314,29 @@ void PatchStructuredPhis(ValueEmitContext& ctx, StructuredFunctionState& structu
|
||||
}
|
||||
|
||||
void EmitStructuredFunction(ValueEmitContext& ctx) {
|
||||
const auto& program = ctx.state.program;
|
||||
StructuredFunctionState structured;
|
||||
ctx.state.builder.AddFunction({OpBranch, ctx.Label(ctx.program.blocks.front())});
|
||||
for (size_t index = 0; index < ctx.program.blocks.size(); index++) {
|
||||
const auto* block = ctx.program.blocks[index];
|
||||
ctx.state.builder.AddFunction({OpBranch, ctx.Label(program.blocks.front())});
|
||||
for (size_t index = 0; index < program.blocks.size(); index++) {
|
||||
const auto* block = program.blocks[index];
|
||||
EmitBlock(ctx, block, [&](ValueEmitContext& lane, const IR::Inst& inst) {
|
||||
EmitStructuredInstruction(lane, structured, inst);
|
||||
});
|
||||
structured.block_exit_labels.emplace(block, ctx.state.current_label);
|
||||
EmitStructuredTerminator(ctx, block, ctx.program.block_info[index]);
|
||||
EmitStructuredTerminator(ctx, block, program.block_info[index]);
|
||||
}
|
||||
PatchStructuredPhis(ctx, structured);
|
||||
}
|
||||
|
||||
void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher) {
|
||||
auto& state = ctx.state;
|
||||
const auto* entry = ctx.program.blocks.front();
|
||||
const auto* entry = state.program.blocks.front();
|
||||
state.builder.AddFunction({OpBranch, ctx.Label(entry)});
|
||||
EmitBlock(ctx, entry, [&](ValueEmitContext& lane, const IR::Inst& inst) {
|
||||
EmitDispatcherInstruction(lane, dispatcher, inst);
|
||||
});
|
||||
const auto initial_pc =
|
||||
EmitDispatcherNextPc(ctx, dispatcher, entry, ctx.program.block_info.front());
|
||||
EmitDispatcherNextPc(ctx, dispatcher, entry, state.program.block_info.front());
|
||||
const auto initial_parent = state.current_label;
|
||||
state.builder.AddFunction({OpBranch, dispatcher.header_label});
|
||||
|
||||
@@ -357,21 +357,21 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
|
||||
state.builder.AddFunction(
|
||||
{OpSelectionMerge, dispatcher.after_switch_label, SelectionControlNone});
|
||||
std::vector<uint32_t> words {OpSwitch, pc, dispatcher.after_switch_label};
|
||||
for (size_t index = 1; index < ctx.program.blocks.size(); index++) {
|
||||
words.push_back(ctx.program.block_info[index].id);
|
||||
words.push_back(ctx.Label(ctx.program.blocks[index]));
|
||||
for (size_t index = 1; index < state.program.blocks.size(); index++) {
|
||||
words.push_back(state.program.block_info[index].id);
|
||||
words.push_back(ctx.Label(state.program.blocks[index]));
|
||||
}
|
||||
state.builder.AddFunction(words);
|
||||
std::vector<uint32_t> next_pc_words {OpPhi, TypeU32(state), next_pc,
|
||||
ConstantU32(state, UINT32_MAX), dispatcher.select_label};
|
||||
|
||||
for (size_t index = 1; index < ctx.program.blocks.size(); index++) {
|
||||
EmitBlock(ctx, ctx.program.blocks[index],
|
||||
for (size_t index = 1; index < state.program.blocks.size(); index++) {
|
||||
EmitBlock(ctx, state.program.blocks[index],
|
||||
[&](ValueEmitContext& lane, const IR::Inst& inst) {
|
||||
EmitDispatcherInstruction(lane, dispatcher, inst);
|
||||
});
|
||||
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, ctx.program.blocks[index],
|
||||
ctx.program.block_info[index]);
|
||||
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, state.program.blocks[index],
|
||||
state.program.block_info[index]);
|
||||
next_pc_words.push_back(selected);
|
||||
next_pc_words.push_back(state.current_label);
|
||||
state.builder.AddFunction({OpBranch, dispatcher.after_switch_label});
|
||||
@@ -423,8 +423,8 @@ uint32_t ValueEmitContext::Def(IR::Value value) {
|
||||
if (inst == nullptr) {
|
||||
Fail("direct SPIR-V emitter received a non-value argument");
|
||||
}
|
||||
if (dispatcher_spills != nullptr && current_block != nullptr &&
|
||||
inst->Parent() != current_block) {
|
||||
if (dispatcher_spills != nullptr && state.current_block != nullptr &&
|
||||
inst->Parent() != state.current_block) {
|
||||
if (const auto found = dispatcher_spills->find(inst); found != dispatcher_spills->end()) {
|
||||
if (const auto loaded = dispatcher_block_loads.find(inst);
|
||||
loaded != dispatcher_block_loads.end() &&
|
||||
@@ -437,12 +437,7 @@ uint32_t ValueEmitContext::Def(IR::Value value) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
if (const auto found = definitions.find(inst); found != definitions.end()) {
|
||||
return found->second;
|
||||
}
|
||||
const auto id = state.builder.AllocateId();
|
||||
definitions.emplace(inst, id);
|
||||
return id;
|
||||
return Result(*inst);
|
||||
}
|
||||
|
||||
uint32_t ValueEmitContext::Arg(const IR::Inst& inst, size_t index) {
|
||||
@@ -537,10 +532,12 @@ uint32_t ValueEmitContext::Result(const IR::Inst& inst) {
|
||||
|
||||
uint32_t ValueEmitContext::Emit(const IR::Inst& inst, uint32_t opcode, IR::Type type,
|
||||
std::initializer_list<uint32_t> args) {
|
||||
std::vector<uint32_t> words {opcode, TypeId(type), Result(inst)};
|
||||
const auto type_id = TypeId(type);
|
||||
const auto result = Result(inst);
|
||||
std::vector<uint32_t> words {opcode, type_id, result};
|
||||
words.insert(words.end(), args.begin(), args.end());
|
||||
state.builder.AddFunction(words);
|
||||
return Result(inst);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ValueEmitContext::Define(const IR::Inst& inst, uint32_t value) {
|
||||
@@ -571,33 +568,34 @@ const IR::Inst* ValueEmitContext::ImageAddress(IR::Value value) {
|
||||
}
|
||||
|
||||
const IR::MemoryInfo& ValueEmitContext::Memory(const IR::Inst& inst) const {
|
||||
return program.memory_info.at(inst.Flags<IR::MemoryFlags>().index);
|
||||
return state.program.memory_info.at(inst.Flags<IR::MemoryFlags>().index);
|
||||
}
|
||||
|
||||
const IR::ExportInfo& ValueEmitContext::Export(const IR::Inst& inst) const {
|
||||
return program.export_info.at(inst.Flags<IR::ExportFlags>().index);
|
||||
return state.program.export_info.at(inst.Flags<IR::ExportFlags>().index);
|
||||
}
|
||||
|
||||
uint32_t ValueEmitContext::Label(const IR::Block* block) const {
|
||||
return labels.at(block);
|
||||
return state.labels.at(block);
|
||||
}
|
||||
|
||||
[[noreturn]] void ValueEmitContext::Fail(const char* reason) const {
|
||||
EXIT("SPIR-V emission failed: hash=0x%016" PRIx64 " stage=%u reason=%s\n",
|
||||
program.shader_hash, static_cast<unsigned>(program.stage), reason);
|
||||
state.program.shader_hash, static_cast<unsigned>(state.program.stage), reason);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
[[noreturn]] void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) const {
|
||||
EXIT("SPIR-V emission failed: hash=0x%016" PRIx64 " stage=%u opcode=%s reason=%s\n",
|
||||
program.shader_hash, static_cast<unsigned>(program.stage),
|
||||
state.program.shader_hash, static_cast<unsigned>(state.program.stage),
|
||||
IR::ValueOpcodeName(inst.GetOpcode()), reason);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void EmitProgram(EmitterState& state, const IR::Program& program) {
|
||||
ValueEmitContext ctx(state, program);
|
||||
ValueEmitContext high(state, program);
|
||||
void EmitProgram(EmitterState& state) {
|
||||
const auto& program = state.program;
|
||||
ValueEmitContext ctx(state);
|
||||
ValueEmitContext high(state);
|
||||
if (state.lane_count == 2) {
|
||||
ctx.other_half = &high;
|
||||
high.other_half = &ctx;
|
||||
@@ -610,8 +608,7 @@ void EmitProgram(EmitterState& state, const IR::Program& program) {
|
||||
}
|
||||
for (const auto* block: program.blocks) {
|
||||
const auto label = state.builder.AllocateId();
|
||||
ctx.labels.emplace(block, label);
|
||||
high.labels.emplace(block, label);
|
||||
state.labels.emplace(block, label);
|
||||
}
|
||||
if (state.program.dispatcher_fallback) {
|
||||
auto& dispatch = dispatcher.emplace();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <stack>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::CFG {
|
||||
@@ -51,26 +52,6 @@ uint32_t ProgramEndPc(const Decoder::Program& program) {
|
||||
return InstructionEndPc(program.instructions.back());
|
||||
}
|
||||
|
||||
bool IsUnconditionalBranch(Opcode opcode) {
|
||||
return opcode == Opcode::S_BRANCH;
|
||||
}
|
||||
|
||||
bool IsConditionalBranch(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::S_CBRANCH_SCC0:
|
||||
case Opcode::S_CBRANCH_SCC1:
|
||||
case Opcode::S_CBRANCH_VCCZ:
|
||||
case Opcode::S_CBRANCH_VCCNZ:
|
||||
case Opcode::S_CBRANCH_EXECZ:
|
||||
case Opcode::S_CBRANCH_EXECNZ: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsBranch(Opcode opcode) {
|
||||
return IsUnconditionalBranch(opcode) || IsConditionalBranch(opcode);
|
||||
}
|
||||
|
||||
BranchCondition ConditionForOpcode(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::S_BRANCH: return BranchCondition::Always;
|
||||
@@ -80,6 +61,8 @@ BranchCondition ConditionForOpcode(Opcode opcode) {
|
||||
case Opcode::S_CBRANCH_VCCNZ: return BranchCondition::VccNonZero;
|
||||
case Opcode::S_CBRANCH_EXECZ: return BranchCondition::ExecZero;
|
||||
case Opcode::S_CBRANCH_EXECNZ: return BranchCondition::ExecNonZero;
|
||||
case Opcode::S_SUBVECTOR_LOOP_BEGIN:
|
||||
case Opcode::S_SUBVECTOR_LOOP_END: return BranchCondition::ScalarInstruction;
|
||||
default: return BranchCondition::Unknown;
|
||||
}
|
||||
}
|
||||
@@ -323,11 +306,10 @@ bool ResolveJumpTableEntryCount(const Decoder::Program& program, uint32_t before
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AddUniqueTargetPc(std::vector<uint32_t>& targets, uint32_t target) {
|
||||
if (std::find(targets.begin(), targets.end(), target) == targets.end()) {
|
||||
targets.push_back(target);
|
||||
void AddUnique(std::vector<uint32_t>& values, uint32_t value) {
|
||||
if (std::find(values.begin(), values.end(), value) == values.end()) {
|
||||
values.push_back(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScalarCodeWrittenInRange(const Decoder::Program& program, uint32_t begin_index,
|
||||
@@ -421,7 +403,7 @@ bool ResolveSetpcJumpTable(const Decoder::Program& program, uint32_t setpc_index
|
||||
return false;
|
||||
}
|
||||
const auto target_pc = (target_base + low) & ~3u;
|
||||
AddUniqueTargetPc(targets, target_pc);
|
||||
AddUnique(targets, target_pc);
|
||||
selector_values.push_back(i * 8u);
|
||||
selector_target_pcs.push_back(target_pc);
|
||||
}
|
||||
@@ -507,7 +489,7 @@ bool ResolveSetpcDwordJumpTable(const Decoder::Program& program, uint32_t setpc_
|
||||
return false;
|
||||
}
|
||||
const auto target_pc = (target_base - offset) & ~3u;
|
||||
AddUniqueTargetPc(targets, target_pc);
|
||||
AddUnique(targets, target_pc);
|
||||
selector_values.push_back(i * 4u);
|
||||
selector_target_pcs.push_back(target_pc);
|
||||
}
|
||||
@@ -590,12 +572,6 @@ bool IsValidTarget(uint32_t target, const std::set<uint32_t>& instruction_pcs, u
|
||||
return target == end_pc || (target >= first_pc && instruction_pcs.contains(target));
|
||||
}
|
||||
|
||||
void AddUnique(std::vector<uint32_t>& values, uint32_t value) {
|
||||
if (std::find(values.begin(), values.end(), value) == values.end()) {
|
||||
values.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> AllBlockIds(uint32_t count) {
|
||||
std::vector<uint32_t> ids;
|
||||
ids.reserve(count);
|
||||
@@ -868,15 +844,15 @@ void ComputeNaturalLoops(Graph& graph) {
|
||||
loop.header = edge.to;
|
||||
loop.latch = edge.from;
|
||||
loop.continue_block = edge.from;
|
||||
loop.body_blocks = body;
|
||||
loop.body_blocks = std::move(body);
|
||||
|
||||
for (auto block_id: body) {
|
||||
for (auto block_id: loop.body_blocks) {
|
||||
const auto* block = graph.FindBlock(block_id);
|
||||
if (block == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (auto succ: block->successors) {
|
||||
if (!Contains(body, succ)) {
|
||||
if (!Contains(loop.body_blocks, succ)) {
|
||||
AddUnique(loop.exit_blocks, succ);
|
||||
}
|
||||
}
|
||||
@@ -995,6 +971,29 @@ void RecomputeAnalyses(Graph& graph) {
|
||||
ComputeComponents(graph);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ApplyBlockOrder(Graph& graph, std::vector<BasicBlock> blocks) {
|
||||
std::vector<uint32_t> id_map(blocks.size(), UINT32_MAX);
|
||||
for (uint32_t i = 0; i < blocks.size(); i++) {
|
||||
id_map[blocks[i].id] = i;
|
||||
}
|
||||
|
||||
graph.blocks = std::move(blocks);
|
||||
graph.entry_block = RemapId(graph.entry_block, id_map);
|
||||
for (auto& block: graph.blocks) {
|
||||
block.id = RemapId(block.id, id_map);
|
||||
RemapIds(block.predecessors, id_map);
|
||||
RemapIds(block.successors, id_map);
|
||||
RemapIds(block.dominators, id_map);
|
||||
RemapIds(block.post_dominators, id_map);
|
||||
block.terminator.true_block = RemapId(block.terminator.true_block, id_map);
|
||||
block.terminator.false_block = RemapId(block.terminator.false_block, id_map);
|
||||
block.terminator.merge_block = RemapId(block.terminator.merge_block, id_map);
|
||||
block.terminator.continue_block = RemapId(block.terminator.continue_block, id_map);
|
||||
}
|
||||
|
||||
return id_map;
|
||||
}
|
||||
|
||||
uint32_t MoveBlockBefore(Graph& graph, uint32_t block_id, uint32_t before_id) {
|
||||
if (block_id == before_id || block_id >= graph.blocks.size() ||
|
||||
before_id >= graph.blocks.size()) {
|
||||
@@ -1016,24 +1015,7 @@ uint32_t MoveBlockBefore(Graph& graph, uint32_t block_id, uint32_t before_id) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> id_map(new_blocks.size(), UINT32_MAX);
|
||||
for (uint32_t i = 0; i < new_blocks.size(); i++) {
|
||||
id_map[new_blocks[i].id] = i;
|
||||
}
|
||||
|
||||
graph.blocks = std::move(new_blocks);
|
||||
graph.entry_block = RemapId(graph.entry_block, id_map);
|
||||
for (auto& block: graph.blocks) {
|
||||
block.id = RemapId(block.id, id_map);
|
||||
RemapIds(block.predecessors, id_map);
|
||||
RemapIds(block.successors, id_map);
|
||||
RemapIds(block.dominators, id_map);
|
||||
RemapIds(block.post_dominators, id_map);
|
||||
block.terminator.true_block = RemapId(block.terminator.true_block, id_map);
|
||||
block.terminator.false_block = RemapId(block.terminator.false_block, id_map);
|
||||
block.terminator.merge_block = RemapId(block.terminator.merge_block, id_map);
|
||||
block.terminator.continue_block = RemapId(block.terminator.continue_block, id_map);
|
||||
}
|
||||
const auto id_map = ApplyBlockOrder(graph, std::move(new_blocks));
|
||||
|
||||
return RemapId(block_id, id_map);
|
||||
}
|
||||
@@ -1396,16 +1378,16 @@ bool SplitSharedMergeBlock(Graph& graph, uint32_t merge,
|
||||
}
|
||||
|
||||
std::vector<uint32_t> construct_predecessors;
|
||||
std::vector<uint32_t> external_predecessors;
|
||||
bool has_external_predecessor = false;
|
||||
for (auto pred: merge_block->predecessors) {
|
||||
if (Contains(construct_blocks, pred)) {
|
||||
AddUnique(construct_predecessors, pred);
|
||||
} else {
|
||||
AddUnique(external_predecessors, pred);
|
||||
has_external_predecessor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (construct_predecessors.empty() || (!force_split && external_predecessors.empty())) {
|
||||
if (construct_predecessors.empty() || (!force_split && !has_external_predecessor)) {
|
||||
return false;
|
||||
}
|
||||
const auto synthetic_merge = AppendSyntheticBranchBlock(graph, merge);
|
||||
@@ -1656,24 +1638,7 @@ void PlaceGotoRouteBlocks(Graph& graph, uint32_t original_block_count,
|
||||
new_blocks.push_back(std::move(old_blocks[block_id]));
|
||||
}
|
||||
|
||||
std::vector<uint32_t> id_map(new_blocks.size(), UINT32_MAX);
|
||||
for (uint32_t block_id = 0; block_id < new_blocks.size(); block_id++) {
|
||||
id_map[new_blocks[block_id].id] = block_id;
|
||||
}
|
||||
|
||||
graph.blocks = std::move(new_blocks);
|
||||
graph.entry_block = RemapId(graph.entry_block, id_map);
|
||||
for (auto& block: graph.blocks) {
|
||||
block.id = RemapId(block.id, id_map);
|
||||
RemapIds(block.predecessors, id_map);
|
||||
RemapIds(block.successors, id_map);
|
||||
RemapIds(block.dominators, id_map);
|
||||
RemapIds(block.post_dominators, id_map);
|
||||
block.terminator.true_block = RemapId(block.terminator.true_block, id_map);
|
||||
block.terminator.false_block = RemapId(block.terminator.false_block, id_map);
|
||||
block.terminator.merge_block = RemapId(block.terminator.merge_block, id_map);
|
||||
block.terminator.continue_block = RemapId(block.terminator.continue_block, id_map);
|
||||
}
|
||||
ApplyBlockOrder(graph, std::move(new_blocks));
|
||||
}
|
||||
|
||||
uint32_t AppendGotoSelectBlock(Graph& graph, uint32_t route_variable, uint32_t true_target,
|
||||
@@ -1767,7 +1732,7 @@ bool RouteOneSharedArm(Graph& graph, uint32_t original_block_count, uint32_t out
|
||||
inner_is_true ? outer->terminator.false_block : outer->terminator.true_block;
|
||||
const auto* inner = graph.FindBlock(inner_id);
|
||||
if (inner == nullptr || inner->inst_begin == inner->inst_end ||
|
||||
inner->predecessors != std::vector<uint32_t> {outer_id} ||
|
||||
inner->predecessors.size() != 1u || inner->predecessors.front() != outer_id ||
|
||||
inner->terminator.kind != TerminatorKind::ConditionalBranch ||
|
||||
IsInnermostLoopControlConditional(graph, *inner)) {
|
||||
continue;
|
||||
@@ -1942,7 +1907,7 @@ Graph BuildGraph(const Decoder::Program& program) {
|
||||
for (uint32_t i = 0; i < program.instructions.size(); i++) {
|
||||
const auto& inst = program.instructions[i];
|
||||
const auto next_pc = InstructionEndPc(inst);
|
||||
if (IsBranch(inst.opcode)) {
|
||||
if (Decoder::IsDirectBranch(inst.opcode)) {
|
||||
if (!IsValidTarget(inst.branch_target, instruction_pcs, first_pc, end_pc)) {
|
||||
ExitBuildFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("branch at pc 0x{:08x} targets invalid pc 0x{:08x}",
|
||||
@@ -1959,9 +1924,9 @@ Graph BuildGraph(const Decoder::Program& program) {
|
||||
graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("unsupported dynamic S_SETPC_B64 at pc 0x{:08x}", inst.pc));
|
||||
}
|
||||
const auto& target_pcs = target_info.indirect
|
||||
? target_info.target_pcs
|
||||
: std::vector<uint32_t> {target_info.target};
|
||||
const auto target_pcs = target_info.indirect
|
||||
? std::span<const uint32_t>(target_info.target_pcs)
|
||||
: std::span<const uint32_t>(&target_info.target, 1);
|
||||
for (const auto target: target_pcs) {
|
||||
if (!IsValidTarget(target, instruction_pcs, first_pc, end_pc)) {
|
||||
ExitBuildFailure(
|
||||
@@ -1981,7 +1946,6 @@ Graph BuildGraph(const Decoder::Program& program) {
|
||||
}
|
||||
|
||||
std::vector<uint32_t> sorted_labels(labels.begin(), labels.end());
|
||||
std::sort(sorted_labels.begin(), sorted_labels.end());
|
||||
for (uint32_t i = 0; i < sorted_labels.size(); i++) {
|
||||
const auto start = sorted_labels[i];
|
||||
if (start > end_pc) {
|
||||
@@ -2050,11 +2014,11 @@ Graph BuildGraph(const Decoder::Program& program) {
|
||||
block.terminator.condition = BranchCondition::Always;
|
||||
block.terminator.true_block = pc_to_block.at(target_info.target);
|
||||
}
|
||||
} else if (IsUnconditionalBranch(last.opcode)) {
|
||||
} else if (last.opcode == Opcode::S_BRANCH) {
|
||||
block.terminator.kind = TerminatorKind::Branch;
|
||||
block.terminator.condition = BranchCondition::Always;
|
||||
block.terminator.true_block = pc_to_block.at(last.branch_target);
|
||||
} else if (IsConditionalBranch(last.opcode)) {
|
||||
} else if (Decoder::IsConditionalBranch(last.opcode)) {
|
||||
block.terminator.kind = TerminatorKind::ConditionalBranch;
|
||||
block.terminator.condition = ConditionForOpcode(last.opcode);
|
||||
block.terminator.true_block = pc_to_block.at(last.branch_target);
|
||||
@@ -2122,11 +2086,7 @@ Graph BuildGraph(const Decoder::Program& program) {
|
||||
}
|
||||
SortUnique(graph.code_table_load_pcs);
|
||||
|
||||
ComputeDominators(graph);
|
||||
ComputePostDominators(graph);
|
||||
ComputeBackEdges(graph);
|
||||
ComputeNaturalLoops(graph);
|
||||
ComputeComponents(graph);
|
||||
RecomputeAnalyses(graph);
|
||||
|
||||
if (indirect_setpc) {
|
||||
graph.irreducible = true;
|
||||
@@ -2266,6 +2226,7 @@ std::string BranchConditionToString(BranchCondition condition) {
|
||||
case BranchCondition::VccNonZero: return "vccnz";
|
||||
case BranchCondition::ExecZero: return "execz";
|
||||
case BranchCondition::ExecNonZero: return "execnz";
|
||||
case BranchCondition::ScalarInstruction: return "scalar_instruction";
|
||||
case BranchCondition::GotoVariable: return "goto_variable";
|
||||
default: return "unknown";
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ enum class BranchCondition {
|
||||
VccNonZero,
|
||||
ExecZero,
|
||||
ExecNonZero,
|
||||
ScalarInstruction,
|
||||
GotoVariable,
|
||||
Unknown
|
||||
};
|
||||
|
||||
@@ -10,7 +10,6 @@ void DecodeExp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
const uint32_t en = word0 & 0xfu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.family = Family::EXP;
|
||||
inst.opcode_id = target;
|
||||
|
||||
@@ -8,24 +8,15 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
|
||||
struct MimgSampleInfo {
|
||||
uint32_t encoding = 0;
|
||||
const char* name = nullptr;
|
||||
uint32_t flags = 0;
|
||||
uint32_t address_components = 3;
|
||||
uint32_t encoding = 0;
|
||||
const char* name = nullptr;
|
||||
uint32_t flags = 0;
|
||||
};
|
||||
|
||||
struct MimgGatherInfo {
|
||||
uint32_t encoding = 0;
|
||||
const char* name = nullptr;
|
||||
Opcode decoded = Opcode::UNSUPPORTED;
|
||||
uint32_t flags = 0;
|
||||
uint32_t address_components = 3;
|
||||
};
|
||||
|
||||
struct MimgAtomicInfo {
|
||||
uint32_t encoding = 0;
|
||||
const char* name = nullptr;
|
||||
Opcode decoded = Opcode::UNSUPPORTED;
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::UNSUPPORTED;
|
||||
uint32_t flags = 0;
|
||||
};
|
||||
|
||||
constexpr ImageDimension DecodeImageDimension(uint32_t dim) {
|
||||
@@ -84,131 +75,127 @@ constexpr uint32_t ImageSampleAddressComponents(uint32_t flags, ImageDimension d
|
||||
return components;
|
||||
}
|
||||
|
||||
constexpr MimgSampleInfo SampleInfo(uint32_t opcode, const char* name, uint32_t flags) {
|
||||
return {opcode, name, flags, ImageSampleAddressComponents(flags, ImageDimension::Dim2D)};
|
||||
}
|
||||
|
||||
constexpr MimgSampleInfo MIMG_SAMPLE_OPCODE_LIST[] = {
|
||||
SampleInfo(0x20u, "image_sample", 0),
|
||||
SampleInfo(0x21u, "image_sample_cl", ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x22u, "image_sample_d", ImageSampleFlagDerivative),
|
||||
SampleInfo(0x23u, "image_sample_d_cl", ImageSampleFlagDerivative | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x24u, "image_sample_l", ImageSampleFlagLod),
|
||||
SampleInfo(0x25u, "image_sample_b", ImageSampleFlagBias),
|
||||
SampleInfo(0x26u, "image_sample_b_cl", ImageSampleFlagBias | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x27u, "image_sample_lz", ImageSampleFlagLevelZero),
|
||||
SampleInfo(0x28u, "image_sample_c", ImageSampleFlagCompare),
|
||||
SampleInfo(0x29u, "image_sample_c_cl", ImageSampleFlagCompare | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x2au, "image_sample_c_d", ImageSampleFlagCompare | ImageSampleFlagDerivative),
|
||||
SampleInfo(0x2bu, "image_sample_c_d_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x2cu, "image_sample_c_l", ImageSampleFlagCompare | ImageSampleFlagLod),
|
||||
SampleInfo(0x2du, "image_sample_c_b", ImageSampleFlagCompare | ImageSampleFlagBias),
|
||||
SampleInfo(0x2eu, "image_sample_c_b_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x2fu, "image_sample_c_lz", ImageSampleFlagCompare | ImageSampleFlagLevelZero),
|
||||
SampleInfo(0x30u, "image_sample_o", ImageSampleFlagOffset),
|
||||
SampleInfo(0x31u, "image_sample_cl_o", ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0x32u, "image_sample_d_o", ImageSampleFlagDerivative | ImageSampleFlagOffset),
|
||||
SampleInfo(0x33u, "image_sample_d_cl_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0x34u, "image_sample_l_o", ImageSampleFlagLod | ImageSampleFlagOffset),
|
||||
SampleInfo(0x35u, "image_sample_b_o", ImageSampleFlagBias | ImageSampleFlagOffset),
|
||||
SampleInfo(0x36u, "image_sample_b_cl_o",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0x37u, "image_sample_lz_o", ImageSampleFlagLevelZero | ImageSampleFlagOffset),
|
||||
SampleInfo(0x38u, "image_sample_c_o", ImageSampleFlagCompare | ImageSampleFlagOffset),
|
||||
SampleInfo(0x39u, "image_sample_c_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0x3au, "image_sample_c_d_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagOffset),
|
||||
SampleInfo(0x3bu, "image_sample_c_d_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset),
|
||||
SampleInfo(0x3cu, "image_sample_c_l_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLod | ImageSampleFlagOffset),
|
||||
SampleInfo(0x3du, "image_sample_c_b_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset),
|
||||
SampleInfo(0x3eu, "image_sample_c_b_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset),
|
||||
SampleInfo(0x3fu, "image_sample_c_lz_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset),
|
||||
SampleInfo(0x68u, "image_sample_cd", ImageSampleFlagDerivative | ImageSampleFlagCd),
|
||||
SampleInfo(0x69u, "image_sample_cd_cl",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x6au, "image_sample_c_cd",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd),
|
||||
SampleInfo(0x6bu, "image_sample_c_cd_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagLodClamp),
|
||||
SampleInfo(0x6cu, "image_sample_cd_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagOffset),
|
||||
SampleInfo(0x6du, "image_sample_cd_cl_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset),
|
||||
SampleInfo(0x6eu, "image_sample_c_cd_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagOffset),
|
||||
SampleInfo(0x6fu, "image_sample_c_cd_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagLodClamp | ImageSampleFlagOffset),
|
||||
SampleInfo(0xa0u, "image_sample_a", ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa1u, "image_sample_cl_a", ImageSampleFlagLodClamp | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa5u, "image_sample_b_a", ImageSampleFlagBias | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa6u, "image_sample_b_cl_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa8u, "image_sample_c_a", ImageSampleFlagCompare | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xa9u, "image_sample_c_cl_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xadu, "image_sample_c_b_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xaeu, "image_sample_c_b_cl_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb0u, "image_sample_o_a", ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb1u, "image_sample_cl_o_a",
|
||||
ImageSampleFlagLodClamp | ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb5u, "image_sample_b_o_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb6u, "image_sample_b_cl_o_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb8u, "image_sample_c_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
SampleInfo(0xb9u, "image_sample_c_cl_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust),
|
||||
SampleInfo(0xbdu, "image_sample_c_b_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust),
|
||||
SampleInfo(0xbeu, "image_sample_c_b_cl_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset | ImageSampleFlagAdjust),
|
||||
{0x20u, "image_sample", 0},
|
||||
{0x21u, "image_sample_cl", ImageSampleFlagLodClamp},
|
||||
{0x22u, "image_sample_d", ImageSampleFlagDerivative},
|
||||
{0x23u, "image_sample_d_cl", ImageSampleFlagDerivative | ImageSampleFlagLodClamp},
|
||||
{0x24u, "image_sample_l", ImageSampleFlagLod},
|
||||
{0x25u, "image_sample_b", ImageSampleFlagBias},
|
||||
{0x26u, "image_sample_b_cl", ImageSampleFlagBias | ImageSampleFlagLodClamp},
|
||||
{0x27u, "image_sample_lz", ImageSampleFlagLevelZero},
|
||||
{0x28u, "image_sample_c", ImageSampleFlagCompare},
|
||||
{0x29u, "image_sample_c_cl", ImageSampleFlagCompare | ImageSampleFlagLodClamp},
|
||||
{0x2au, "image_sample_c_d", ImageSampleFlagCompare | ImageSampleFlagDerivative},
|
||||
{0x2bu, "image_sample_c_d_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp},
|
||||
{0x2cu, "image_sample_c_l", ImageSampleFlagCompare | ImageSampleFlagLod},
|
||||
{0x2du, "image_sample_c_b", ImageSampleFlagCompare | ImageSampleFlagBias},
|
||||
{0x2eu, "image_sample_c_b_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp},
|
||||
{0x2fu, "image_sample_c_lz", ImageSampleFlagCompare | ImageSampleFlagLevelZero},
|
||||
{0x30u, "image_sample_o", ImageSampleFlagOffset},
|
||||
{0x31u, "image_sample_cl_o", ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0x32u, "image_sample_d_o", ImageSampleFlagDerivative | ImageSampleFlagOffset},
|
||||
{0x33u, "image_sample_d_cl_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0x34u, "image_sample_l_o", ImageSampleFlagLod | ImageSampleFlagOffset},
|
||||
{0x35u, "image_sample_b_o", ImageSampleFlagBias | ImageSampleFlagOffset},
|
||||
{0x36u, "image_sample_b_cl_o",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0x37u, "image_sample_lz_o", ImageSampleFlagLevelZero | ImageSampleFlagOffset},
|
||||
{0x38u, "image_sample_c_o", ImageSampleFlagCompare | ImageSampleFlagOffset},
|
||||
{0x39u, "image_sample_c_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0x3au, "image_sample_c_d_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagOffset},
|
||||
{0x3bu, "image_sample_c_d_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset},
|
||||
{0x3cu, "image_sample_c_l_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLod | ImageSampleFlagOffset},
|
||||
{0x3du, "image_sample_c_b_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset},
|
||||
{0x3eu, "image_sample_c_b_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset},
|
||||
{0x3fu, "image_sample_c_lz_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset},
|
||||
{0x68u, "image_sample_cd", ImageSampleFlagDerivative | ImageSampleFlagCd},
|
||||
{0x69u, "image_sample_cd_cl",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp},
|
||||
{0x6au, "image_sample_c_cd",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd},
|
||||
{0x6bu, "image_sample_c_cd_cl",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagLodClamp},
|
||||
{0x6cu, "image_sample_cd_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagOffset},
|
||||
{0x6du, "image_sample_cd_cl_o",
|
||||
ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset},
|
||||
{0x6eu, "image_sample_c_cd_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagOffset},
|
||||
{0x6fu, "image_sample_c_cd_cl_o",
|
||||
ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd |
|
||||
ImageSampleFlagLodClamp | ImageSampleFlagOffset},
|
||||
{0xa0u, "image_sample_a", ImageSampleFlagAdjust},
|
||||
{0xa1u, "image_sample_cl_a", ImageSampleFlagLodClamp | ImageSampleFlagAdjust},
|
||||
{0xa5u, "image_sample_b_a", ImageSampleFlagBias | ImageSampleFlagAdjust},
|
||||
{0xa6u, "image_sample_b_cl_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagAdjust},
|
||||
{0xa8u, "image_sample_c_a", ImageSampleFlagCompare | ImageSampleFlagAdjust},
|
||||
{0xa9u, "image_sample_c_cl_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagAdjust},
|
||||
{0xadu, "image_sample_c_b_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagAdjust},
|
||||
{0xaeu, "image_sample_c_b_cl_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagAdjust},
|
||||
{0xb0u, "image_sample_o_a", ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
{0xb1u, "image_sample_cl_o_a",
|
||||
ImageSampleFlagLodClamp | ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
{0xb5u, "image_sample_b_o_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
{0xb6u, "image_sample_b_cl_o_a",
|
||||
ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust},
|
||||
{0xb8u, "image_sample_c_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
{0xb9u, "image_sample_c_cl_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust},
|
||||
{0xbdu, "image_sample_c_b_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset |
|
||||
ImageSampleFlagAdjust},
|
||||
{0xbeu, "image_sample_c_b_cl_o_a",
|
||||
ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp |
|
||||
ImageSampleFlagOffset | ImageSampleFlagAdjust},
|
||||
};
|
||||
|
||||
constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = {
|
||||
{0x47u, "image_gather4_lz", Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero, 2u},
|
||||
{0x48u, "image_gather4_c", Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare, 3u},
|
||||
{0x4fu, "image_gather4_c_lz", Opcode::IMAGE_GATHER4_C_LZ,
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero, 3u},
|
||||
{0x57u, "image_gather4_lz_o", Opcode::IMAGE_GATHER4_LZ_O,
|
||||
ImageSampleFlagLevelZero | ImageSampleFlagOffset, 3u},
|
||||
{0x58u, "image_gather4_c_o", Opcode::IMAGE_GATHER4_C_O,
|
||||
ImageSampleFlagCompare | ImageSampleFlagOffset, 4u},
|
||||
{0x5fu, "image_gather4_c_lz_o", Opcode::IMAGE_GATHER4_C_LZ_O,
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset, 4u},
|
||||
{0x61u, "image_gather4h", Opcode::IMAGE_GATHER4H, ImageSampleFlagGatherHorizontal, 2u},
|
||||
{0x47u, Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero},
|
||||
{0x48u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare},
|
||||
{0x4fu, Opcode::IMAGE_GATHER4_C_LZ,
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero},
|
||||
{0x57u, Opcode::IMAGE_GATHER4_LZ_O,
|
||||
ImageSampleFlagLevelZero | ImageSampleFlagOffset},
|
||||
{0x58u, Opcode::IMAGE_GATHER4_C_O,
|
||||
ImageSampleFlagCompare | ImageSampleFlagOffset},
|
||||
{0x5fu, Opcode::IMAGE_GATHER4_C_LZ_O,
|
||||
ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset},
|
||||
{0x61u, Opcode::IMAGE_GATHER4H, ImageSampleFlagGatherHorizontal},
|
||||
};
|
||||
|
||||
constexpr MimgAtomicInfo MIMG_ATOMIC_OPCODE_LIST[] = {
|
||||
{0x0fu, "image_atomic_swap", Opcode::IMAGE_ATOMIC_SWAP},
|
||||
{0x11u, "image_atomic_add", Opcode::IMAGE_ATOMIC_ADD},
|
||||
{0x15u, "image_atomic_umin", Opcode::IMAGE_ATOMIC_UMIN},
|
||||
{0x17u, "image_atomic_umax", Opcode::IMAGE_ATOMIC_UMAX},
|
||||
{0x18u, "image_atomic_and", Opcode::IMAGE_ATOMIC_AND},
|
||||
{0x19u, "image_atomic_or", Opcode::IMAGE_ATOMIC_OR},
|
||||
{0x1au, "image_atomic_xor", Opcode::IMAGE_ATOMIC_XOR},
|
||||
constexpr Detail::OpcodeMap MIMG_ATOMIC_OPCODE_LIST[] = {
|
||||
{0x0fu, Opcode::IMAGE_ATOMIC_SWAP},
|
||||
{0x11u, Opcode::IMAGE_ATOMIC_ADD},
|
||||
{0x15u, Opcode::IMAGE_ATOMIC_UMIN},
|
||||
{0x17u, Opcode::IMAGE_ATOMIC_UMAX},
|
||||
{0x18u, Opcode::IMAGE_ATOMIC_AND},
|
||||
{0x19u, Opcode::IMAGE_ATOMIC_OR},
|
||||
{0x1au, Opcode::IMAGE_ATOMIC_XOR},
|
||||
};
|
||||
|
||||
constexpr auto MIMG_SAMPLE_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_SAMPLE_OPCODE_LIST);
|
||||
@@ -223,12 +210,12 @@ const MimgGatherInfo* LookupGather(uint32_t opcode) {
|
||||
return Detail::FindOpcode(MIMG_GATHER_OPS, opcode);
|
||||
}
|
||||
|
||||
const MimgAtomicInfo* LookupAtomic(uint32_t opcode) {
|
||||
const Detail::OpcodeMap* LookupAtomic(uint32_t opcode) {
|
||||
return Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode);
|
||||
}
|
||||
|
||||
Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const MimgGatherInfo* gather,
|
||||
const MimgAtomicInfo* atomic) {
|
||||
const Detail::OpcodeMap* atomic) {
|
||||
if (sample != nullptr) {
|
||||
return Opcode::IMAGE_SAMPLE;
|
||||
}
|
||||
@@ -262,7 +249,7 @@ uint32_t DecodeMimgSampleFlags(const MimgSampleInfo* sample, const MimgGatherInf
|
||||
|
||||
uint32_t DecodeMimgAddressComponents(uint32_t opcode, ImageDimension dimension,
|
||||
const MimgSampleInfo* sample, const MimgGatherInfo* gather,
|
||||
const MimgAtomicInfo* atomic) {
|
||||
const Detail::OpcodeMap* atomic) {
|
||||
if (sample != nullptr) {
|
||||
return ImageSampleAddressComponents(sample->flags, dimension);
|
||||
}
|
||||
@@ -349,7 +336,6 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const auto* atomic = LookupAtomic(opcode);
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = word_count;
|
||||
inst.family = Family::MIMG;
|
||||
inst.opcode_id = opcode;
|
||||
|
||||
@@ -94,6 +94,7 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = {
|
||||
{0x12u, Opcode::DS_MIN_F32, 1, 32}, {0x13u, Opcode::DS_MAX_F32, 1, 32},
|
||||
{0x1eu, Opcode::DS_WRITE_B8, 1, 8}, {0x1fu, Opcode::DS_WRITE_B16, 1, 16},
|
||||
{0x20u, Opcode::DS_ADD_RTN_U32, 1, 32}, {0x21u, Opcode::DS_SUB_RTN_U32, 1, 32},
|
||||
{0x23u, Opcode::DS_INC_RTN_U32, 1, 32}, {0x24u, Opcode::DS_DEC_RTN_U32, 1, 32},
|
||||
{0x25u, Opcode::DS_MIN_RTN_I32, 1, 32}, {0x26u, Opcode::DS_MAX_RTN_I32, 1, 32},
|
||||
{0x27u, Opcode::DS_MIN_RTN_U32, 1, 32}, {0x28u, Opcode::DS_MAX_RTN_U32, 1, 32},
|
||||
{0x29u, Opcode::DS_AND_RTN_B32, 1, 32}, {0x2au, Opcode::DS_OR_RTN_B32, 1, 32},
|
||||
@@ -129,13 +130,6 @@ uint32_t SignExtendU32(uint32_t value, uint32_t bits) {
|
||||
return (value ^ sign) - sign;
|
||||
}
|
||||
|
||||
void MarkMemoryUnsupported(Instruction& inst, Family family, uint32_t opcode, const char* reason) {
|
||||
inst.family = family;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Opcode::UNSUPPORTED;
|
||||
SetUnsupported(inst, family, opcode, reason);
|
||||
}
|
||||
|
||||
void ApplyMemoryInfo(Instruction& inst, const MemoryOpcodeInfo* info) {
|
||||
if (info == nullptr) {
|
||||
inst.opcode = Opcode::UNSUPPORTED;
|
||||
@@ -172,6 +166,8 @@ bool IsDsAtomicOpcode(Opcode opcode) {
|
||||
case Opcode::DS_ADD_RTN_U32:
|
||||
case Opcode::DS_SUB_U32:
|
||||
case Opcode::DS_SUB_RTN_U32:
|
||||
case Opcode::DS_INC_RTN_U32:
|
||||
case Opcode::DS_DEC_RTN_U32:
|
||||
case Opcode::DS_MIN_I32:
|
||||
case Opcode::DS_MIN_RTN_I32:
|
||||
case Opcode::DS_MAX_I32:
|
||||
@@ -231,7 +227,6 @@ void DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t soffset = (word1 >> 25u) & 0x7fu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = SignExtendU32(word1 & 0x1fffffu, 21u);
|
||||
inst.glc = ((word0 >> 16u) & 1u) != 0;
|
||||
@@ -241,7 +236,7 @@ void DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::SMEM, opcode, "SMEM opcode is not implemented");
|
||||
SetUnsupported(inst, Family::SMEM, opcode, "SMEM opcode is not implemented");
|
||||
}
|
||||
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
@@ -263,7 +258,6 @@ void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
const uint32_t soffset = (word1 >> 24u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = word0 & 0xfffu;
|
||||
inst.idxen = ((word0 >> 13u) & 1u) != 0;
|
||||
@@ -276,7 +270,7 @@ void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::MUBUF, opcode, "MUBUF opcode is not implemented");
|
||||
SetUnsupported(inst, Family::MUBUF, opcode, "MUBUF opcode is not implemented");
|
||||
}
|
||||
|
||||
DecodeVectorGpr(vdata, inst.dst);
|
||||
@@ -299,7 +293,6 @@ void DecodeMtbuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
const uint32_t soffset = (word1 >> 24u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = word0 & 0xfffu;
|
||||
inst.idxen = ((word0 >> 13u) & 1u) != 0;
|
||||
@@ -314,7 +307,7 @@ void DecodeMtbuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::MTBUF, opcode, "MTBUF opcode is not implemented");
|
||||
SetUnsupported(inst, Family::MTBUF, opcode, "MTBUF opcode is not implemented");
|
||||
}
|
||||
|
||||
DecodeVectorGpr(vdata, inst.dst);
|
||||
@@ -339,7 +332,6 @@ void DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t addr = word1 & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = seg == 0u ? (offset & 0x7ffu) : SignExtendU32(offset, 12u);
|
||||
inst.glc = ((word0 >> 16u) & 1u) != 0;
|
||||
@@ -356,7 +348,7 @@ void DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
return;
|
||||
}
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::FLAT, opcode, "FLAT opcode is not implemented");
|
||||
SetUnsupported(inst, Family::FLAT, opcode, "FLAT opcode is not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -384,7 +376,6 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
const uint32_t addr = word1 & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.word_count = 2;
|
||||
inst.offset = offset0 | (offset1 << 8u);
|
||||
inst.gds = ((word0 >> 17u) & 1u) != 0u;
|
||||
@@ -394,7 +385,7 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
ApplyMemoryInfo(inst, info);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
MarkMemoryUnsupported(inst, Family::DS, opcode, "DS opcode is not implemented");
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS opcode is not implemented");
|
||||
}
|
||||
if (inst.opcode == Opcode::DS_SWIZZLE_B32 && inst.offset >= 0xe000u) {
|
||||
SetUnsupported(inst, Family::DS, opcode, "DS swizzle FFT mode is not implemented");
|
||||
|
||||
@@ -41,6 +41,7 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = {
|
||||
{0x06u, Opcode::S_CMOV_B64},
|
||||
{0x07u, Opcode::S_NOT_B32},
|
||||
{0x08u, Opcode::S_NOT_B64},
|
||||
{0x09u, Opcode::S_WQM_B32},
|
||||
{0x0au, Opcode::S_WQM_B64},
|
||||
{0x0bu, Opcode::S_BREV_B32},
|
||||
{0x0fu, Opcode::S_BCNT1_I32_B32},
|
||||
@@ -62,6 +63,7 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = {
|
||||
{0x37u, Opcode::S_ANDN1_SAVEEXEC_B64},
|
||||
{0x3bu, Opcode::S_BITREPLICATE_B64_B32},
|
||||
{0x3cu, Opcode::S_AND_SAVEEXEC_B32},
|
||||
{0x40u, Opcode::S_ORN2_SAVEEXEC_B32},
|
||||
{0x44u, Opcode::S_ANDN1_SAVEEXEC_B32},
|
||||
};
|
||||
|
||||
@@ -82,6 +84,7 @@ constexpr OpcodeMap SOPK_OPCODE_LIST[] = {
|
||||
{0x0eu, Opcode::S_CMP_LE_U32}, {0x0fu, Opcode::S_ADD_I32}, {0x10u, Opcode::S_MULK_I32},
|
||||
{0x13u, Opcode::S_SETREG_B32}, {0x17u, Opcode::S_WAITCNT}, {0x18u, Opcode::S_WAITCNT},
|
||||
{0x19u, Opcode::S_WAITCNT}, {0x1au, Opcode::S_WAITCNT},
|
||||
{0x1bu, Opcode::S_SUBVECTOR_LOOP_BEGIN}, {0x1cu, Opcode::S_SUBVECTOR_LOOP_END},
|
||||
};
|
||||
|
||||
constexpr OpcodeMap SOPP_OPCODE_LIST[] = {
|
||||
@@ -129,7 +132,6 @@ void DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t sdst = (word >> 16u) & 0x7fu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOP1;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOP1_OPS, opcode);
|
||||
@@ -169,7 +171,6 @@ void DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t sdst = (word >> 16u) & 0x7fu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOP2_OPS, opcode);
|
||||
@@ -194,7 +195,6 @@ void DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
: static_cast<int32_t>(static_cast<int16_t>(word & 0xffffu));
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPK;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOPK_OPS, opcode);
|
||||
@@ -211,6 +211,11 @@ void DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
|
||||
switch (inst.opcode) {
|
||||
case Opcode::S_MOVK_I32: DecodeScalarDestination(sdst, pc, inst.dst); return;
|
||||
case Opcode::S_SUBVECTOR_LOOP_BEGIN:
|
||||
case Opcode::S_SUBVECTOR_LOOP_END:
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
inst.branch_target = pc + 4u + static_cast<uint32_t>(imm * 4);
|
||||
return;
|
||||
case Opcode::S_WAITCNT: {
|
||||
const uint32_t waitcnt = word & 0xffffu;
|
||||
inst.dst.kind = OperandKind::Null;
|
||||
@@ -250,7 +255,6 @@ void DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t opcode = (word >> 16u) & 0x7fu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPC;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOPC_OPS, opcode);
|
||||
@@ -272,7 +276,6 @@ void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t simm = word & 0xffffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPP;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(SOPP_OPS, opcode);
|
||||
@@ -288,8 +291,8 @@ void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.opcode == Opcode::S_TTRACEDATA || inst.opcode == Opcode::S_INST_PREFETCH)
|
||||
? 1
|
||||
: 0;
|
||||
inst.branch_offset = static_cast<int32_t>(static_cast<int16_t>(simm)) * 4;
|
||||
inst.branch_target = pc + 4u + static_cast<uint32_t>(inst.branch_offset);
|
||||
const auto branch_offset = static_cast<int32_t>(static_cast<int16_t>(simm)) * 4;
|
||||
inst.branch_target = pc + 4u + static_cast<uint32_t>(branch_offset);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
|
||||
@@ -25,19 +25,6 @@ bool HasLiteral(const Instruction& inst) {
|
||||
inst.src3.kind == OperandKind::LiteralConstant;
|
||||
}
|
||||
|
||||
bool IsControlFlowBranch(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::S_BRANCH:
|
||||
case Opcode::S_CBRANCH_SCC0:
|
||||
case Opcode::S_CBRANCH_SCC1:
|
||||
case Opcode::S_CBRANCH_VCCZ:
|
||||
case Opcode::S_CBRANCH_VCCNZ:
|
||||
case Opcode::S_CBRANCH_EXECZ:
|
||||
case Opcode::S_CBRANCH_EXECNZ: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyLiteral(Operand& operand, uint32_t literal) {
|
||||
if (operand.kind == OperandKind::LiteralConstant) {
|
||||
operand.value = literal;
|
||||
@@ -47,7 +34,7 @@ void ApplyLiteral(Operand& operand, uint32_t literal) {
|
||||
|
||||
std::string RawWordsToString(const Instruction& inst) {
|
||||
std::string text;
|
||||
for (uint32_t i = 0; i < inst.raw_count; i++) {
|
||||
for (uint32_t i = 0; i < inst.word_count; i++) {
|
||||
if (i != 0) {
|
||||
text += " ";
|
||||
}
|
||||
@@ -198,6 +185,24 @@ std::string FormatExp(const Instruction& inst) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsConditionalBranch(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::S_CBRANCH_SCC0:
|
||||
case Opcode::S_CBRANCH_SCC1:
|
||||
case Opcode::S_CBRANCH_VCCZ:
|
||||
case Opcode::S_CBRANCH_VCCNZ:
|
||||
case Opcode::S_CBRANCH_EXECZ:
|
||||
case Opcode::S_CBRANCH_EXECNZ:
|
||||
case Opcode::S_SUBVECTOR_LOOP_BEGIN:
|
||||
case Opcode::S_SUBVECTOR_LOOP_END: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsDirectBranch(Opcode opcode) {
|
||||
return opcode == Opcode::S_BRANCH || IsConditionalBranch(opcode);
|
||||
}
|
||||
|
||||
const char* ImageDimensionToString(ImageDimension dimension) {
|
||||
switch (dimension) {
|
||||
case ImageDimension::Dim1D: return "1d";
|
||||
@@ -234,8 +239,7 @@ void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
|
||||
if (code >= 240u && code <= 247u) {
|
||||
constexpr float values[] = {0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f, 4.0f, -4.0f};
|
||||
operand.kind = OperandKind::FloatInlineConstant;
|
||||
operand.float_val = values[code - 240u];
|
||||
operand.value = FloatBits(operand.float_val);
|
||||
operand.value = FloatBits(values[code - 240u]);
|
||||
return;
|
||||
}
|
||||
if (code >= 256u && code <= 511u) {
|
||||
@@ -253,8 +257,7 @@ void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
|
||||
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return;
|
||||
case 248u:
|
||||
operand.kind = OperandKind::FloatInlineConstant;
|
||||
operand.float_val = 0.15915494309189535f;
|
||||
operand.value = FloatBits(operand.float_val);
|
||||
operand.value = FloatBits(0.15915494309189535f);
|
||||
return;
|
||||
case 251u: operand.kind = OperandKind::VccZ; return;
|
||||
case 252u: operand.kind = OperandKind::ExecZ; return;
|
||||
@@ -307,8 +310,7 @@ void ReadLiteralOperands(std::span<const uint32_t> code, uint32_t word_index, In
|
||||
void SetRawWords(Instruction& inst, std::span<const uint32_t> code, uint32_t word_index,
|
||||
uint32_t word_count) {
|
||||
inst.word_count = word_count;
|
||||
inst.raw_count = word_count;
|
||||
for (uint32_t i = 0; i < inst.raw_count; i++) {
|
||||
for (uint32_t i = 0; i < inst.word_count; i++) {
|
||||
inst.raw[i] = code[word_index + i];
|
||||
}
|
||||
}
|
||||
@@ -392,7 +394,7 @@ void DecodeProgram(std::span<const uint32_t> code, Program& program) {
|
||||
const auto& inst = program.instructions.back();
|
||||
word_index += inst.word_count;
|
||||
|
||||
if (IsControlFlowBranch(inst.opcode)) {
|
||||
if (IsDirectBranch(inst.opcode)) {
|
||||
const auto target_index = inst.branch_target / sizeof(uint32_t);
|
||||
if (branch_targets.empty()) {
|
||||
branch_targets.resize(code.size());
|
||||
@@ -415,7 +417,9 @@ std::string OperandToString(const Operand& operand) {
|
||||
case OperandKind::IntegerInlineConstant:
|
||||
text = fmt::format("{}", operand.signed_val);
|
||||
break;
|
||||
case OperandKind::FloatInlineConstant: text = fmt::format("{:f}", operand.float_val); break;
|
||||
case OperandKind::FloatInlineConstant:
|
||||
text = fmt::format("{:f}", std::bit_cast<float>(operand.value));
|
||||
break;
|
||||
case OperandKind::Sgpr: text = fmt::format("s{}", operand.reg); break;
|
||||
case OperandKind::Vgpr: text = fmt::format("v{}", operand.reg); break;
|
||||
case OperandKind::VccLo: text = "vcc_lo"; break;
|
||||
@@ -483,9 +487,11 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::S_BITSET0_B64:
|
||||
case Opcode::S_BITSET1_B64:
|
||||
case Opcode::S_NOT_B64:
|
||||
case Opcode::S_WQM_B32:
|
||||
case Opcode::S_WQM_B64:
|
||||
case Opcode::S_QUADMASK_B64:
|
||||
case Opcode::S_AND_SAVEEXEC_B32:
|
||||
case Opcode::S_ORN2_SAVEEXEC_B32:
|
||||
case Opcode::S_ANDN1_SAVEEXEC_B32:
|
||||
case Opcode::S_AND_SAVEEXEC_B64:
|
||||
case Opcode::S_ORN2_SAVEEXEC_B64:
|
||||
@@ -532,6 +538,11 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} 0x{:08x}", inst.pc,
|
||||
magic_enum::enum_name(inst.opcode),
|
||||
inst.branch_target));
|
||||
case Opcode::S_SUBVECTOR_LOOP_BEGIN:
|
||||
case Opcode::S_SUBVECTOR_LOOP_END:
|
||||
return WithUnsupportedReason(inst, fmt::format(
|
||||
"0x{:08x}: {} {}, 0x{:08x}", inst.pc, magic_enum::enum_name(inst.opcode),
|
||||
OperandToString(inst.dst), inst.branch_target));
|
||||
case Opcode::EXP: return WithUnsupportedReason(inst, FormatExp(inst));
|
||||
case Opcode::IMAGE_SAMPLE:
|
||||
case Opcode::IMAGE_STORE:
|
||||
@@ -627,6 +638,8 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::DS_ADD_RTN_U32:
|
||||
case Opcode::DS_SUB_U32:
|
||||
case Opcode::DS_SUB_RTN_U32:
|
||||
case Opcode::DS_INC_RTN_U32:
|
||||
case Opcode::DS_DEC_RTN_U32:
|
||||
case Opcode::DS_MIN_I32:
|
||||
case Opcode::DS_MIN_RTN_I32:
|
||||
case Opcode::DS_MAX_I32:
|
||||
|
||||
@@ -53,13 +53,17 @@ enum class Opcode {
|
||||
S_QUADMASK_B64,
|
||||
S_GETPC_B64,
|
||||
S_SETPC_B64,
|
||||
S_SUBVECTOR_LOOP_BEGIN,
|
||||
S_SUBVECTOR_LOOP_END,
|
||||
S_AND_SAVEEXEC_B32,
|
||||
S_ORN2_SAVEEXEC_B32,
|
||||
S_ANDN1_SAVEEXEC_B32,
|
||||
S_AND_SAVEEXEC_B64,
|
||||
S_ORN2_SAVEEXEC_B64,
|
||||
S_ANDN1_SAVEEXEC_B64,
|
||||
S_NOT_B32,
|
||||
S_NOT_B64,
|
||||
S_WQM_B32,
|
||||
S_WQM_B64,
|
||||
S_ADD_U32,
|
||||
S_ADDC_U32,
|
||||
@@ -488,6 +492,8 @@ enum class Opcode {
|
||||
DS_ADD_RTN_U32,
|
||||
DS_SUB_U32,
|
||||
DS_SUB_RTN_U32,
|
||||
DS_INC_RTN_U32,
|
||||
DS_DEC_RTN_U32,
|
||||
DS_MIN_I32,
|
||||
DS_MIN_RTN_I32,
|
||||
DS_MAX_I32,
|
||||
@@ -634,7 +640,6 @@ struct Operand {
|
||||
OperandKind kind = OperandKind::Unknown;
|
||||
uint32_t value = 0;
|
||||
int32_t signed_val = 0;
|
||||
float float_val = 0.0f;
|
||||
uint32_t reg = 0;
|
||||
uint32_t sdwa_sel = 6;
|
||||
// Native 16-bit destinations use the same selector fields internally but preserve the
|
||||
@@ -659,10 +664,8 @@ struct Operand {
|
||||
|
||||
struct Instruction {
|
||||
uint32_t pc = 0;
|
||||
uint32_t word = 0;
|
||||
uint32_t word_count = 1;
|
||||
uint32_t raw[MaxInstructionRawWords] = {};
|
||||
uint32_t raw_count = 1;
|
||||
Family family = Family::Unknown;
|
||||
uint32_t opcode_id = 0;
|
||||
Opcode opcode = Opcode::UNKNOWN;
|
||||
@@ -696,7 +699,6 @@ struct Instruction {
|
||||
bool idxen = false;
|
||||
bool offen = false;
|
||||
bool image_r128 = false;
|
||||
int32_t branch_offset = 0;
|
||||
uint32_t branch_target = 0;
|
||||
struct {
|
||||
uint32_t target = 0;
|
||||
@@ -718,6 +720,8 @@ Family GetInstructionFamily(uint32_t word);
|
||||
// The output object must be freshly initialized.
|
||||
void DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst);
|
||||
void DecodeProgram(std::span<const uint32_t> code, Program& program);
|
||||
bool IsConditionalBranch(Opcode opcode);
|
||||
bool IsDirectBranch(Opcode opcode);
|
||||
|
||||
void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand);
|
||||
void DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand);
|
||||
|
||||
@@ -714,57 +714,7 @@ void DecodeVop1Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
ReadLiteralOperands(code, word_index, inst);
|
||||
}
|
||||
|
||||
using Vop1ModifierDecodeFn = void (*)(uint32_t pc, std::span<const uint32_t> code,
|
||||
uint32_t word_index, uint32_t opcode, uint32_t vdst,
|
||||
Instruction& inst);
|
||||
|
||||
struct Vop1ModifierDecoder {
|
||||
uint32_t escape = 0;
|
||||
Vop1ModifierDecodeFn decode = nullptr;
|
||||
};
|
||||
|
||||
constexpr Vop1ModifierDecoder VOP1_MODIFIER_DECODERS[] = {
|
||||
{249u, DecodeVop1Sdwa},
|
||||
{250u, DecodeVop1Dpp},
|
||||
};
|
||||
|
||||
bool TryDecodeVop1Modifier(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
uint32_t src0, uint32_t opcode, uint32_t vdst, Instruction& inst) {
|
||||
for (const auto& decoder: VOP1_MODIFIER_DECODERS) {
|
||||
if (decoder.escape == src0) {
|
||||
decoder.decode(pc, code, word_index, opcode, vdst, inst);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsVop2FloatSourceOpcode(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::V_ADD_F32:
|
||||
case Opcode::V_SUB_F32:
|
||||
case Opcode::V_SUBREV_F32:
|
||||
case Opcode::V_MUL_F32:
|
||||
case Opcode::V_MIN_F32:
|
||||
case Opcode::V_MAX_F32:
|
||||
case Opcode::V_MAC_F32:
|
||||
case Opcode::V_MADMK_F32:
|
||||
case Opcode::V_MADAK_F32:
|
||||
case Opcode::V_CVT_PKRTZ_F16_F32:
|
||||
case Opcode::V_ADD_F16:
|
||||
case Opcode::V_SUB_F16:
|
||||
case Opcode::V_SUBREV_F16:
|
||||
case Opcode::V_MUL_F16:
|
||||
case Opcode::V_FMAC_F16:
|
||||
case Opcode::V_FMAMK_F16:
|
||||
case Opcode::V_FMAAK_F16:
|
||||
case Opcode::V_MAX_F16:
|
||||
case Opcode::V_MIN_F16: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsVop2FloatResultOpcode(Opcode opcode) {
|
||||
bool IsVop2FloatOpcode(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::V_ADD_F32:
|
||||
case Opcode::V_SUB_F32:
|
||||
@@ -975,12 +925,12 @@ bool ValidateVop2Sdwa(Instruction& inst, uint32_t opcode, const Vop2SdwaFields&
|
||||
SetUnsupported(inst, Family::VOP2, opcode, "VOP2 SDWA selector is invalid");
|
||||
return false;
|
||||
}
|
||||
if ((fields.clamp != 0u || fields.omod != 0u) && !IsVop2FloatResultOpcode(inst.opcode)) {
|
||||
if ((fields.clamp != 0u || fields.omod != 0u) && !IsVop2FloatOpcode(inst.opcode)) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode, "VOP2 SDWA output modifiers are not supported");
|
||||
return false;
|
||||
}
|
||||
if (IsFullWidthVop2Sdwa(fields)) {
|
||||
if (!IsVop2FloatSourceOpcode(inst.opcode) && inst.opcode != Opcode::V_CNDMASK_B32 &&
|
||||
if (!IsVop2FloatOpcode(inst.opcode) && inst.opcode != Opcode::V_CNDMASK_B32 &&
|
||||
(fields.src0_neg != 0u || fields.src0_abs != 0u || fields.src1_neg != 0u ||
|
||||
fields.src1_abs != 0u)) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode,
|
||||
@@ -1098,7 +1048,7 @@ void DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
inst.src1.negate_hi = inst.src1.negate;
|
||||
}
|
||||
|
||||
if (!IsVop2FloatSourceOpcode(inst.opcode) && !packed_fmac &&
|
||||
if (!IsVop2FloatOpcode(inst.opcode) && !packed_fmac &&
|
||||
(inst.src0.negate || inst.src0.absolute || inst.src1.negate || inst.src1.absolute)) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode,
|
||||
"VOP2 DPP integer source modifiers are not supported");
|
||||
@@ -1107,32 +1057,6 @@ void DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
FinalizeVop2Instruction(code, word_index, inst);
|
||||
}
|
||||
|
||||
using Vop2ModifierDecodeFn = void (*)(uint32_t pc, std::span<const uint32_t> code,
|
||||
uint32_t word_index, uint32_t opcode, uint32_t vdst,
|
||||
uint32_t vsrc1, Instruction& inst);
|
||||
|
||||
struct Vop2ModifierDecoder {
|
||||
uint32_t escape = 0;
|
||||
Vop2ModifierDecodeFn decode = nullptr;
|
||||
};
|
||||
|
||||
constexpr Vop2ModifierDecoder VOP2_MODIFIER_DECODERS[] = {
|
||||
{249u, DecodeVop2Sdwa},
|
||||
{250u, DecodeVop2Dpp},
|
||||
};
|
||||
|
||||
bool TryDecodeVop2Modifier(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
uint32_t src0, uint32_t opcode, uint32_t vdst, uint32_t vsrc1,
|
||||
Instruction& inst) {
|
||||
for (const auto& decoder: VOP2_MODIFIER_DECODERS) {
|
||||
if (decoder.escape == src0) {
|
||||
decoder.decode(pc, code, word_index, opcode, vdst, vsrc1, inst);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct VopcSdwaFields {
|
||||
uint32_t src0 = 0;
|
||||
uint32_t sdst = 0;
|
||||
@@ -1534,7 +1458,6 @@ void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t vsrc1 = (word >> 9u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::VOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = LookupVop2Opcode(opcode);
|
||||
@@ -1544,8 +1467,10 @@ void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
SetUnsupported(inst, Family::VOP2, opcode, "VOP2 opcode is not implemented");
|
||||
return;
|
||||
}
|
||||
if (TryDecodeVop2Modifier(pc, code, word_index, src0, opcode, vdst, vsrc1, inst)) {
|
||||
return;
|
||||
switch (src0) {
|
||||
case 249u: DecodeVop2Sdwa(pc, code, word_index, opcode, vdst, vsrc1, inst); return;
|
||||
case 250u: DecodeVop2Dpp(pc, code, word_index, opcode, vdst, vsrc1, inst); return;
|
||||
default: break;
|
||||
}
|
||||
|
||||
DecodeVectorGpr(vdst, inst.dst);
|
||||
@@ -1562,7 +1487,6 @@ void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t vdst = (word >> 17u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::VOP1;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(VOP1_OPS, opcode);
|
||||
@@ -1577,8 +1501,10 @@ void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.src_count = 0;
|
||||
return;
|
||||
}
|
||||
if (TryDecodeVop1Modifier(pc, code, word_index, src0, opcode, vdst, inst)) {
|
||||
return;
|
||||
switch (src0) {
|
||||
case 249u: DecodeVop1Sdwa(pc, code, word_index, opcode, vdst, inst); return;
|
||||
case 250u: DecodeVop1Dpp(pc, code, word_index, opcode, vdst, inst); return;
|
||||
default: break;
|
||||
}
|
||||
const bool scalar_dst = UsesScalarDestination(inst.opcode);
|
||||
if (scalar_dst) {
|
||||
@@ -1599,7 +1525,6 @@ void DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t vsrc1 = (word >> 9u) & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.family = Family::VOPC;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(VOPC_OPS, opcode);
|
||||
@@ -1638,7 +1563,6 @@ void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const uint32_t neg = (word1 >> 29u) & 0x7u;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.family = Family::VOP3;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = LookupVop3Opcode(opcode);
|
||||
@@ -1792,7 +1716,6 @@ void DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
const uint32_t neg = (word1 >> 29u) & 0x7u;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word0;
|
||||
inst.family = Family::VOP3P;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Detail::LookupOpcode(VOP3P_OPS, opcode);
|
||||
@@ -1842,7 +1765,6 @@ void DecodeVintrp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_ind
|
||||
const uint32_t vsrc = word & 0xffu;
|
||||
|
||||
inst.pc = pc;
|
||||
inst.word = word;
|
||||
inst.word_count = 1;
|
||||
inst.family = Family::VINTRP;
|
||||
inst.opcode_id = opcode;
|
||||
|
||||
@@ -22,29 +22,63 @@ Decoder::Operand ConditionOperand(Decoder::OperandKind kind) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void Translator::S_SUBVECTOR_LOOP(const Decoder::Instruction& inst, bool begin) {
|
||||
const auto zero = IR::U32(IR::Value(0u));
|
||||
const auto lo = ir.GetExecLo();
|
||||
const auto hi = ir.GetExecHi();
|
||||
const auto saved = ReadU32(inst.dst);
|
||||
if (begin) {
|
||||
const auto low_active = ir.INotEqual(lo, zero);
|
||||
instruction_branch_condition = ir.IEqual(ir.BitwiseOr(lo, hi), zero);
|
||||
WriteRawU32(inst.dst, ir.Select(instruction_branch_condition, saved,
|
||||
ir.Select(low_active, hi, lo)));
|
||||
// Keep the ISA assignment order: SDST may itself name an EXEC half.
|
||||
WriteRawU32(ConditionOperand(Decoder::OperandKind::ExecHi),
|
||||
ir.Select(low_active, zero, ir.GetExecHi()));
|
||||
} else {
|
||||
const auto high_active = ir.INotEqual(hi, zero);
|
||||
instruction_branch_condition =
|
||||
ir.LogicalAnd(ir.LogicalNot(high_active), ir.INotEqual(saved, zero));
|
||||
WriteRawU32(ConditionOperand(Decoder::OperandKind::ExecHi),
|
||||
ir.Select(instruction_branch_condition, saved, hi));
|
||||
WriteRawU32(inst.dst,
|
||||
ir.Select(instruction_branch_condition, lo, ReadU32(inst.dst)));
|
||||
WriteRawU32(ConditionOperand(Decoder::OperandKind::ExecLo),
|
||||
ir.Select(high_active, saved,
|
||||
ir.Select(instruction_branch_condition, zero, ir.GetExecLo())));
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode operation,
|
||||
bool negate_exec, bool negate_source, bool write_64) {
|
||||
if (!write_64) {
|
||||
// Read the encoded scalar word and preserve EXEC_HI, including in wave32.
|
||||
const auto old = ir.GetExecLo();
|
||||
const auto src = ReadU32(inst.src0);
|
||||
const auto lhs = negate_exec ? ir.BitwiseNot(old) : old;
|
||||
const auto rhs = negate_source ? ir.BitwiseNot(src) : src;
|
||||
IR::U32 result;
|
||||
switch (operation) {
|
||||
case IR::ValueOpcode::LogicalAnd: result = ir.BitwiseAnd(lhs, rhs); break;
|
||||
case IR::ValueOpcode::LogicalOr: result = ir.BitwiseOr(lhs, rhs); break;
|
||||
default: EXIT("unsupported SAVEEXEC operation");
|
||||
}
|
||||
WriteRawU32(inst.dst, old);
|
||||
WriteRawU32(ConditionOperand(Decoder::OperandKind::ExecLo), result);
|
||||
ir.SetScc(ir.INotEqual(result, IR::U32(IR::Value(0u))));
|
||||
return;
|
||||
}
|
||||
const auto old = ir.GetExec();
|
||||
const auto src = ReadMask(inst.src0);
|
||||
const auto lhs = negate_exec ? ir.LogicalNot(old) : old;
|
||||
const auto rhs = negate_source ? ir.LogicalNot(src) : src;
|
||||
auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
|
||||
if (write_64) {
|
||||
WriteMask(inst.dst, old, true);
|
||||
} else {
|
||||
WriteRawU32(inst.dst, ir.GetExecLo());
|
||||
if (current_wave_size == 64u) {
|
||||
const auto low_half =
|
||||
ir.ULessThan(IR::U32(ir.Emit(IR::ValueOpcode::LaneId)), IR::U32(IR::Value(32u)));
|
||||
result = IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {low_half, result, old}));
|
||||
}
|
||||
}
|
||||
const auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
|
||||
WriteMask(inst.dst, old, true);
|
||||
const auto mask = BallotMask(result);
|
||||
ir.SetExec(result);
|
||||
ir.SetExecLo(mask[0]);
|
||||
ir.SetExecHi(mask[1]);
|
||||
ir.SetScc(
|
||||
ir.INotEqual(write_64 ? ir.BitwiseOr(mask[0], mask[1]) : mask[0], IR::U32(IR::Value(0u))));
|
||||
ir.SetScc(ir.INotEqual(ir.BitwiseOr(mask[0], mask[1]), IR::U32(IR::Value(0u))));
|
||||
}
|
||||
|
||||
void Translator::ADD_U32(const Decoder::Instruction& inst, bool vector, bool use_carry_in) {
|
||||
@@ -267,10 +301,19 @@ void Translator::S_MOV_B64(const Decoder::Instruction& inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
|
||||
const auto mask_valid = ReadMaskValid(inst.src0);
|
||||
const auto result =
|
||||
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
|
||||
void Translator::S_WQM(const Decoder::Instruction& inst, bool wide) {
|
||||
const auto source = wide ? ReadU64(inst.src0)
|
||||
: ir.ConstructU64(ReadU32(inst.src0), IR::U32(IR::Value(0u)));
|
||||
const auto result = IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {source}));
|
||||
if (!wide) {
|
||||
const auto low = ir.CompositeExtract(result, 0);
|
||||
// A 32-bit write preserves the other EXEC/VCC half and invalidates any
|
||||
// overlapping SGPR-pair mask provenance through the ordinary scalar path.
|
||||
WriteRawU32(inst.dst, low);
|
||||
ir.SetScc(ir.INotEqual(low, IR::U32(IR::Value(0u))));
|
||||
return;
|
||||
}
|
||||
const auto mask_valid = ReadMaskValid(inst.src0);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||
@@ -328,7 +371,7 @@ void Translator::V_READFIRSTLANE_B32(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
void Translator::V_READLANE_B32(const Decoder::Instruction& inst) {
|
||||
const auto lane_mask = IR::U32(IR::Value(current_wave_size == 32u ? 31u : 63u));
|
||||
const auto lane_mask = IR::U32(IR::Value(program.wave_size == 32u ? 31u : 63u));
|
||||
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
ir.Emit(IR::ValueOpcode::ReadLane, {ReadU32(inst.src0), lane}));
|
||||
@@ -336,7 +379,7 @@ void Translator::V_READLANE_B32(const Decoder::Instruction& inst) {
|
||||
|
||||
void Translator::V_WRITELANE_B32(const Decoder::Instruction& inst) {
|
||||
EXIT_IF(inst.dst.kind != Decoder::OperandKind::Vgpr);
|
||||
const auto lane_mask = IR::U32(IR::Value(current_wave_size == 32u ? 31u : 63u));
|
||||
const auto lane_mask = IR::U32(IR::Value(program.wave_size == 32u ? 31u : 63u));
|
||||
const auto reg = static_cast<IR::VectorReg>(inst.dst.reg);
|
||||
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
|
||||
const auto result = IR::U32(
|
||||
|
||||
@@ -89,10 +89,10 @@ void Translator::V_CVT_16_F16(const Decoder::Instruction& inst, bool signed_valu
|
||||
if (signed_value) {
|
||||
const auto converted =
|
||||
ConvertF32ToI32Saturated(value, -32768.0f, 32768.0f, 32767.0f, 0xffff8000u, 0x7fffu);
|
||||
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
|
||||
return;
|
||||
}
|
||||
WriteU16(DestinationOperand(inst),
|
||||
Write16Bits(DestinationOperand(inst),
|
||||
ConvertF32ToU32Saturated(value, 65536.0f, 65535.0f, 0xffffu));
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ bool Translator::Float16Unary(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
result = ApplyF32ResultModifiers(inst.dst, result);
|
||||
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
|
||||
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
|
||||
WriteU16(DestinationOperand(inst), ir.Select(negative, invalid, bits));
|
||||
Write16Bits(DestinationOperand(inst), ir.Select(negative, invalid, bits));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ bool Translator::Float16Trig(const Decoder::Instruction& inst, IR::ValueOpcode o
|
||||
result = ApplyF32ResultModifiers(inst.dst, result);
|
||||
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
|
||||
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
|
||||
WriteU16(DestinationOperand(inst), ir.Select(infinite, invalid, bits));
|
||||
Write16Bits(DestinationOperand(inst), ir.Select(infinite, invalid, bits));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ bool Translator::Float16Ternary(const Decoder::Instruction& inst, IR::ValueOpcod
|
||||
bool accumulator, bool mix) {
|
||||
std::array<IR::Value, 3> args;
|
||||
for (uint32_t index = 0; index < args.size(); index++) {
|
||||
const auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
const auto& operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
args[index] = mix ? IR::Value(ReadMixF32(operand)) : IR::Value(ReadF16AsF32(operand));
|
||||
}
|
||||
WriteF16(DestinationOperand(inst), IR::F32(ir.Emit(opcode, {args[0], args[1], args[2]})));
|
||||
@@ -128,7 +128,7 @@ bool Translator::FloatBinary(const Decoder::Instruction& inst, IR::ValueOpcode o
|
||||
bool reverse) {
|
||||
std::array<IR::Value, 2> args;
|
||||
for (uint32_t index = 0; index < args.size(); index++) {
|
||||
const auto operand = SourceAt(inst, reverse ? 1u - index : index);
|
||||
const auto& operand = SourceAt(inst, reverse ? 1u - index : index);
|
||||
args[index] = ReadOperand(operand, IR::ArgTypeOf(opcode, index));
|
||||
}
|
||||
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {args[0], args[1]}));
|
||||
@@ -139,7 +139,7 @@ bool Translator::FloatTernary(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
bool accumulator, bool mix) {
|
||||
std::array<IR::Value, 3> args;
|
||||
for (uint32_t index = 0; index < args.size(); index++) {
|
||||
const auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
const auto& operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
const auto type = IR::ArgTypeOf(opcode, index);
|
||||
args[index] = type == IR::Type::F32 && mix ? IR::Value(ReadMixF32(operand))
|
||||
: ReadOperand(operand, type);
|
||||
@@ -203,9 +203,6 @@ bool Translator::V_CUBEMA_F32(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
bool Translator::FloatCube(const Decoder::Instruction& inst, uint32_t result_kind) {
|
||||
const auto select_f32 = [&](IR::U1 condition, IR::F32 true_value, IR::F32 false_value) {
|
||||
return IR::F32(ir.Emit(IR::ValueOpcode::SelectF32, {condition, true_value, false_value}));
|
||||
};
|
||||
const auto x = ReadMixF32(inst.src0);
|
||||
const auto y = ReadMixF32(inst.src1);
|
||||
const auto z = ReadMixF32(inst.src2);
|
||||
@@ -223,18 +220,18 @@ bool Translator::FloatCube(const Decoder::Instruction& inst, uint32_t result_kin
|
||||
const auto y_neg = IR::U1(ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {y, IR::Value::F32(0.0f)}));
|
||||
const auto z_neg = IR::U1(ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {z, IR::Value::F32(0.0f)}));
|
||||
const auto select_face = [&](IR::F32 x_value, IR::F32 y_value, IR::F32 z_value) {
|
||||
return select_f32(z_face, z_value, select_f32(y_face, y_value, x_value));
|
||||
return SelectF32(z_face, z_value, SelectF32(y_face, y_value, x_value));
|
||||
};
|
||||
IR::F32 result;
|
||||
switch (result_kind) {
|
||||
case 0u:
|
||||
result = select_face(
|
||||
select_f32(x_neg, IR::F32(IR::Value::F32(1.0f)), IR::F32(IR::Value::F32(0.0f))),
|
||||
select_f32(y_neg, IR::F32(IR::Value::F32(3.0f)), IR::F32(IR::Value::F32(2.0f))),
|
||||
select_f32(z_neg, IR::F32(IR::Value::F32(5.0f)), IR::F32(IR::Value::F32(4.0f))));
|
||||
SelectF32(x_neg, IR::F32(IR::Value::F32(1.0f)), IR::F32(IR::Value::F32(0.0f))),
|
||||
SelectF32(y_neg, IR::F32(IR::Value::F32(3.0f)), IR::F32(IR::Value::F32(2.0f))),
|
||||
SelectF32(z_neg, IR::F32(IR::Value::F32(5.0f)), IR::F32(IR::Value::F32(4.0f))));
|
||||
break;
|
||||
case 1u: result = select_face(select_f32(x_neg, z, nz), x, select_f32(z_neg, nx, x)); break;
|
||||
case 2u: result = select_face(ny, select_f32(y_neg, nz, z), ny); break;
|
||||
case 1u: result = select_face(SelectF32(x_neg, z, nz), x, SelectF32(z_neg, nx, x)); break;
|
||||
case 2u: result = select_face(ny, SelectF32(y_neg, nz, z), ny); break;
|
||||
case 3u: {
|
||||
const auto two = IR::F32(IR::Value::F32(2.0f));
|
||||
result = select_face(IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {x, two})),
|
||||
|
||||
@@ -9,7 +9,7 @@ bool Translator::Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcod
|
||||
const auto value = ReadU16AsU32(inst.src1, arithmetic);
|
||||
const auto count = ir.BitwiseAnd(ReadU16AsU32(inst.src0, false), IR::U32(IR::Value(15u)));
|
||||
const auto result = IR::U32(ir.Emit(opcode, {value, count}));
|
||||
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ bool Translator::Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpco
|
||||
const auto lhs = ReadU16AsU32(inst.src0, sign);
|
||||
const auto rhs = ReadU16AsU32(inst.src1, sign);
|
||||
const auto result = IR::U32(ir.Emit(opcode, {lhs, rhs}));
|
||||
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ bool Translator::V_MED3_I16(const Decoder::Instruction& inst) {
|
||||
const auto result = IR::U32(ir.Emit(
|
||||
IR::ValueOpcode::SMedTri32, {ReadU16AsU32(inst.src0, true), ReadU16AsU32(inst.src1, true),
|
||||
ReadU16AsU32(inst.src2, true)}));
|
||||
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ bool Translator::SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
std::array<IR::Value, 3> args;
|
||||
for (uint32_t index = 0; index < inst.src_count; index++) {
|
||||
const auto arg_type = IR::ArgTypeOf(opcode, index);
|
||||
const auto operand = SourceAt(inst, reverse && index < 2u ? 1u - index : index);
|
||||
const auto& operand = SourceAt(inst, reverse && index < 2u ? 1u - index : index);
|
||||
args[index] = ReadOperand(operand, arg_type == IR::Type::Void ? type : arg_type);
|
||||
if (mask_shift_count && index == 1u) {
|
||||
args[index] = ir.BitwiseAnd(IR::U32(args[index]), IR::U32(IR::Value(31u)));
|
||||
@@ -563,10 +563,8 @@ bool Translator::PackB16(const Decoder::Instruction& inst, bool high0, bool high
|
||||
: ReadU32(inst.src0);
|
||||
const auto hi = high1 ? ir.ShiftRightLogical(ReadU32(inst.src1), IR::U32(IR::Value(16u)))
|
||||
: ReadU32(inst.src1);
|
||||
const auto low_bits = ir.BitwiseAnd(lo, IR::U32(IR::Value(0xffffu)));
|
||||
const auto high_bits = ir.ShiftLeftLogical(ir.BitwiseAnd(hi, IR::U32(IR::Value(0xffffu))),
|
||||
IR::U32(IR::Value(16u)));
|
||||
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low_bits, high_bits));
|
||||
const auto result = PackU16Lanes(lo, hi);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,16 +71,22 @@ ResourceKind FlatSegmentResourceKind(uint32_t segment) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsScalarAddressLoad(Decoder::Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Decoder::Opcode::S_LOAD_DWORD:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX2:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX4:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX8:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX16: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
ResourceKind MemoryKind(const Decoder::Instruction& decoded) {
|
||||
switch (decoded.family) {
|
||||
case Decoder::Family::SMEM:
|
||||
return decoded.opcode == Decoder::Opcode::S_LOAD_DWORD ||
|
||||
decoded.opcode == Decoder::Opcode::S_LOAD_DWORDX2 ||
|
||||
decoded.opcode == Decoder::Opcode::S_LOAD_DWORDX4 ||
|
||||
decoded.opcode == Decoder::Opcode::S_LOAD_DWORDX8 ||
|
||||
decoded.opcode == Decoder::Opcode::S_LOAD_DWORDX16
|
||||
? ResourceKind::ScalarAddress
|
||||
: ResourceKind::ScalarBuffer;
|
||||
return IsScalarAddressLoad(decoded.opcode) ? ResourceKind::ScalarAddress
|
||||
: ResourceKind::ScalarBuffer;
|
||||
case Decoder::Family::MUBUF:
|
||||
case Decoder::Family::MTBUF: return ResourceKind::Buffer;
|
||||
case Decoder::Family::FLAT: return FlatSegmentResourceKind(decoded.memory_segment);
|
||||
@@ -146,16 +152,6 @@ IR::MemoryInfo MemoryInfoFromDecoded(const Decoder::Instruction& decoded) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
bool IsScalarAddressLoad(Decoder::Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Decoder::Opcode::S_LOAD_DWORD:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX2:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX4:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX8:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX16: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsScalarBufferLoad(Decoder::Opcode opcode) {
|
||||
switch (opcode) {
|
||||
@@ -290,15 +286,15 @@ IR::Value Translator::GetAddressResource(IR::Value low, IR::Value high) {
|
||||
|
||||
Translator::AddressOperands Translator::ReadAddressOperands(const Decoder::Instruction& inst,
|
||||
uint32_t first_source) {
|
||||
const auto memory = MemoryInfoFromDecoded(inst);
|
||||
const auto kind = MemoryKind(inst);
|
||||
const auto low = ReadU32(MemorySourceAt(inst, first_source));
|
||||
const auto high_or_base = MemorySourceAt(inst, first_source + 1u);
|
||||
if (memory.kind == IR::ResourceKind::Scratch) {
|
||||
if (kind == IR::ResourceKind::Scratch) {
|
||||
const auto offset =
|
||||
high_or_base.kind != Decoder::OperandKind::Vgpr ? ReadU32(high_or_base) : low;
|
||||
return {ir.Emit(IR::ValueOpcode::GetScratchResource), offset, IR::Value(0u)};
|
||||
}
|
||||
if (memory.kind == IR::ResourceKind::Global &&
|
||||
if (kind == IR::ResourceKind::Global &&
|
||||
high_or_base.kind != Decoder::OperandKind::Vgpr) {
|
||||
const auto base_low = ReadU32(high_or_base);
|
||||
const auto base_high = ReadU32(OffsetOperand(high_or_base, 1u));
|
||||
@@ -329,18 +325,17 @@ IR::Value Translator::GetSamplerResource(const IR::MemoryInfo& memory) {
|
||||
|
||||
IR::Value Translator::MakeImageAddress(const Decoder::Instruction& inst,
|
||||
const Decoder::Operand& base) {
|
||||
const auto memory = MemoryInfoFromDecoded(inst);
|
||||
std::array<IR::Value, 13> components {};
|
||||
components.fill(IR::Value(0u));
|
||||
const auto count =
|
||||
Decoder::ImageAddressDwordCount(memory.image_sample_flags, memory.image_address_components);
|
||||
Decoder::ImageAddressDwordCount(inst.image_sample_flags, inst.image_address_components);
|
||||
EXIT_IF(count > components.size());
|
||||
const auto nsa_components =
|
||||
std::min(memory.image_nsa_dwords * 4u, Decoder::MaxImageNsaAddressComponents);
|
||||
std::min(inst.image_nsa_dwords * 4u, Decoder::MaxImageNsaAddressComponents);
|
||||
for (uint32_t index = 0; index < count; index++) {
|
||||
if (index != 0u && index - 1u < nsa_components) {
|
||||
components[index] =
|
||||
ir.GetVectorReg(static_cast<IR::VectorReg>(memory.image_nsa_addr[index - 1u]));
|
||||
ir.GetVectorReg(static_cast<IR::VectorReg>(inst.image_nsa_addr[index - 1u]));
|
||||
} else {
|
||||
components[index] = ReadRawU32(OffsetOperand(PlainOperand(base), index));
|
||||
}
|
||||
@@ -384,11 +379,10 @@ void Translator::WriteImageComponents(const Decoder::Operand& dst, IR::Value val
|
||||
|
||||
Translator::BufferAddress Translator::ReadBufferAddress(const Decoder::Instruction& inst,
|
||||
uint32_t first_source) {
|
||||
const auto memory = MemoryInfoFromDecoded(inst);
|
||||
uint32_t cursor = first_source;
|
||||
const auto next = [&]() { return ReadU32(MemorySourceAt(inst, cursor++)); };
|
||||
const auto index = memory.idxen ? next() : IR::U32(IR::Value(0u));
|
||||
const auto offset = memory.offen ? next() : IR::U32(IR::Value(0u));
|
||||
const auto index = inst.idxen ? next() : IR::U32(IR::Value(0u));
|
||||
const auto offset = inst.offen ? next() : IR::U32(IR::Value(0u));
|
||||
const auto soffset = next();
|
||||
return {index, offset, soffset};
|
||||
}
|
||||
@@ -974,6 +968,10 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicISub32, false);
|
||||
case Decoder::Opcode::DS_SUB_RTN_U32:
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicISub32, true);
|
||||
case Decoder::Opcode::DS_INC_RTN_U32:
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicInc32, true);
|
||||
case Decoder::Opcode::DS_DEC_RTN_U32:
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicDec32, true);
|
||||
case Decoder::Opcode::DS_MIN_I32:
|
||||
return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicSMin32, false);
|
||||
case Decoder::Opcode::DS_MIN_RTN_I32:
|
||||
|
||||
@@ -8,9 +8,12 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
|
||||
case O::S_MOV_B32:
|
||||
case O::S_MOVK_I32: MOV_B32(inst, false); return true;
|
||||
case O::S_MOV_B64: S_MOV_B64(inst); return true;
|
||||
case O::S_WQM_B64: S_WQM_B64(inst); return true;
|
||||
case O::S_WQM_B32: S_WQM(inst, false); return true;
|
||||
case O::S_WQM_B64: S_WQM(inst, true); return true;
|
||||
case O::S_GETPC_B64: S_GETPC_B64(inst); return true;
|
||||
case O::S_SETPC_B64: return true;
|
||||
case O::S_SUBVECTOR_LOOP_BEGIN: S_SUBVECTOR_LOOP(inst, true); return true;
|
||||
case O::S_SUBVECTOR_LOOP_END: S_SUBVECTOR_LOOP(inst, false); return true;
|
||||
case O::S_CSELECT_B32: S_CSELECT_B32(inst); return true;
|
||||
case O::S_CSELECT_B64: ScalarSelect64(inst, inst.src1); return true;
|
||||
case O::S_CMOV_B64: ScalarSelect64(inst, inst.dst); return true;
|
||||
@@ -23,6 +26,9 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
|
||||
case O::S_ANDN1_SAVEEXEC_B32:
|
||||
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, true, false);
|
||||
return true;
|
||||
case O::S_ORN2_SAVEEXEC_B32:
|
||||
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalOr, true, false, false);
|
||||
return true;
|
||||
case O::S_AND_SAVEEXEC_B64:
|
||||
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, false, true);
|
||||
return true;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
Decoder::Operand Translator::SourceAt(const Decoder::Instruction& inst, uint32_t index) {
|
||||
const Decoder::Operand& Translator::SourceAt(const Decoder::Instruction& inst, uint32_t index) {
|
||||
switch (index) {
|
||||
case 0: return inst.src0;
|
||||
case 1: return inst.src1;
|
||||
@@ -25,7 +25,7 @@ Decoder::Operand Translator::DestinationOperand(const Decoder::Instruction& inst
|
||||
return destination;
|
||||
}
|
||||
for (uint32_t index = 0; index < std::min(inst.src_count, 3u); index++) {
|
||||
const auto source = SourceAt(inst, index);
|
||||
const auto& source = SourceAt(inst, index);
|
||||
if (!source.dpp) {
|
||||
continue;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ Decoder::Operand Translator::PlainOperand(const Decoder::Operand& operand) {
|
||||
std::array<IR::U32, 2> Translator::BallotMask(IR::U1 value) {
|
||||
const auto mask = ir.Emit(IR::ValueOpcode::Ballot, {value});
|
||||
return {ir.CompositeExtract(mask, 0),
|
||||
current_wave_size == 64u ? ir.CompositeExtract(mask, 1) : IR::U32(IR::Value(0u))};
|
||||
program.wave_size == 64u ? ir.CompositeExtract(mask, 1) : IR::U32(IR::Value(0u))};
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadRawU32(const Decoder::Operand& operand) {
|
||||
@@ -134,11 +134,15 @@ IR::U32 Translator::ReadRawU32(const Decoder::Operand& operand) {
|
||||
case Decoder::OperandKind::Scc:
|
||||
return ir.Select(ir.GetScc(), IR::U32(IR::Value(1u)), IR::U32(IR::Value(0u)));
|
||||
case Decoder::OperandKind::VccZ:
|
||||
return ir.Select(ir.LogicalNot(ir.GetVcc()), IR::U32(IR::Value(1u)),
|
||||
IR::U32(IR::Value(0u)));
|
||||
case Decoder::OperandKind::ExecZ:
|
||||
return ir.Select(ir.LogicalNot(ir.GetExec()), IR::U32(IR::Value(1u)),
|
||||
IR::U32(IR::Value(0u)));
|
||||
case Decoder::OperandKind::ExecZ: {
|
||||
const bool vcc = operand.kind == Decoder::OperandKind::VccZ;
|
||||
auto mask = vcc ? ir.GetVccLo() : ir.GetExecLo();
|
||||
if (program.wave_size == 64u) {
|
||||
mask = ir.BitwiseOr(mask, vcc ? ir.GetVccHi() : ir.GetExecHi());
|
||||
}
|
||||
const auto zero = IR::U32(IR::Value(0u));
|
||||
return ir.Select(ir.IEqual(mask, zero), IR::U32(IR::Value(1u)), zero);
|
||||
}
|
||||
default: EXIT("invalid decoded operand used as a raw U32 source");
|
||||
}
|
||||
}
|
||||
@@ -440,10 +444,6 @@ void Translator::WriteF16(const Decoder::Operand& operand, IR::F32 value) {
|
||||
Write16Bits(operand, bits);
|
||||
}
|
||||
|
||||
void Translator::WriteU16(const Decoder::Operand& operand, IR::U32 value) {
|
||||
Write16Bits(operand, value);
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadU32(const Decoder::Operand& operand) {
|
||||
return IR::U32(ReadOperand(operand, IR::Type::U32));
|
||||
}
|
||||
@@ -596,7 +596,7 @@ void Translator::WriteU32Pair(const Decoder::Operand& operand,
|
||||
|
||||
IR::U1 Translator::ThreadBit(const std::array<IR::U32, 2>& mask) {
|
||||
const auto lane = IR::U32(ir.Emit(IR::ValueOpcode::LaneId));
|
||||
const auto word = current_wave_size == 64u
|
||||
const auto word = program.wave_size == 64u
|
||||
? ir.Select(ir.ULessThan(lane, IR::U32(IR::Value(32u))), mask[0], mask[1])
|
||||
: mask[0];
|
||||
const auto bit = ir.BitwiseAnd(lane, IR::U32(IR::Value(31u)));
|
||||
@@ -604,10 +604,6 @@ IR::U1 Translator::ThreadBit(const std::array<IR::U32, 2>& mask) {
|
||||
IR::U32(IR::Value(0u)));
|
||||
}
|
||||
|
||||
IR::U1 Translator::ReadCondition(const Decoder::Operand& operand) {
|
||||
return IR::U1(ReadOperand(operand, IR::Type::U1));
|
||||
}
|
||||
|
||||
IR::U32 Translator::ConditionBit(const Decoder::Operand& operand) {
|
||||
return ir.Select(ReadMask(operand), IR::U32(IR::Value(1u)), IR::U32(IR::Value(0u)));
|
||||
}
|
||||
@@ -621,7 +617,7 @@ IR::U1 Translator::ReadMask(const Decoder::Operand& operand) {
|
||||
switch (operand.kind) {
|
||||
case Decoder::OperandKind::Sgpr: {
|
||||
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
|
||||
const auto mask = current_wave_size == 64u
|
||||
const auto mask = program.wave_size == 64u
|
||||
? ReadU32Pair(operand)
|
||||
: std::array {ReadRawU32(operand), IR::U32(IR::Value(0u))};
|
||||
return IR::U1(ir.Emit(
|
||||
@@ -632,7 +628,7 @@ IR::U1 Translator::ReadMask(const Decoder::Operand& operand) {
|
||||
case Decoder::OperandKind::ExecHi: return ir.GetExec();
|
||||
case Decoder::OperandKind::VccLo:
|
||||
case Decoder::OperandKind::VccHi:
|
||||
return current_wave_size == 32u
|
||||
return program.wave_size == 32u
|
||||
? ThreadBit({ReadRawU32(operand), IR::U32(IR::Value(0u))})
|
||||
: ir.GetVcc();
|
||||
case Decoder::OperandKind::Scc: return ir.GetScc();
|
||||
@@ -682,7 +678,7 @@ std::array<IR::U32, 2> Translator::WriteMask(const Decoder::Operand& operand, IR
|
||||
}
|
||||
ir.SetScalarReg(reg, mask[0]);
|
||||
// A wave32 VALU mask destination must not overwrite the neighboring SGPR.
|
||||
if ((write_64 || current_wave_size == 64u) &&
|
||||
if ((write_64 || program.wave_size == 64u) &&
|
||||
IR::RegIndex(reg) + 1u < IR::NumScalarRegs) {
|
||||
const auto high = static_cast<IR::ScalarReg>(IR::RegIndex(reg) + 1u);
|
||||
ir.SetScalarReg(high, mask[1]);
|
||||
@@ -700,7 +696,7 @@ std::array<IR::U32, 2> Translator::WriteMask(const Decoder::Operand& operand, IR
|
||||
}
|
||||
case Decoder::OperandKind::VccLo:
|
||||
case Decoder::OperandKind::VccHi: {
|
||||
if (!write_64 && current_wave_size == 32u) {
|
||||
if (!write_64 && program.wave_size == 32u) {
|
||||
WriteRawU32(operand, mask[0]);
|
||||
return mask;
|
||||
}
|
||||
@@ -762,6 +758,10 @@ void Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo
|
||||
case CFG::BranchCondition::VccNonZero: condition = ir.GetVcc(); break;
|
||||
case CFG::BranchCondition::ExecZero: condition = ir.LogicalNot(ir.GetExec()); break;
|
||||
case CFG::BranchCondition::ExecNonZero: condition = ir.GetExec(); break;
|
||||
case CFG::BranchCondition::ScalarInstruction:
|
||||
EXIT_IF(instruction_branch_condition.IsEmpty());
|
||||
condition = instruction_branch_condition;
|
||||
break;
|
||||
case CFG::BranchCondition::GotoVariable:
|
||||
if (source.terminator.goto_variable == UINT32_MAX) {
|
||||
EXIT("block %u reads an invalid goto variable", source.id);
|
||||
@@ -1117,7 +1117,7 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
: u32(0);
|
||||
const auto vertex = entry_ir.IMul(local, step);
|
||||
const auto first = entry_ir.IAdd(vertex, parity);
|
||||
const auto second = mesh.InputPrimitiveSize() == 3u
|
||||
const auto second = mesh.InputPrimitiveSize() >= 2u
|
||||
? entry_ir.ISub(entry_ir.IAdd(vertex, u32(1)), parity)
|
||||
: u32(0);
|
||||
const auto third = mesh.InputPrimitiveSize() == 3u
|
||||
@@ -1198,7 +1198,7 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
|
||||
}
|
||||
for (const auto& cfg_block: cfg.blocks) {
|
||||
const auto typed_index = block_indices.at(cfg_block.id);
|
||||
Translator translator(result, result.blocks[typed_index], vector_limit, options.wave_size);
|
||||
Translator translator(result, result.blocks[typed_index], vector_limit);
|
||||
for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) {
|
||||
const auto& instruction = decoded.instructions[index];
|
||||
if (IsCodeTableLoad(cfg, instruction.pc)) {
|
||||
|
||||
@@ -9,9 +9,8 @@ namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
class Translator {
|
||||
public:
|
||||
Translator(IR::Program& program, IR::Block* block, uint32_t vector_limit, uint32_t wave_size)
|
||||
: program(program), ir(block), current_vector_limit(vector_limit),
|
||||
current_wave_size(wave_size) {}
|
||||
Translator(IR::Program& program, IR::Block* block, uint32_t vector_limit)
|
||||
: program(program), ir(block), current_vector_limit(vector_limit) {}
|
||||
|
||||
void TranslateInstruction(const Decoder::Instruction& inst);
|
||||
void TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
@@ -19,7 +18,7 @@ public:
|
||||
void AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info);
|
||||
|
||||
private:
|
||||
Decoder::Operand SourceAt(const Decoder::Instruction& inst, uint32_t index);
|
||||
const Decoder::Operand& SourceAt(const Decoder::Instruction& inst, uint32_t index);
|
||||
Decoder::Operand DestinationOperand(const Decoder::Instruction& inst);
|
||||
Decoder::Operand OffsetOperand(const Decoder::Operand& operand, uint32_t offset);
|
||||
Decoder::Operand ScalarDestinationOperand(const Decoder::Operand& operand, uint32_t offset);
|
||||
@@ -36,7 +35,6 @@ private:
|
||||
IR::U32 PackHalf2x16(IR::F32 low, IR::F32 high);
|
||||
void Write16Bits(const Decoder::Operand& operand, IR::U32 value);
|
||||
void WriteF16(const Decoder::Operand& operand, IR::F32 value);
|
||||
void WriteU16(const Decoder::Operand& operand, IR::U32 value);
|
||||
IR::U32 ReadU32(const Decoder::Operand& operand);
|
||||
std::array<IR::U32, 2> ReadU32Pair(const Decoder::Operand& operand);
|
||||
IR::U64 ReadU64(const Decoder::Operand& operand);
|
||||
@@ -49,7 +47,6 @@ private:
|
||||
IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane);
|
||||
std::array<IR::U32, 2> ExtractU64(IR::U64 value);
|
||||
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
|
||||
IR::U1 ReadCondition(const Decoder::Operand& operand);
|
||||
IR::U32 ConditionBit(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMask(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMaskValid(const Decoder::Operand& operand);
|
||||
@@ -215,6 +212,7 @@ private:
|
||||
bool V_CNDMASK_B32(const Decoder::Instruction& inst);
|
||||
bool PackB16(const Decoder::Instruction& inst, bool high0, bool high1);
|
||||
|
||||
void S_SUBVECTOR_LOOP(const Decoder::Instruction& inst, bool begin);
|
||||
void S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode operation, bool negate_exec,
|
||||
bool negate_source, bool write_64);
|
||||
void ADD_U32(const Decoder::Instruction& inst, bool vector, bool use_carry_in);
|
||||
@@ -236,7 +234,7 @@ private:
|
||||
void ScalarSelect64(const Decoder::Instruction& inst, const Decoder::Operand& false_source);
|
||||
void MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers);
|
||||
void S_MOV_B64(const Decoder::Instruction& inst);
|
||||
void S_WQM_B64(const Decoder::Instruction& inst);
|
||||
void S_WQM(const Decoder::Instruction& inst, bool wide);
|
||||
void V_MOVRELS_B32(const Decoder::Instruction& inst);
|
||||
void V_MOVRELD_B32(const Decoder::Instruction& inst);
|
||||
void V_READFIRSTLANE_B32(const Decoder::Instruction& inst);
|
||||
@@ -255,10 +253,10 @@ private:
|
||||
|
||||
IR::Program& program;
|
||||
IR::IREmitter ir;
|
||||
IR::U1 instruction_branch_condition;
|
||||
Decoder::Opcode current_opcode = Decoder::Opcode::UNKNOWN;
|
||||
uint32_t current_pc = 0;
|
||||
uint32_t current_vector_limit = 1;
|
||||
uint32_t current_wave_size = 64;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
@@ -189,28 +189,27 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
program.blocks.size() != program.block_storage.size()) {
|
||||
return Fail("value IR block storage is inconsistent");
|
||||
}
|
||||
std::unordered_set<const Block*> blocks;
|
||||
std::unordered_map<const Block*, size_t> block_indices;
|
||||
std::unordered_map<uint32_t, const Block*> blocks_by_id;
|
||||
std::unordered_set<const Inst*> instructions;
|
||||
std::unordered_map<const Inst*, size_t> instruction_positions;
|
||||
for (size_t block_index = 0; block_index < program.blocks.size(); block_index++) {
|
||||
const auto* block = program.blocks[block_index];
|
||||
if (block == nullptr || program.block_storage[block_index] == nullptr ||
|
||||
block != program.block_storage[block_index].get()) {
|
||||
return Fail("value IR block pointer is inconsistent");
|
||||
}
|
||||
if (!blocks.insert(block).second) {
|
||||
if (!block_indices.emplace(block, block_index).second) {
|
||||
return Fail("value IR block pointer is duplicated");
|
||||
}
|
||||
block_indices.emplace(block, block_index);
|
||||
if (program.block_info[block_index].id == UINT32_MAX) {
|
||||
return Fail("value IR block uses the reserved exit id");
|
||||
}
|
||||
if (!blocks_by_id.emplace(program.block_info[block_index].id, block).second) {
|
||||
return Fail("value IR block id is duplicated");
|
||||
}
|
||||
size_t position = 0;
|
||||
for (const auto& inst: *block) {
|
||||
if (!instructions.insert(&inst).second) {
|
||||
if (!instruction_positions.emplace(&inst, position++).second) {
|
||||
return Fail("value IR instruction is duplicated");
|
||||
}
|
||||
}
|
||||
@@ -223,7 +222,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
const auto* block = program.blocks[block_index];
|
||||
std::unordered_set<const Block*> predecessors;
|
||||
for (const auto* predecessor: block->ImmPredecessors()) {
|
||||
if (predecessor == nullptr || !blocks.contains(predecessor)) {
|
||||
if (predecessor == nullptr || !block_indices.contains(predecessor)) {
|
||||
return Fail("value IR block has a foreign predecessor");
|
||||
}
|
||||
if (!predecessors.insert(predecessor).second) {
|
||||
@@ -237,7 +236,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
|
||||
std::unordered_set<const Block*> successors;
|
||||
for (const auto* successor: block->ImmSuccessors()) {
|
||||
if (successor == nullptr || !blocks.contains(successor)) {
|
||||
if (successor == nullptr || !block_indices.contains(successor)) {
|
||||
return Fail("value IR block has a foreign successor");
|
||||
}
|
||||
if (!successors.insert(successor).second) {
|
||||
@@ -264,7 +263,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
return false;
|
||||
}
|
||||
const auto* definition = value.TryInstruction();
|
||||
return definition == nullptr || instructions.contains(definition);
|
||||
return definition == nullptr || instruction_positions.contains(definition);
|
||||
};
|
||||
switch (terminator.kind) {
|
||||
case CFG::TerminatorKind::Branch:
|
||||
@@ -338,7 +337,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
std::unordered_set<const Block*> incoming_blocks;
|
||||
for (size_t arg_index = 0; arg_index < inst.NumArgs(); arg_index++) {
|
||||
const auto* predecessor = inst.PhiBlock(arg_index);
|
||||
if (predecessor == nullptr || !blocks.contains(predecessor) ||
|
||||
if (predecessor == nullptr || !block_indices.contains(predecessor) ||
|
||||
!predecessors.contains(predecessor)) {
|
||||
return Fail("value IR Phi has a foreign or non-predecessor parent");
|
||||
}
|
||||
@@ -520,7 +519,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
TypeName(ArgTypeOf(inst.GetOpcode(), arg_index))));
|
||||
}
|
||||
if (const auto* definition = arg.TryInstruction();
|
||||
definition != nullptr && !instructions.contains(definition)) {
|
||||
definition != nullptr && !instruction_positions.contains(definition)) {
|
||||
return Fail("value IR argument has a foreign definition");
|
||||
}
|
||||
if (const auto* definition = arg.TryInstruction(); definition != nullptr) {
|
||||
@@ -582,13 +581,6 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<const Inst*, size_t> instruction_positions;
|
||||
for (const auto* block: program.blocks) {
|
||||
size_t position = 0;
|
||||
for (const auto& inst: *block) {
|
||||
instruction_positions.emplace(&inst, position++);
|
||||
}
|
||||
}
|
||||
const auto dominates = [&](const Block* definition, const Block* use) {
|
||||
return dominators[block_indices.at(use)][block_indices.at(definition)];
|
||||
};
|
||||
|
||||
@@ -415,7 +415,7 @@ struct BindingLayout {
|
||||
|
||||
struct ShaderInfo {
|
||||
static constexpr uint32_t MaxBuffers = 32;
|
||||
static constexpr uint32_t MaxImages = 32;
|
||||
static constexpr uint32_t MaxImages = 64;
|
||||
static constexpr uint32_t MaxSamplers = 32;
|
||||
static constexpr uint32_t MaxSampledPairs = 64;
|
||||
|
||||
|
||||
@@ -163,6 +163,8 @@ SharedAccess SharedAccessOf(ValueOpcode opcode) {
|
||||
case ValueOpcode::SharedAtomicSwap32:
|
||||
case ValueOpcode::SharedAtomicIAdd32:
|
||||
case ValueOpcode::SharedAtomicISub32:
|
||||
case ValueOpcode::SharedAtomicInc32:
|
||||
case ValueOpcode::SharedAtomicDec32:
|
||||
case ValueOpcode::SharedAtomicSMin32:
|
||||
case ValueOpcode::SharedAtomicUMin32:
|
||||
case ValueOpcode::SharedAtomicSMax32:
|
||||
|
||||
@@ -255,6 +255,8 @@ VALUE_OPCODE(SharedAtomicFMax32, Void, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicSwap32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicIAdd32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicISub32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicInc32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicDec32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicSMin32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicUMin32, U32, U32, U32, U1)
|
||||
VALUE_OPCODE(SharedAtomicSMax32, U32, U32, U32, U1)
|
||||
|
||||
@@ -280,8 +280,7 @@ private:
|
||||
return finish(false);
|
||||
}
|
||||
}
|
||||
if (op == ValueOpcode::GetBufferResource || op == ValueOpcode::GetImageResource ||
|
||||
op == ValueOpcode::GetSamplerResource || op == ValueOpcode::GetAddressResource) {
|
||||
if (IsDescriptorHandle(op)) {
|
||||
size_t expected = 4u;
|
||||
if (op == ValueOpcode::GetImageResource) {
|
||||
expected = 8u;
|
||||
|
||||
@@ -123,46 +123,6 @@ struct DefTable {
|
||||
std::unordered_map<uint32_t, ValueMap> goto_variables;
|
||||
};
|
||||
|
||||
ValueOpcode UndefOpcode(ScalarReg) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ThreadBitScalarReg) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ScalarMaskTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VectorReg) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(GotoVariable) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(SccTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ExecTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ExecLoTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ExecHiTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VccTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VccLoTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VccHiTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
ValueOpcode UndefOpcode(M0Tag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
|
||||
Value InitialValue(ScalarReg) {
|
||||
return Value(0u);
|
||||
}
|
||||
@@ -248,7 +208,7 @@ public:
|
||||
state.result = def;
|
||||
} else if (!block->IsSsaSealed()) {
|
||||
auto& phi = *block->PrependNewInst(block->begin(), ValueOpcode::Phi);
|
||||
phi.SetFlags(TypeOf(UndefOpcode(variable)));
|
||||
phi.SetFlags(InitialValue(variable).GetType());
|
||||
incomplete_phis[block][Variable(variable)] = φ
|
||||
state.result = Value(&phi);
|
||||
} else if (const auto predecessors = block->ImmPredecessors();
|
||||
@@ -258,7 +218,7 @@ public:
|
||||
break;
|
||||
} else {
|
||||
auto& phi = *block->PrependNewInst(block->begin(), ValueOpcode::Phi);
|
||||
phi.SetFlags(TypeOf(UndefOpcode(variable)));
|
||||
phi.SetFlags(InitialValue(variable).GetType());
|
||||
Write(variable, block, Value(&phi));
|
||||
state.phi = φ
|
||||
prepare_phi();
|
||||
|
||||
@@ -593,7 +593,6 @@ static void ShaderGetStaticInputInfoPS(
|
||||
}
|
||||
ps_info.ps_pos_x = (active_inputs & 0x00000100u) != 0;
|
||||
ps_info.ps_pos_y = (active_inputs & 0x00000200u) != 0;
|
||||
ps_info.ps_pos_xy = ps_info.ps_pos_x && ps_info.ps_pos_y;
|
||||
ps_info.ps_pos_z = (active_inputs & 0x00000400u) != 0;
|
||||
ps_info.ps_pos_w = (active_inputs & 0x00000800u) != 0;
|
||||
ps_info.ps_front_face = (active_inputs & 0x00001000u) != 0;
|
||||
@@ -756,19 +755,9 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context&
|
||||
}
|
||||
return params;
|
||||
}
|
||||
EXIT_IF(regs.gs_regs.data_addr == 0);
|
||||
const auto back = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():");
|
||||
const auto back_params =
|
||||
GetShaderParams(regs.gs_regs.data_addr, "ShaderRecompiler GS",
|
||||
GetDeclaredShaderHash(regs.gs_regs.data_addr), {}, back);
|
||||
params.back_code = back_params.code;
|
||||
// Merged shaders receive the GS-back user-data pointer in s0:s1, followed
|
||||
// by the ordinary user SGPRs at s8. Keep both in the runtime register snapshot.
|
||||
// NGG user SGPRs start at s8; a separately compiled GS back half also receives
|
||||
// its user-data pointer in s0:s1.
|
||||
params.user_data.insert(params.user_data.begin(), 8u, 0u);
|
||||
params.user_data[0] = static_cast<uint32_t>(regs.gs_regs.user_data_addr);
|
||||
params.user_data[1] = static_cast<uint32_t>(regs.gs_regs.user_data_addr >> 32u);
|
||||
const uint64_t hashes[] = {params.hash, back_params.hash};
|
||||
params.hash = XXH3_64bits(hashes, sizeof(hashes));
|
||||
info = {};
|
||||
info.pa_cl_vs_out_cntl = sh.m_paClVsOutCntl;
|
||||
auto& mesh = info.mesh;
|
||||
@@ -777,11 +766,25 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context&
|
||||
mesh.max_vertices = sh.m_geMaxOutputPerSubgroup;
|
||||
mesh.provoking_vertex = context.GetModeControl().provoking_vtx_last ? 2u : 0u;
|
||||
mesh.lds_size_dwords = static_cast<uint32_t>(regs.gs_regs.rsrc2.lds_size) * 128u;
|
||||
mesh.scratch_size_dwords = std::max(data.scratch_size_dwords, back.scratch_size_dwords);
|
||||
mesh.scratch_size_dwords = data.scratch_size_dwords;
|
||||
if (data.type == Prospero::ShaderBinaryType::kGsFront) {
|
||||
EXIT_IF(regs.gs_regs.data_addr == 0);
|
||||
const auto back = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():");
|
||||
const auto back_params =
|
||||
GetShaderParams(regs.gs_regs.data_addr, "ShaderRecompiler GS",
|
||||
GetDeclaredShaderHash(regs.gs_regs.data_addr), {}, back);
|
||||
params.back_code = back_params.code;
|
||||
params.user_data[0] = static_cast<uint32_t>(regs.gs_regs.user_data_addr);
|
||||
params.user_data[1] = static_cast<uint32_t>(regs.gs_regs.user_data_addr >> 32u);
|
||||
const uint64_t hashes[] = {params.hash, back_params.hash};
|
||||
params.hash = XXH3_64bits(hashes, sizeof(hashes));
|
||||
mesh.scratch_size_dwords = std::max(mesh.scratch_size_dwords, back.scratch_size_dwords);
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(regs.gs_regs.rsrc1.gs_vgpr_component_count != 3u ||
|
||||
regs.gs_regs.rsrc2.es_vgpr_component_count != 3u);
|
||||
const auto& group = user_config.GetGeControl();
|
||||
if ((user_config.GetPrimType() != Prospero::PrimitiveType::kPointList &&
|
||||
user_config.GetPrimType() != Prospero::PrimitiveType::kLineList &&
|
||||
user_config.GetPrimType() != Prospero::PrimitiveType::kTriStrip &&
|
||||
user_config.GetPrimType() != Prospero::PrimitiveType::kTriList) ||
|
||||
sh.m_vgtGsOutPrimType != 2u || sh.m_vgtGsMaxVertOut < 3u ||
|
||||
|
||||
@@ -80,10 +80,15 @@ struct ShaderMeshInputInfo: ShaderWorkgroupInputInfo {
|
||||
uint32_t provoking_vertex = 0;
|
||||
|
||||
[[nodiscard]] constexpr uint32_t InputPrimitiveSize() const {
|
||||
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kPointList) ? 1u : 3u;
|
||||
switch (static_cast<Prospero::PrimitiveType>(input_primitive)) {
|
||||
case Prospero::PrimitiveType::kPointList: return 1u;
|
||||
case Prospero::PrimitiveType::kLineList: return 2u;
|
||||
default: return 3u;
|
||||
}
|
||||
}
|
||||
[[nodiscard]] constexpr uint32_t InputPrimitiveStep() const {
|
||||
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriList) ? 3u : 1u;
|
||||
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriStrip)
|
||||
? 1u : InputPrimitiveSize();
|
||||
}
|
||||
[[nodiscard]] constexpr uint32_t InputPrimitiveCount(uint32_t vertices) const {
|
||||
const auto size = InputPrimitiveSize();
|
||||
@@ -102,7 +107,6 @@ struct ShaderVertexInputInfo {
|
||||
ShaderVertexInputBuffer buffers[RES_MAX];
|
||||
ShaderStageRuntime stage;
|
||||
int resources_num = 0;
|
||||
int fetch_shader_reg = 0;
|
||||
int fetch_attrib_reg = 0;
|
||||
int fetch_buffer_reg = 0;
|
||||
int buffers_num = 0;
|
||||
@@ -135,7 +139,6 @@ struct ShaderPixelInputInfo {
|
||||
uint32_t scratch_size_dwords = 0;
|
||||
bool ps_pos_x = false;
|
||||
bool ps_pos_y = false;
|
||||
bool ps_pos_xy = false;
|
||||
bool ps_pos_z = false;
|
||||
bool ps_pos_w = false;
|
||||
bool ps_front_face = false;
|
||||
@@ -259,6 +262,7 @@ struct Shader {
|
||||
};
|
||||
|
||||
struct ShaderMappedData {
|
||||
Prospero::ShaderBinaryType type {};
|
||||
ShaderUserData* user_data = nullptr;
|
||||
ShaderSemantic* input_semantics = nullptr;
|
||||
uint32_t num_input_semantics = 0;
|
||||
|
||||
+127
-185
@@ -28,7 +28,8 @@ namespace Libs::LibKernel::FileSystem {
|
||||
|
||||
LIB_NAME("libkernel", "libkernel");
|
||||
|
||||
constexpr int DESCRIPTOR_MIN = 3;
|
||||
constexpr int DESCRIPTOR_MIN = 3;
|
||||
constexpr uint64_t DIR_BLOCK_SIZE = 512;
|
||||
|
||||
enum class SpecialFile {
|
||||
None,
|
||||
@@ -50,8 +51,7 @@ public:
|
||||
void Mount(const std::filesystem::path& folder, const std::string& point);
|
||||
void Umount(const std::string& folder_or_point);
|
||||
|
||||
[[nodiscard]] std::filesystem::path GetRealFilename(const std::string& mounted_file_name);
|
||||
[[nodiscard]] std::filesystem::path GetRealDirectory(const std::string& mounted_directory);
|
||||
[[nodiscard]] std::filesystem::path ResolvePath(const std::string& mounted_name);
|
||||
|
||||
private:
|
||||
std::vector<MountPair> m_mount_pairs;
|
||||
@@ -69,7 +69,7 @@ struct File {
|
||||
std::atomic_bool sync_writes;
|
||||
SpecialFile special;
|
||||
Common::Mutex mutex;
|
||||
std::vector<Common::File::DirEntry> dents;
|
||||
std::vector<uint8_t> dirents;
|
||||
uint64_t dents_offset;
|
||||
};
|
||||
|
||||
@@ -120,6 +120,51 @@ static uint64_t AlignUp(uint64_t value, uint64_t alignment) {
|
||||
return (alignment != 0 ? (value + alignment - 1) & ~(alignment - 1) : value);
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> PackDirents(const std::vector<Common::File::DirEntry>& entries) {
|
||||
std::vector<uint8_t> dirents;
|
||||
uint64_t offset = 0;
|
||||
uint64_t last_reclen_offset = 0;
|
||||
for (const auto& entry: entries) {
|
||||
const auto& name = entry.name;
|
||||
EXIT_NOT_IMPLEMENTED(name.size() > 255);
|
||||
const auto reclen = AlignUp(8 + name.size() + 1, 4);
|
||||
if (offset + reclen > dirents.size()) {
|
||||
if (!dirents.empty()) {
|
||||
auto* last_reclen =
|
||||
reinterpret_cast<uint16_t*>(dirents.data() + last_reclen_offset);
|
||||
*last_reclen += static_cast<uint16_t>(dirents.size() - offset);
|
||||
}
|
||||
offset = dirents.size();
|
||||
dirents.resize(offset + DIR_BLOCK_SIZE);
|
||||
}
|
||||
*reinterpret_cast<uint32_t*>(dirents.data() + offset) =
|
||||
Common::hash(name.data(), static_cast<uint32_t>(name.size()));
|
||||
*reinterpret_cast<uint16_t*>(dirents.data() + offset + 4) = static_cast<uint16_t>(reclen);
|
||||
dirents[offset + 6] = entry.is_file ? 8 : 4;
|
||||
dirents[offset + 7] = static_cast<uint8_t>(name.size());
|
||||
memcpy(dirents.data() + offset + 8, name.c_str(), name.size() + 1);
|
||||
last_reclen_offset = offset + 4;
|
||||
offset += reclen;
|
||||
}
|
||||
if (!dirents.empty()) {
|
||||
auto* last_reclen = reinterpret_cast<uint16_t*>(dirents.data() + last_reclen_offset);
|
||||
*last_reclen += static_cast<uint16_t>(dirents.size() - offset);
|
||||
}
|
||||
return dirents;
|
||||
}
|
||||
|
||||
static uint64_t ReadDirectory(File* file, void* buf, uint64_t count) {
|
||||
const auto remaining =
|
||||
file->dents_offset < file->dirents.size() ? file->dirents.size() - file->dents_offset : 0;
|
||||
const auto bytes = std::min(count, remaining);
|
||||
if (bytes != 0) {
|
||||
Memory::InvalidateMemory(reinterpret_cast<uint64_t>(buf), bytes);
|
||||
memcpy(buf, file->dirents.data() + file->dents_offset, bytes);
|
||||
file->dents_offset += bytes;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int PosixToKernel(int posix_errno) {
|
||||
return KERNEL_ERROR_UNKNOWN + posix_errno;
|
||||
}
|
||||
@@ -284,26 +329,24 @@ static bool HasWindowsForbiddenFilenameCharacter(const std::string& relative_pat
|
||||
}
|
||||
#endif
|
||||
|
||||
std::filesystem::path MountPoints::GetRealFilename(const std::string& mounted_file_name) {
|
||||
std::filesystem::path MountPoints::ResolvePath(const std::string& mounted_name) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
auto mounted_path =
|
||||
Common::DirectoryWithoutFilename(Common::FixFilenameSlash(mounted_file_name));
|
||||
|
||||
// Match the entire guest path so a mount root works with or without a trailing slash.
|
||||
const auto mounted_path = Common::FixDirectorySlash(mounted_name);
|
||||
const auto it = std::find_if(
|
||||
m_mount_pairs.begin(), m_mount_pairs.end(),
|
||||
[&mounted_path](const MountPair& p) { return Common::StartsWith(mounted_path, p.point); });
|
||||
if (it != m_mount_pairs.end()) {
|
||||
const auto& p = *it;
|
||||
auto rel_path =
|
||||
Common::RemoveFirst(Common::FixFilenameSlash(mounted_file_name), p.point.size());
|
||||
auto rel_path = Common::RemoveFirst(Common::FixFilenameSlash(mounted_name), p.point.size());
|
||||
while (Common::StartsWith(rel_path, '/')) {
|
||||
rel_path = Common::RemoveFirst(rel_path, 1);
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
if (HasWindowsForbiddenFilenameCharacter(rel_path)) {
|
||||
::printf("FileSystem: Windows-incompatible guest filename: %s\n",
|
||||
mounted_file_name.c_str());
|
||||
mounted_name.c_str());
|
||||
}
|
||||
return p.dir / rel_path;
|
||||
#else
|
||||
@@ -311,32 +354,7 @@ std::filesystem::path MountPoints::GetRealFilename(const std::string& mounted_fi
|
||||
#endif
|
||||
}
|
||||
|
||||
return mounted_file_name;
|
||||
}
|
||||
|
||||
std::filesystem::path MountPoints::GetRealDirectory(const std::string& mounted_directory) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
auto mounted_path = Common::FixDirectorySlash(mounted_directory);
|
||||
|
||||
const auto it = std::find_if(
|
||||
m_mount_pairs.begin(), m_mount_pairs.end(),
|
||||
[&mounted_path](const MountPair& p) { return Common::StartsWith(mounted_path, p.point); });
|
||||
if (it != m_mount_pairs.end()) {
|
||||
const auto& p = *it;
|
||||
auto rel_path =
|
||||
Common::RemoveFirst(Common::FixDirectorySlash(mounted_directory), p.point.size());
|
||||
while (Common::StartsWith(rel_path, '/')) {
|
||||
rel_path = Common::RemoveFirst(rel_path, 1);
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
return p.dir / rel_path;
|
||||
#else
|
||||
return ResolvePathIgnoringCase(p.dir / rel_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
return mounted_directory;
|
||||
return mounted_name;
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
@@ -370,7 +388,7 @@ void Umount(const std::string& folder_or_point) {
|
||||
|
||||
std::filesystem::path GetRealFilename(const std::string& mounted_file_name) {
|
||||
|
||||
return g_mount_points->GetRealFilename(mounted_file_name);
|
||||
return g_mount_points->ResolvePath(mounted_file_name);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
@@ -437,8 +455,7 @@ int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode) {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
file->real_name = (directory ? g_mount_points->GetRealDirectory(file->name)
|
||||
: g_mount_points->GetRealFilename(file->name));
|
||||
file->real_name = g_mount_points->ResolvePath(file->name);
|
||||
|
||||
if (trunc && rw_mode == Common::File::Mode::Read) {
|
||||
return KERNEL_ERROR_EACCES;
|
||||
@@ -462,15 +479,16 @@ int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode) {
|
||||
EXIT_NOT_IMPLEMENTED(!directory && rw_mode != Common::File::Mode::Read);
|
||||
EXIT_NOT_IMPLEMENTED(!directory && (trunc || creat));
|
||||
|
||||
file->dents = Common::File::GetDirEntries(file->real_name);
|
||||
const auto entries = Common::File::GetDirEntries(file->real_name);
|
||||
file->dirents = PackDirents(entries);
|
||||
file->dents_offset = 0;
|
||||
file->directory = true;
|
||||
|
||||
LOGF_COLOR(Log::Color::Green, "\tOpen dir: %s, entries = %" PRIu32 ", [ok]\n",
|
||||
Common::PathToString(file->real_name).c_str(),
|
||||
static_cast<uint32_t>(file->dents.size()));
|
||||
static_cast<uint32_t>(entries.size()));
|
||||
|
||||
for (const auto& f: file->dents) {
|
||||
for (const auto& f: entries) {
|
||||
LOGF("\t\t%s %s\n", f.is_file ? "[file]" : "[dir ]", f.name.c_str());
|
||||
}
|
||||
} else {
|
||||
@@ -567,11 +585,18 @@ int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
|
||||
if (nbytes > INT_MAX) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (file->directory) {
|
||||
Common::LockGuard lock(file->mutex);
|
||||
const auto bytes = ReadDirectory(file, buf, nbytes);
|
||||
LOGF("\tRead %" PRIu64 " directory bytes from: %s\n", bytes,
|
||||
Common::PathToString(file->real_name).c_str());
|
||||
return static_cast<int64_t>(bytes);
|
||||
}
|
||||
|
||||
if (file->special == SpecialFile::Random) {
|
||||
FillRandomBuffer(buf, nbytes);
|
||||
@@ -790,7 +815,7 @@ int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (d < DESCRIPTOR_MIN) {
|
||||
return KERNEL_ERROR_EPERM;
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
@@ -799,8 +824,6 @@ int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
if (file->special != SpecialFile::None) {
|
||||
@@ -809,38 +832,47 @@ int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence) {
|
||||
|
||||
Common::LockGuard lock(file->mutex);
|
||||
|
||||
bool is_invalid = file->f.IsInvalid();
|
||||
|
||||
if (whence == 1) {
|
||||
offset = static_cast<int64_t>(file->f.Tell()) + offset;
|
||||
whence = 0;
|
||||
}
|
||||
|
||||
if (whence == 2) {
|
||||
offset = static_cast<int64_t>(file->f.Size()) + offset;
|
||||
whence = 0;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(whence != 0);
|
||||
|
||||
if (offset < 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
file->f.Seek(offset);
|
||||
auto pos = static_cast<int64_t>(file->f.Tell());
|
||||
|
||||
EXIT_IF(pos != offset);
|
||||
|
||||
if (is_invalid) {
|
||||
if (!file->directory && file->f.IsInvalid()) {
|
||||
LOGF("\tfile is invalid\n");
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
LOGF("\tLseek (pos = %" PRId64 ") to: %s\n", offset,
|
||||
uint64_t base = 0;
|
||||
switch (whence) {
|
||||
case 0: break;
|
||||
case 1: base = file->directory ? file->dents_offset : file->f.Tell(); break;
|
||||
case 2: base = file->directory ? file->dirents.size() : file->f.Size(); break;
|
||||
default: return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
uint64_t position = 0;
|
||||
if (offset >= 0) {
|
||||
if (base > static_cast<uint64_t>(INT64_MAX - offset)) {
|
||||
return KERNEL_ERROR_EOVERFLOW;
|
||||
}
|
||||
position = base + static_cast<uint64_t>(offset);
|
||||
} else {
|
||||
const auto distance = uint64_t {0} - static_cast<uint64_t>(offset);
|
||||
if (base < distance) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
position = base - distance;
|
||||
if (position > INT64_MAX) {
|
||||
return KERNEL_ERROR_EOVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
if (file->directory) {
|
||||
file->dents_offset = position;
|
||||
} else {
|
||||
file->f.Seek(position);
|
||||
EXIT_IF(file->f.Tell() != position);
|
||||
}
|
||||
|
||||
LOGF("\tLseek (pos = %" PRIu64 ") to: %s\n", position,
|
||||
Common::PathToString(file->real_name).c_str());
|
||||
|
||||
return pos;
|
||||
return static_cast<int64_t>(position);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb) {
|
||||
@@ -852,12 +884,9 @@ int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb) {
|
||||
|
||||
LOGF("\t KernelStat: %s\n", path);
|
||||
|
||||
std::string path_s = std::string(path);
|
||||
auto real_file_name = g_mount_points->GetRealFilename(path_s);
|
||||
auto real_directory = g_mount_points->GetRealDirectory(path_s);
|
||||
auto real_file_name = g_mount_points->ResolvePath(path);
|
||||
|
||||
bool is_dir = Common::File::IsDirectoryExisting(real_file_name) ||
|
||||
Common::File::IsDirectoryExisting(real_directory);
|
||||
bool is_dir = Common::File::IsDirectoryExisting(real_file_name);
|
||||
bool is_file = Common::File::IsFileExisting(real_file_name);
|
||||
|
||||
if (!is_dir && !is_file) {
|
||||
@@ -874,9 +903,10 @@ int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb) {
|
||||
auto wt = at;
|
||||
|
||||
if (is_dir) {
|
||||
stat.st_size = 0;
|
||||
stat.st_size =
|
||||
static_cast<int64_t>(PackDirents(Common::File::GetDirEntries(real_file_name)).size());
|
||||
stat.st_blksize = 512;
|
||||
stat.st_blocks = 0;
|
||||
stat.st_blocks = stat.st_size / 512;
|
||||
} else {
|
||||
stat.st_size = static_cast<int64_t>(Common::File::Size(real_file_name));
|
||||
stat.st_blksize = 512;
|
||||
@@ -952,9 +982,9 @@ int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb) {
|
||||
stat.st_blksize = 512;
|
||||
stat.st_blocks = (stat.st_size + 511) / 512;
|
||||
} else {
|
||||
stat.st_size = 0;
|
||||
stat.st_size = static_cast<int64_t>(file->dirents.size());
|
||||
stat.st_blksize = 512;
|
||||
stat.st_blocks = 0;
|
||||
stat.st_blocks = stat.st_size / 512;
|
||||
}
|
||||
|
||||
SecToTimespec(&stat.st_atim, at.ToUnix());
|
||||
@@ -1014,12 +1044,9 @@ int KYTY_SYSV_ABI KernelUnlink(const char* path) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto path_s = std::string(path);
|
||||
auto real_file_name = g_mount_points->GetRealFilename(path_s);
|
||||
auto real_directory = g_mount_points->GetRealDirectory(path_s);
|
||||
auto real_file_name = g_mount_points->ResolvePath(path);
|
||||
|
||||
bool is_dir = Common::File::IsDirectoryExisting(real_file_name) ||
|
||||
Common::File::IsDirectoryExisting(real_directory);
|
||||
bool is_dir = Common::File::IsDirectoryExisting(real_file_name);
|
||||
bool is_file = Common::File::IsFileExisting(real_file_name);
|
||||
|
||||
if (is_dir) {
|
||||
@@ -1053,8 +1080,8 @@ int KYTY_SYSV_ABI KernelRename(const char* from, const char* to) {
|
||||
|
||||
auto from_path = std::string(from);
|
||||
auto to_path = std::string(to);
|
||||
auto real_from = g_mount_points->GetRealFilename(from_path);
|
||||
auto real_to = g_mount_points->GetRealFilename(to_path);
|
||||
auto real_from = g_mount_points->ResolvePath(from_path);
|
||||
auto real_to = g_mount_points->ResolvePath(to_path);
|
||||
|
||||
if (!Common::File::IsFileExisting(real_from)) {
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
@@ -1092,14 +1119,15 @@ int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* ba
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
constexpr uint64_t DIR_BLOCK_SIZE = 512;
|
||||
constexpr uint64_t DIRENT_META_SIZE = 8;
|
||||
|
||||
if (!file->directory || nbytes < static_cast<int>(DIR_BLOCK_SIZE)) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
Common::LockGuard lock(file->mutex);
|
||||
if (file->dents_offset > file->dirents.size() || file->dents_offset % DIR_BLOCK_SIZE != 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
LOGF("\t dir = %s\n"
|
||||
"\t nbytes = %d\n"
|
||||
@@ -1109,94 +1137,8 @@ int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* ba
|
||||
if (basep != nullptr) {
|
||||
*basep = static_cast<int64_t>(file->dents_offset);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> dirents;
|
||||
dirents.reserve(
|
||||
std::max<size_t>(static_cast<size_t>(DIR_BLOCK_SIZE), file->dents.size() * 64u));
|
||||
|
||||
uint64_t next_ceiling = 0;
|
||||
uint64_t dirent_offset = 0;
|
||||
int64_t last_reclen_offset = -1;
|
||||
|
||||
for (const auto& entry: file->dents) {
|
||||
const auto& str = entry.name;
|
||||
auto str_size = str.size();
|
||||
EXIT_NOT_IMPLEMENTED(str_size > 255);
|
||||
|
||||
uint64_t reclen = AlignUp(DIRENT_META_SIZE + str_size + 1, 4);
|
||||
if (dirent_offset + reclen > next_ceiling) {
|
||||
if (last_reclen_offset >= 0) {
|
||||
auto* last_reclen =
|
||||
reinterpret_cast<uint16_t*>(dirents.data() + last_reclen_offset);
|
||||
*last_reclen = static_cast<uint16_t>(*last_reclen + (next_ceiling - dirent_offset));
|
||||
}
|
||||
|
||||
dirent_offset = next_ceiling;
|
||||
next_ceiling += DIR_BLOCK_SIZE;
|
||||
dirents.resize(next_ceiling);
|
||||
std::fill(dirents.begin() + static_cast<std::ptrdiff_t>(dirent_offset), dirents.end(),
|
||||
0);
|
||||
}
|
||||
|
||||
*reinterpret_cast<uint32_t*>(dirents.data() + dirent_offset + 0) =
|
||||
Common::hash(str.data(), static_cast<uint32_t>(str.size()));
|
||||
*reinterpret_cast<uint16_t*>(dirents.data() + dirent_offset + 4) =
|
||||
static_cast<uint16_t>(reclen);
|
||||
*reinterpret_cast<uint8_t*>(dirents.data() + dirent_offset + 6) = (entry.is_file ? 8 : 4);
|
||||
*reinterpret_cast<uint8_t*>(dirents.data() + dirent_offset + 7) =
|
||||
static_cast<uint8_t>(str_size);
|
||||
memcpy(dirents.data() + dirent_offset + 8, str.data(), str_size);
|
||||
dirents[dirent_offset + 8 + str_size] = '\0';
|
||||
|
||||
last_reclen_offset = static_cast<int64_t>(dirent_offset + 4);
|
||||
dirent_offset += reclen;
|
||||
|
||||
LOGF("\t name = %s\n", str.data());
|
||||
}
|
||||
|
||||
if (last_reclen_offset >= 0) {
|
||||
auto* last_reclen = reinterpret_cast<uint16_t*>(dirents.data() + last_reclen_offset);
|
||||
*last_reclen = static_cast<uint16_t>(*last_reclen + (next_ceiling - dirent_offset));
|
||||
}
|
||||
|
||||
const uint64_t directory_size = next_ceiling;
|
||||
if (file->dents_offset >= directory_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t bytes_written = 0;
|
||||
uint64_t working_offset = file->dents_offset;
|
||||
uint64_t dirent_buffer_offset = 0;
|
||||
uint64_t aligned_count = AlignDown(static_cast<uint64_t>(nbytes), DIR_BLOCK_SIZE);
|
||||
|
||||
while (dirent_buffer_offset < dirents.size()) {
|
||||
const auto* normal_dirent = dirents.data() + dirent_buffer_offset;
|
||||
uint16_t reclen = *reinterpret_cast<const uint16_t*>(normal_dirent + 4);
|
||||
uint8_t namlen = *(normal_dirent + 7);
|
||||
|
||||
if (namlen == 0 || reclen == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (working_offset >= reclen) {
|
||||
dirent_buffer_offset += reclen;
|
||||
working_offset -= reclen;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bytes_written + reclen > aligned_count) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto bytes_to_copy = reclen - working_offset;
|
||||
memcpy(buf + bytes_written, normal_dirent + working_offset, bytes_to_copy);
|
||||
bytes_written += bytes_to_copy;
|
||||
dirent_buffer_offset += reclen;
|
||||
working_offset = 0;
|
||||
}
|
||||
|
||||
file->dents_offset += bytes_written;
|
||||
return static_cast<int64_t>(bytes_written);
|
||||
return static_cast<int>(
|
||||
ReadDirectory(file, buf, AlignDown(static_cast<uint64_t>(nbytes), DIR_BLOCK_SIZE)));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelGetdents(int fd, char* buf, int nbytes) {
|
||||
@@ -1220,7 +1162,7 @@ int KYTY_SYSV_ABI KernelMkdir(const char* path, uint16_t mode) {
|
||||
"\t mode = %04" PRIx16 "\n",
|
||||
path, mode);
|
||||
|
||||
auto real_name = g_mount_points->GetRealDirectory(std::string(path));
|
||||
auto real_name = g_mount_points->ResolvePath(std::string(path));
|
||||
|
||||
if (Common::File::IsDirectoryExisting(real_name)) {
|
||||
return KERNEL_ERROR_EEXIST;
|
||||
@@ -1246,7 +1188,7 @@ int KYTY_SYSV_ABI KernelRmdir(const char* path) {
|
||||
|
||||
LOGF("\t path = %s\n", path);
|
||||
|
||||
auto real_name = g_mount_points->GetRealDirectory(std::string(path));
|
||||
auto real_name = g_mount_points->ResolvePath(std::string(path));
|
||||
|
||||
if (!Common::File::IsDirectoryExisting(real_name)) {
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
@@ -1278,7 +1220,7 @@ int KYTY_SYSV_ABI KernelCheckReachability(const char* path) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
auto real_name = g_mount_points->GetRealFilename(mounted_path);
|
||||
auto real_name = g_mount_points->ResolvePath(mounted_path);
|
||||
|
||||
if (Common::File::IsFileExisting(real_name) || Common::File::IsDirectoryExisting(real_name)) {
|
||||
return OK;
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
#include <QToolButton>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtAlgorithms>
|
||||
|
||||
#if QT_CONFIG(vulkan)
|
||||
#include <QVulkanInstance>
|
||||
#include <QVulkanWindow>
|
||||
#include <QtAlgorithms>
|
||||
#endif
|
||||
|
||||
#include "ui_configuration_edit_dialog.h"
|
||||
|
||||
@@ -168,6 +171,8 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||
ListInit(m_ui->comboBox_present_mode, info.present_mode);
|
||||
m_ui->comboBox_gpu->clear();
|
||||
m_ui->comboBox_gpu->addItem(tr("Auto"));
|
||||
// Keep Auto when Qt is built without Vulkan support.
|
||||
#if QT_CONFIG(vulkan)
|
||||
#if defined(__APPLE__)
|
||||
if (!qEnvironmentVariableIsSet("QT_VULKAN_LIB")) {
|
||||
const auto base = QCoreApplication::applicationDirPath();
|
||||
@@ -192,6 +197,7 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||
m_ui->comboBox_gpu->addItem(QString::fromUtf8(device.deviceName));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
m_ui->comboBox_gpu->setCurrentIndex(
|
||||
info.gpu_index >= 0 && info.gpu_index < m_ui->comboBox_gpu->count() - 1 ? info.gpu_index + 1
|
||||
: 0);
|
||||
|
||||
@@ -662,6 +662,7 @@ int KYTY_SYSV_ABI AgcCreateShader(Shader** dst, void* header, const volatile voi
|
||||
LOGF("\t base = 0x%016" PRIx64 "\n", base);
|
||||
|
||||
ShaderMappedData map;
|
||||
map.type = static_cast<Prospero::ShaderBinaryType>(h->type);
|
||||
map.user_data = h->user_data;
|
||||
map.input_semantics = h->input_semantics;
|
||||
map.num_input_semantics = h->num_input_semantics;
|
||||
|
||||
+46
-177
@@ -101,109 +101,23 @@ static uint32_t ComputeFileId(const char* guest_path) {
|
||||
return hash & static_cast<uint32_t>(std::numeric_limits<int32_t>::max());
|
||||
}
|
||||
|
||||
static bool CopyStringToOutput(const std::string& str, char* out, size_t out_size) {
|
||||
if (str.empty() || str.size() + 1 > out_size) {
|
||||
return false;
|
||||
static int ReadGuestCString(uint64_t addr, char* out, size_t out_size) {
|
||||
if (addr == 0) {
|
||||
return LibKernel::KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
std::memcpy(out, str.c_str(), str.size() + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ReadGuestCString(uint64_t addr, char* out, size_t out_size) {
|
||||
if (out == nullptr || out_size == 0 || addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto max_len = static_cast<uint64_t>(out_size - 1);
|
||||
const auto* src = reinterpret_cast<const char*>(static_cast<uintptr_t>(addr));
|
||||
for (uint64_t pos = 0; pos < max_len; pos++) {
|
||||
const auto* src = reinterpret_cast<const char*>(addr);
|
||||
for (size_t pos = 0; pos < out_size; pos++) {
|
||||
if (addr > std::numeric_limits<uint64_t>::max() - pos) {
|
||||
out[pos] = '\0';
|
||||
return false;
|
||||
return LibKernel::KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
const auto ch = src[pos];
|
||||
out[pos] = ch;
|
||||
if (ch == '\0') {
|
||||
return pos != 0;
|
||||
out[pos] = src[pos];
|
||||
if (out[pos] == '\0') {
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
out[max_len] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ReadGuestWideCString(uint64_t addr, char* out, size_t out_size) {
|
||||
if (out == nullptr || out_size == 0 || addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<char16_t, 1024> tmp {};
|
||||
const auto max_len = std::min<uint64_t>(tmp.size() - 1, out_size - 1);
|
||||
const auto* src = reinterpret_cast<const char*>(static_cast<uintptr_t>(addr));
|
||||
for (uint64_t pos = 0; pos < max_len; pos++) {
|
||||
if (pos > (std::numeric_limits<uint64_t>::max() - addr) / sizeof(char16_t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char16_t ch = 0;
|
||||
std::memcpy(&ch, src + pos * sizeof(char16_t), sizeof(ch));
|
||||
tmp[static_cast<size_t>(pos)] = ch;
|
||||
if (ch == u'\0') {
|
||||
return pos != 0 && CopyStringToOutput(Common::Utf16ToUtf8(tmp.data()), out, out_size);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ReadGuestPathText(uint64_t addr, char* out, size_t out_size) {
|
||||
std::array<char, 1024> narrow {};
|
||||
if (ReadGuestCString(addr, narrow.data(), std::min(narrow.size(), out_size))) {
|
||||
const auto narrow_len = std::strlen(narrow.data());
|
||||
if (narrow_len > 1 || !ReadGuestWideCString(addr, out, out_size)) {
|
||||
std::memcpy(out, narrow.data(), std::strlen(narrow.data()) + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ReadGuestWideCString(addr, out, out_size)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ReadPathPointer(uint64_t pointer_addr, char* out, size_t out_size) {
|
||||
uint64_t path_addr = 0;
|
||||
if (!ReadGuest(pointer_addr, &path_addr)) {
|
||||
return false;
|
||||
}
|
||||
if (path_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
if (ReadGuestPathText(path_addr, out, out_size)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ResolvePathFromList(uint64_t path_list, uint64_t index, char* out, size_t out_size) {
|
||||
if (ReadPathPointer(path_list + index * sizeof(uint64_t), out, out_size)) {
|
||||
return true;
|
||||
}
|
||||
if (index != 0) {
|
||||
return false;
|
||||
}
|
||||
if (ReadGuestPathText(path_list, out, out_size)) {
|
||||
return true;
|
||||
}
|
||||
for (uint64_t offset = 0; offset < 0x40; offset += sizeof(uint64_t)) {
|
||||
if (ReadPathPointer(path_list + offset, out, out_size)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return LibKernel::KERNEL_ERROR_ENAMETOOLONG;
|
||||
}
|
||||
|
||||
static void RegisterHostPathLocked(uint32_t file_id, const std::string& host_path,
|
||||
@@ -280,36 +194,6 @@ static int GetHostPathStat(const std::string& host_path, LibKernel::FileSystem::
|
||||
return OK;
|
||||
}
|
||||
|
||||
static bool JoinPrefixPath(const char* prefix, const char* path, char* out, size_t out_size) {
|
||||
if (path == nullptr || out == nullptr || out_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prefix == nullptr || prefix[0] == '\0') {
|
||||
std::strncpy(out, path, out_size - 1);
|
||||
out[out_size - 1] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto prefix_len = std::strlen(prefix);
|
||||
const auto path_len = std::strlen(path);
|
||||
const bool needs_sep = prefix_len != 0 && path_len != 0 && prefix[prefix_len - 1] != '/' &&
|
||||
prefix[prefix_len - 1] != '\\' && path[0] != '/' && path[0] != '\\';
|
||||
const auto total_len = prefix_len + (needs_sep ? 1u : 0u) + path_len;
|
||||
if (total_len + 1u > out_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(out, prefix, prefix_len);
|
||||
auto pos = prefix_len;
|
||||
if (needs_sep) {
|
||||
out[pos++] = '/';
|
||||
}
|
||||
std::memcpy(out + pos, path, path_len);
|
||||
out[total_len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size) {
|
||||
if (guest_path == nullptr || guest_path[0] == '\0') {
|
||||
return LibKernel::KERNEL_ERROR_EINVAL;
|
||||
@@ -381,8 +265,8 @@ static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size)
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int ResolvePathsCommon(const void* path_list, uint64_t count, uint32_t* ids, uint64_t* sizes,
|
||||
uint32_t* error_index, const char* prefix, int* results) {
|
||||
static int ResolvePathsCommon(const char* const* path_list, uint32_t count, uint32_t* ids,
|
||||
uint64_t* sizes, uint32_t* error_index, const char* prefix, int* results) {
|
||||
if (path_list == nullptr || count == 0 || count > 1024 ||
|
||||
(ids == nullptr && sizes == nullptr && results == nullptr)) {
|
||||
return LibKernel::KERNEL_ERROR_EINVAL;
|
||||
@@ -406,18 +290,27 @@ static int ResolvePathsCommon(const void* path_list, uint64_t count, uint32_t* i
|
||||
return LibKernel::KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
const auto path_list_addr = reinterpret_cast<uint64_t>(path_list);
|
||||
bool any_error = false;
|
||||
int first_error = OK;
|
||||
uint32_t success_count = 0;
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
char guest_path[1024] {};
|
||||
char resolved_path[2048] {};
|
||||
int result = OK;
|
||||
if (!AprShared::ResolvePathFromList(path_list_addr, i, guest_path, sizeof(guest_path)) ||
|
||||
!JoinPrefixPath(prefix, guest_path, resolved_path, sizeof(resolved_path))) {
|
||||
result = LibKernel::KERNEL_ERROR_EFAULT;
|
||||
} else {
|
||||
if (!IsValidGuestRange(reinterpret_cast<uint64_t>(path_list), count * sizeof(*path_list))) {
|
||||
return LibKernel::KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
char prefix_buf[1024] {};
|
||||
if (prefix != nullptr) {
|
||||
const int result = ReadGuestCString(reinterpret_cast<uint64_t>(prefix), prefix_buf,
|
||||
sizeof(prefix_buf));
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
const auto prefix_size = std::strlen(prefix_buf);
|
||||
uint32_t success_count = 0;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
char resolved_path[1024] {};
|
||||
// Concatenate the prefix literally, including an empty prefix.
|
||||
std::memcpy(resolved_path, prefix_buf, prefix_size);
|
||||
int result = ReadGuestCString(reinterpret_cast<uint64_t>(path_list[i]),
|
||||
resolved_path + prefix_size, sizeof(resolved_path) - prefix_size);
|
||||
if (result == OK) {
|
||||
result = ResolveOnePath(resolved_path, ids != nullptr ? &ids[i] : nullptr,
|
||||
sizes != nullptr ? &sizes[i] : nullptr);
|
||||
}
|
||||
@@ -437,17 +330,13 @@ static int ResolvePathsCommon(const void* path_list, uint64_t count, uint32_t* i
|
||||
if (error_index != nullptr) {
|
||||
*error_index = static_cast<uint32_t>(i);
|
||||
}
|
||||
if (!any_error) {
|
||||
any_error = true;
|
||||
first_error = result;
|
||||
}
|
||||
if (results == nullptr) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results != nullptr ? static_cast<int>(success_count) : (any_error ? first_error : OK);
|
||||
return results != nullptr ? static_cast<int>(success_count) : OK;
|
||||
}
|
||||
|
||||
static uint32_t AllocateSubmissionId(uint64_t command_buffer, uint64_t result) {
|
||||
@@ -521,7 +410,7 @@ static int KernelSyscallResult(int result) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsToIds(const void* path_list, uint32_t count, uint32_t* ids,
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsToIds(const char* const* path_list, uint32_t count, uint32_t* ids,
|
||||
uint32_t* error_index) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -582,7 +471,7 @@ static int KYTY_SYSV_ABI GetFileSize(uint32_t file_id, uint64_t* size) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizes(const void* path_list, uint32_t count,
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizes(const char* const* path_list, uint32_t count,
|
||||
uint32_t* ids, uint64_t* sizes,
|
||||
uint32_t* error_index) {
|
||||
PRINT_NAME();
|
||||
@@ -591,37 +480,27 @@ static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizes(const void* path_list
|
||||
AprShared::ResolvePathsCommon(path_list, count, ids, sizes, error_index, nullptr, nullptr));
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIds(const char* prefix, const void* path_list,
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIds(const char* prefix, const char* const* path_list,
|
||||
uint32_t count, uint32_t* ids,
|
||||
uint32_t* error_index) {
|
||||
PRINT_NAME();
|
||||
|
||||
char prefix_buf[1024] {};
|
||||
if (prefix != nullptr && !AprShared::ReadGuestPathText(reinterpret_cast<uint64_t>(prefix),
|
||||
prefix_buf, sizeof(prefix_buf))) {
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
|
||||
}
|
||||
return KernelSyscallResult(AprShared::ResolvePathsCommon(path_list, count, ids, nullptr,
|
||||
error_index, prefix_buf, nullptr));
|
||||
error_index, prefix, nullptr));
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIdsAndFileSizes(const char* prefix,
|
||||
const void* path_list,
|
||||
const char* const* path_list,
|
||||
uint32_t count, uint32_t* ids,
|
||||
uint64_t* sizes,
|
||||
uint32_t* error_index) {
|
||||
PRINT_NAME();
|
||||
|
||||
char prefix_buf[1024] {};
|
||||
if (prefix != nullptr && !AprShared::ReadGuestPathText(reinterpret_cast<uint64_t>(prefix),
|
||||
prefix_buf, sizeof(prefix_buf))) {
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
|
||||
}
|
||||
return KernelSyscallResult(AprShared::ResolvePathsCommon(path_list, count, ids, sizes,
|
||||
error_index, prefix_buf, nullptr));
|
||||
error_index, prefix, nullptr));
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsToIdsForEach(const void* path_list, uint32_t count,
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsToIdsForEach(const char* const* path_list, uint32_t count,
|
||||
uint32_t* ids, int* results) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -629,7 +508,7 @@ static int KYTY_SYSV_ABI ResolveFilepathsToIdsForEach(const void* path_list, uin
|
||||
AprShared::ResolvePathsCommon(path_list, count, ids, nullptr, nullptr, nullptr, results));
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizesForEach(const void* path_list,
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizesForEach(const char* const* path_list,
|
||||
uint32_t count, uint32_t* ids,
|
||||
uint64_t* sizes, int* results) {
|
||||
PRINT_NAME();
|
||||
@@ -639,32 +518,22 @@ static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizesForEach(const void* pa
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIdsForEach(const char* prefix,
|
||||
const void* path_list,
|
||||
const char* const* path_list,
|
||||
uint32_t count, uint32_t* ids,
|
||||
int* results) {
|
||||
PRINT_NAME();
|
||||
|
||||
char prefix_buf[1024] {};
|
||||
if (prefix != nullptr && !AprShared::ReadGuestPathText(reinterpret_cast<uint64_t>(prefix),
|
||||
prefix_buf, sizeof(prefix_buf))) {
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
|
||||
}
|
||||
return KernelSyscallResult(AprShared::ResolvePathsCommon(path_list, count, ids, nullptr,
|
||||
nullptr, prefix_buf, results));
|
||||
nullptr, prefix, results));
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIdsAndFileSizesForEach(
|
||||
const char* prefix, const void* path_list, uint32_t count, uint32_t* ids, uint64_t* sizes,
|
||||
const char* prefix, const char* const* path_list, uint32_t count, uint32_t* ids, uint64_t* sizes,
|
||||
int* results) {
|
||||
PRINT_NAME();
|
||||
|
||||
char prefix_buf[1024] {};
|
||||
if (prefix != nullptr && !AprShared::ReadGuestPathText(reinterpret_cast<uint64_t>(prefix),
|
||||
prefix_buf, sizeof(prefix_buf))) {
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
|
||||
}
|
||||
return KernelSyscallResult(
|
||||
AprShared::ResolvePathsCommon(path_list, count, ids, sizes, nullptr, prefix_buf, results));
|
||||
AprShared::ResolvePathsCommon(path_list, count, ids, sizes, nullptr, prefix, results));
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI SubmitCommandBufferAndGetResult(void* command_buffer, uint64_t,
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
#include "common/abi.h"
|
||||
#include "common/assert.h"
|
||||
#include "libs/libs.h"
|
||||
#include "loader/symbolDatabase.h"
|
||||
|
||||
#include <SDL_stdinc.h>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
namespace Libs {
|
||||
|
||||
namespace LibCes {
|
||||
|
||||
LIB_VERSION("Ces", 1, "Ces", 1, 1);
|
||||
|
||||
static const uint8_t* KYTY_SYSV_ABI CesRefersUcsProfileCp1252() {
|
||||
PRINT_NAME();
|
||||
|
||||
static const uint8_t profile = 0;
|
||||
return &profile;
|
||||
}
|
||||
|
||||
static uint32_t CesDecodeUtf8(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len) {
|
||||
if (utf8 == nullptr || utf8max == 0) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 0;
|
||||
}
|
||||
return 0xfffd;
|
||||
}
|
||||
|
||||
const uint8_t c0 = utf8[0];
|
||||
if (c0 < 0x80) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 1;
|
||||
}
|
||||
return c0;
|
||||
}
|
||||
if ((c0 & 0xe0) == 0xc0 && utf8max >= 2 && (utf8[1] & 0xc0) == 0x80) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 2;
|
||||
}
|
||||
return ((c0 & 0x1f) << 6u) | (utf8[1] & 0x3fu);
|
||||
}
|
||||
if ((c0 & 0xf0) == 0xe0 && utf8max >= 3 && (utf8[1] & 0xc0) == 0x80 &&
|
||||
(utf8[2] & 0xc0) == 0x80) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 3;
|
||||
}
|
||||
return ((c0 & 0x0f) << 12u) | ((utf8[1] & 0x3fu) << 6u) | (utf8[2] & 0x3fu);
|
||||
}
|
||||
if ((c0 & 0xf8) == 0xf0 && utf8max >= 4 && (utf8[1] & 0xc0) == 0x80 &&
|
||||
(utf8[2] & 0xc0) == 0x80 && (utf8[3] & 0xc0) == 0x80) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 4;
|
||||
}
|
||||
return ((c0 & 0x07) << 18u) | ((utf8[1] & 0x3fu) << 12u) | ((utf8[2] & 0x3fu) << 6u) |
|
||||
(utf8[3] & 0x3fu);
|
||||
}
|
||||
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 1;
|
||||
}
|
||||
return 0xfffd;
|
||||
}
|
||||
|
||||
struct Cp1252Mapping {
|
||||
uint8_t cp1252;
|
||||
uint32_t unicode;
|
||||
};
|
||||
|
||||
static constexpr Cp1252Mapping CP1252_EXTENDED_MAP[] = {
|
||||
{0x80, 0x20ac}, {0x82, 0x201a}, {0x83, 0x0192}, {0x84, 0x201e}, {0x85, 0x2026}, {0x86, 0x2020},
|
||||
{0x87, 0x2021}, {0x88, 0x02c6}, {0x89, 0x2030}, {0x8a, 0x0160}, {0x8b, 0x2039}, {0x8c, 0x0152},
|
||||
{0x8e, 0x017d}, {0x91, 0x2018}, {0x92, 0x2019}, {0x93, 0x201c}, {0x94, 0x201d}, {0x95, 0x2022},
|
||||
{0x96, 0x2013}, {0x97, 0x2014}, {0x98, 0x02dc}, {0x99, 0x2122}, {0x9a, 0x0161}, {0x9b, 0x203a},
|
||||
{0x9c, 0x0153}, {0x9e, 0x017e}, {0x9f, 0x0178},
|
||||
};
|
||||
|
||||
static uint8_t CesUnicodeToCp1252(uint32_t code) {
|
||||
if (code <= 0x7f || (code >= 0xa0 && code <= 0xff)) {
|
||||
return static_cast<uint8_t>(code);
|
||||
}
|
||||
|
||||
for (const auto& map: CP1252_EXTENDED_MAP) {
|
||||
if (map.unicode == code) {
|
||||
return map.cp1252;
|
||||
}
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
static uint32_t CesCp1252ToUnicode(uint8_t sbc) {
|
||||
if (sbc <= 0x7f || sbc >= 0xa0) {
|
||||
return sbc;
|
||||
}
|
||||
|
||||
for (const auto& map: CP1252_EXTENDED_MAP) {
|
||||
if (map.cp1252 == sbc) {
|
||||
return map.unicode;
|
||||
}
|
||||
}
|
||||
return sbc;
|
||||
}
|
||||
|
||||
static uint32_t CesEncodeUtf8(uint32_t code, uint8_t* utf8, uint32_t utf8max) {
|
||||
uint8_t tmp[4] {};
|
||||
uint32_t len = 0;
|
||||
|
||||
if (code <= 0x7f) {
|
||||
tmp[0] = static_cast<uint8_t>(code);
|
||||
len = 1;
|
||||
} else if (code <= 0x7ff) {
|
||||
tmp[0] = static_cast<uint8_t>(0xc0u | ((code >> 6u) & 0x1fu));
|
||||
tmp[1] = static_cast<uint8_t>(0x80u | (code & 0x3fu));
|
||||
len = 2;
|
||||
} else if (code <= 0xffff) {
|
||||
tmp[0] = static_cast<uint8_t>(0xe0u | ((code >> 12u) & 0x0fu));
|
||||
tmp[1] = static_cast<uint8_t>(0x80u | ((code >> 6u) & 0x3fu));
|
||||
tmp[2] = static_cast<uint8_t>(0x80u | (code & 0x3fu));
|
||||
len = 3;
|
||||
} else {
|
||||
tmp[0] = static_cast<uint8_t>(0xf0u | ((code >> 18u) & 0x07u));
|
||||
tmp[1] = static_cast<uint8_t>(0x80u | ((code >> 12u) & 0x3fu));
|
||||
tmp[2] = static_cast<uint8_t>(0x80u | ((code >> 6u) & 0x3fu));
|
||||
tmp[3] = static_cast<uint8_t>(0x80u | (code & 0x3fu));
|
||||
len = 4;
|
||||
}
|
||||
|
||||
if (utf8 != nullptr && utf8max >= len) {
|
||||
std::memcpy(utf8, tmp, len);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI CesUtf8ToSbc(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len,
|
||||
const uint8_t* profile, uint8_t* sbc) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (sbc == nullptr || profile == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t local_len = 0;
|
||||
const auto code = CesDecodeUtf8(utf8, utf8max, &local_len);
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = local_len;
|
||||
}
|
||||
*sbc = CesUnicodeToCp1252(code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI CesSbcToUtf8(const uint8_t* profile, uint8_t sbc, uint8_t* utf8,
|
||||
uint32_t utf8max, uint32_t* utf8_len) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (profile == nullptr || utf8 == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto code = CesCp1252ToUnicode(sbc);
|
||||
const auto len = CesEncodeUtf8(code, utf8, utf8max);
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = len;
|
||||
}
|
||||
|
||||
return utf8max >= len ? 0 : -1;
|
||||
}
|
||||
|
||||
// Profile sheets and contexts each reserve 32 pointers for library-private state.
|
||||
struct CesProfile {
|
||||
const char* encoding;
|
||||
const void* reserved[31];
|
||||
};
|
||||
|
||||
struct CesContext {
|
||||
const CesProfile* profile;
|
||||
const void* reserved[31];
|
||||
};
|
||||
|
||||
static_assert(sizeof(CesProfile) == 256 && alignof(CesProfile) == 8);
|
||||
static_assert(sizeof(CesContext) == 256 && alignof(CesContext) == 8);
|
||||
|
||||
static constexpr char CP932[] = "CP932";
|
||||
static constexpr int CES_ERROR_INVALID_PARAMETER = static_cast<int>(0x805c0001u);
|
||||
static constexpr int CES_ERROR_INVALID_PROFILE = static_cast<int>(0x805c0004u);
|
||||
static constexpr int CES_ERROR_INVALID_SRC_BUFFER = static_cast<int>(0x805c0010u);
|
||||
static constexpr int CES_ERROR_SRC_BUFFER_END = static_cast<int>(0x805c0011u);
|
||||
static constexpr int CES_ERROR_INVALID_ENCODE = static_cast<int>(0x805c0014u);
|
||||
static constexpr int CES_ERROR_UNASSIGNED_CODE = static_cast<int>(0x805c0020u);
|
||||
static constexpr int CES_ERROR_INVALID_DST_BUFFER = static_cast<int>(0x805c0030u);
|
||||
static constexpr int CES_ERROR_DST_BUFFER_END = static_cast<int>(0x805c0031u);
|
||||
|
||||
static CesProfile* KYTY_SYSV_ABI CesUcsProfileInitSJis1997Cp932(CesProfile* sheet) {
|
||||
PRINT_NAME();
|
||||
if (sheet != nullptr) {
|
||||
*sheet = {};
|
||||
sheet->encoding = CP932;
|
||||
}
|
||||
return sheet;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI CesMbcsUcsContextInit(CesContext* context, const CesProfile* profile) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (context == nullptr || profile == nullptr) {
|
||||
return CES_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
*context = {};
|
||||
context->profile = profile;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ConvertMbcsToUtf8(CesContext* context, const uint8_t* source, uint32_t source_max,
|
||||
uint32_t* source_len, uint8_t* destination, uint32_t destination_max,
|
||||
uint32_t* destination_len, bool measure) {
|
||||
if (source_len != nullptr) {
|
||||
*source_len = 0;
|
||||
}
|
||||
if (destination_len != nullptr) {
|
||||
*destination_len = 0;
|
||||
}
|
||||
if (destination != nullptr && destination_max != 0) {
|
||||
destination[0] = 0;
|
||||
}
|
||||
if (context == nullptr) {
|
||||
return CES_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
if (context->profile == nullptr || context->profile->encoding != CP932) {
|
||||
return CES_ERROR_INVALID_PROFILE;
|
||||
}
|
||||
if (source == nullptr) {
|
||||
return CES_ERROR_INVALID_SRC_BUFFER;
|
||||
}
|
||||
if (!measure && destination == nullptr) {
|
||||
return CES_ERROR_INVALID_DST_BUFFER;
|
||||
}
|
||||
if (!measure && destination_max == 0) {
|
||||
return CES_ERROR_DST_BUFFER_END;
|
||||
}
|
||||
|
||||
size_t source_size = 0;
|
||||
while ((source_max == 0 || source_size < source_max) && source[source_size] != 0) {
|
||||
++source_size;
|
||||
}
|
||||
const bool bounded_end = source_max != 0 && source_size == source_max;
|
||||
const auto converter = SDL_iconv_open("UTF-8", context->profile->encoding);
|
||||
EXIT_IF(converter == reinterpret_cast<SDL_iconv_t>(-1));
|
||||
|
||||
const char* input = reinterpret_cast<const char*>(source);
|
||||
size_t input_left = source_size;
|
||||
size_t produced = 0;
|
||||
int result = 0;
|
||||
std::array<char, 256> scratch;
|
||||
do {
|
||||
char* output = measure ? scratch.data() : reinterpret_cast<char*>(destination) + produced;
|
||||
const size_t capacity = measure ? scratch.size() : destination_max - 1 - produced;
|
||||
size_t output_left = capacity;
|
||||
const auto status = SDL_iconv(converter, &input, &input_left, &output, &output_left);
|
||||
produced += capacity - output_left;
|
||||
if (status == SDL_ICONV_E2BIG) {
|
||||
if (measure) {
|
||||
continue;
|
||||
}
|
||||
result = CES_ERROR_DST_BUFFER_END;
|
||||
} else if (status == SDL_ICONV_EINVAL) {
|
||||
result = bounded_end ? CES_ERROR_SRC_BUFFER_END : CES_ERROR_INVALID_ENCODE;
|
||||
} else if (status == SDL_ICONV_EILSEQ || status == SDL_ICONV_ERROR) {
|
||||
const auto* code = reinterpret_cast<const uint8_t*>(input);
|
||||
const bool lead =
|
||||
(code[0] >= 0x81 && code[0] <= 0x9f) || (code[0] >= 0xe0 && code[0] <= 0xfc);
|
||||
const bool trail = input_left >= 2 && ((code[1] >= 0x40 && code[1] <= 0x7e) ||
|
||||
(code[1] >= 0x80 && code[1] <= 0xfc));
|
||||
result = lead && trail ? CES_ERROR_UNASSIGNED_CODE : CES_ERROR_INVALID_ENCODE;
|
||||
}
|
||||
break;
|
||||
} while (input_left != 0);
|
||||
SDL_iconv_close(converter);
|
||||
|
||||
if (!measure) {
|
||||
destination[produced] = 0;
|
||||
}
|
||||
if (source_len != nullptr) {
|
||||
*source_len = static_cast<uint32_t>(source_size - input_left);
|
||||
}
|
||||
if (destination_len != nullptr) {
|
||||
*destination_len = static_cast<uint32_t>(produced);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI CesMbcsStrGetUtf8Len(CesContext* context, const uint8_t* source,
|
||||
uint32_t source_max, uint32_t* source_len,
|
||||
uint32_t* destination_len) {
|
||||
PRINT_NAME();
|
||||
return ConvertMbcsToUtf8(context, source, source_max, source_len, nullptr, 0, destination_len,
|
||||
true);
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI CesMbcsStrToUtf8Str(CesContext* context, const uint8_t* source,
|
||||
uint32_t source_max, uint32_t* source_len,
|
||||
uint8_t* destination, uint32_t destination_max,
|
||||
uint32_t* destination_len) {
|
||||
PRINT_NAME();
|
||||
return ConvertMbcsToUtf8(context, source, source_max, source_len, destination, destination_max,
|
||||
destination_len, false);
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitCes_1) {
|
||||
LIB_FUNC("ZiDCxUUGbec", LibCes::CesUcsProfileInitSJis1997Cp932);
|
||||
LIB_FUNC("538bRGc6Zo8", LibCes::CesMbcsUcsContextInit);
|
||||
LIB_FUNC("r7Sr1i7KLus", LibCes::CesMbcsStrGetUtf8Len);
|
||||
LIB_FUNC("yGKn6vdYInc", LibCes::CesMbcsStrToUtf8Str);
|
||||
LIB_FUNC("LPzYZ+FR0BI", LibCes::CesRefersUcsProfileCp1252);
|
||||
LIB_FUNC("3Q1gOWWarcw", LibCes::CesUtf8ToSbc);
|
||||
LIB_FUNC("xTd54EEL1Ao", LibCes::CesSbcToUtf8);
|
||||
}
|
||||
|
||||
} // namespace LibCes
|
||||
|
||||
} // namespace Libs
|
||||
@@ -745,7 +745,8 @@ static bool ttf_data_has_supported_magic(const uint8_t* data, uint32_t size) {
|
||||
}
|
||||
|
||||
return (data[0] == 0x00 && data[1] == 0x01 && data[2] == 0x00 && data[3] == 0x00) ||
|
||||
(data[0] == 't' && data[1] == 'r' && data[2] == 'u' && data[3] == 'e');
|
||||
(data[0] == 't' && data[1] == 'r' && data[2] == 'u' && data[3] == 'e') ||
|
||||
(data[0] == 'O' && data[1] == 'T' && data[2] == 'T' && data[3] == 'O');
|
||||
}
|
||||
|
||||
static bool init_stb_font(FontState* font, const uint8_t* data, uint32_t size) {
|
||||
|
||||
@@ -525,6 +525,14 @@ static void* KYTY_SYSV_ABI JsonValueCtor(void* self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
static JsonValue* KYTY_SYSV_ABI JsonValueCopyCtor(JsonValue* self, const JsonValue* src) {
|
||||
PRINT_NAME();
|
||||
|
||||
JsonValueInit(self);
|
||||
JsonValueCopy(self, src);
|
||||
return self;
|
||||
}
|
||||
|
||||
static JsonValue* KYTY_SYSV_ABI JsonValueTypeCtor(JsonValue* self, uint32_t type) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -1014,6 +1022,7 @@ LIB_DEFINE(InitNet_1_Json2) {
|
||||
LIB_FUNC("OcAgPxcq5Vk", LibJson2::JsonMemAllocatorDtor);
|
||||
LIB_FUNC("qBMjqyBn3OM", LibJson2::JsonValueCtor);
|
||||
LIB_FUNC("-wa17B7TGnw", LibJson2::JsonValueCtor);
|
||||
LIB_FUNC("fSb2oQTNrgA", LibJson2::JsonValueCopyCtor);
|
||||
LIB_FUNC("CbrT3dwDILo", LibJson2::JsonValueTypeCtor);
|
||||
LIB_FUNC("WTtYf+cNnXI", LibJson2::JsonValueDtor);
|
||||
LIB_FUNC("0eUrW9JAxM0", LibJson2::JsonValueDtor);
|
||||
|
||||
@@ -2112,6 +2112,33 @@ int KYTY_SYSV_ABI KernelSyncOnAddressWake(volatile void* address, int32_t count)
|
||||
return LibKernel::SyncOnAddress::Wake(address, count);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI UmtxOp(volatile void* address, int operation, uint64_t value,
|
||||
void* uaddr, const void* timeout) {
|
||||
constexpr int UMTX_OP_WAIT = 2;
|
||||
constexpr int UMTX_OP_WAKE = 3;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(uaddr != nullptr);
|
||||
|
||||
switch (operation) {
|
||||
case UMTX_OP_WAIT:
|
||||
if (timeout != nullptr) {
|
||||
LibKernel::KernelTimespec duration {};
|
||||
std::memcpy(&duration, timeout, sizeof(duration));
|
||||
if (duration.tv_sec < 0 || duration.tv_nsec < 0 || duration.tv_nsec >= 1000000000) {
|
||||
*GetErrorAddr() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
EXIT("Valid _umtx_op timed waits are not implemented\n");
|
||||
}
|
||||
return POSIX_CALL(LibKernel::SyncOnAddress::Wait64(
|
||||
static_cast<volatile uint64_t*>(address), value, nullptr,
|
||||
LibKernel::KernelDispatchPendingSignalForCurrentThread));
|
||||
case UMTX_OP_WAKE:
|
||||
return POSIX_CALL(LibKernel::SyncOnAddress::Wake(address, static_cast<int32_t>(value)));
|
||||
default: EXIT("Unsupported _umtx_op operation: %d\n", operation);
|
||||
}
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitLibKernel_1_Posix) {
|
||||
LIB_FUNC("k+AXqu2-eBc", getpagesize);
|
||||
LIB_FUNC("lLMT9vJAck0", clock_gettime);
|
||||
@@ -3310,6 +3337,7 @@ static void AddLibkernelUnityFunc(Loader::SymbolDatabase* s, const char* nid, ui
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitLibKernel_1) {
|
||||
LIB_FUNC("04AjkP0jO9U", Posix::UmtxOp);
|
||||
InitLibKernel_1_FS(s);
|
||||
InitLibKernel_1_Mem(s);
|
||||
InitLibKernel_1_Equeue(s);
|
||||
|
||||
@@ -3003,186 +3003,6 @@ LIB_DEFINE(InitNet_1_NpUniversalDataSystem) {
|
||||
|
||||
} // namespace LibNpUniversalDataSystem
|
||||
|
||||
namespace LibCes {
|
||||
|
||||
LIB_VERSION("Ces", 1, "Ces", 1, 1);
|
||||
|
||||
static const uint8_t* KYTY_SYSV_ABI CesRefersUcsProfileCp1252() {
|
||||
PRINT_NAME();
|
||||
|
||||
static const uint8_t profile = 0;
|
||||
return &profile;
|
||||
}
|
||||
|
||||
static uint32_t CesDecodeUtf8(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len) {
|
||||
if (utf8 == nullptr || utf8max == 0) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 0;
|
||||
}
|
||||
return 0xfffd;
|
||||
}
|
||||
|
||||
const uint8_t c0 = utf8[0];
|
||||
if (c0 < 0x80) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 1;
|
||||
}
|
||||
return c0;
|
||||
}
|
||||
if ((c0 & 0xe0) == 0xc0 && utf8max >= 2 && (utf8[1] & 0xc0) == 0x80) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 2;
|
||||
}
|
||||
return ((c0 & 0x1f) << 6u) | (utf8[1] & 0x3fu);
|
||||
}
|
||||
if ((c0 & 0xf0) == 0xe0 && utf8max >= 3 && (utf8[1] & 0xc0) == 0x80 &&
|
||||
(utf8[2] & 0xc0) == 0x80) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 3;
|
||||
}
|
||||
return ((c0 & 0x0f) << 12u) | ((utf8[1] & 0x3fu) << 6u) | (utf8[2] & 0x3fu);
|
||||
}
|
||||
if ((c0 & 0xf8) == 0xf0 && utf8max >= 4 && (utf8[1] & 0xc0) == 0x80 &&
|
||||
(utf8[2] & 0xc0) == 0x80 && (utf8[3] & 0xc0) == 0x80) {
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 4;
|
||||
}
|
||||
return ((c0 & 0x07) << 18u) | ((utf8[1] & 0x3fu) << 12u) | ((utf8[2] & 0x3fu) << 6u) |
|
||||
(utf8[3] & 0x3fu);
|
||||
}
|
||||
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = 1;
|
||||
}
|
||||
return 0xfffd;
|
||||
}
|
||||
|
||||
struct Cp1252Mapping {
|
||||
uint8_t cp1252;
|
||||
uint32_t unicode;
|
||||
};
|
||||
|
||||
static constexpr Cp1252Mapping CP1252_EXTENDED_MAP[] = {
|
||||
{0x80, 0x20ac}, {0x82, 0x201a}, {0x83, 0x0192}, {0x84, 0x201e}, {0x85, 0x2026}, {0x86, 0x2020},
|
||||
{0x87, 0x2021}, {0x88, 0x02c6}, {0x89, 0x2030}, {0x8a, 0x0160}, {0x8b, 0x2039}, {0x8c, 0x0152},
|
||||
{0x8e, 0x017d}, {0x91, 0x2018}, {0x92, 0x2019}, {0x93, 0x201c}, {0x94, 0x201d}, {0x95, 0x2022},
|
||||
{0x96, 0x2013}, {0x97, 0x2014}, {0x98, 0x02dc}, {0x99, 0x2122}, {0x9a, 0x0161}, {0x9b, 0x203a},
|
||||
{0x9c, 0x0153}, {0x9e, 0x017e}, {0x9f, 0x0178},
|
||||
};
|
||||
|
||||
static uint8_t CesUnicodeToCp1252(uint32_t code) {
|
||||
if (code <= 0x7f || (code >= 0xa0 && code <= 0xff)) {
|
||||
return static_cast<uint8_t>(code);
|
||||
}
|
||||
|
||||
for (const auto& map: CP1252_EXTENDED_MAP) {
|
||||
if (map.unicode == code) {
|
||||
return map.cp1252;
|
||||
}
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
static uint32_t CesCp1252ToUnicode(uint8_t sbc) {
|
||||
if (sbc <= 0x7f || sbc >= 0xa0) {
|
||||
return sbc;
|
||||
}
|
||||
|
||||
for (const auto& map: CP1252_EXTENDED_MAP) {
|
||||
if (map.cp1252 == sbc) {
|
||||
return map.unicode;
|
||||
}
|
||||
}
|
||||
return sbc;
|
||||
}
|
||||
|
||||
static uint32_t CesEncodeUtf8(uint32_t code, uint8_t* utf8, uint32_t utf8max) {
|
||||
uint8_t tmp[4] {};
|
||||
uint32_t len = 0;
|
||||
|
||||
if (code <= 0x7f) {
|
||||
tmp[0] = static_cast<uint8_t>(code);
|
||||
len = 1;
|
||||
} else if (code <= 0x7ff) {
|
||||
tmp[0] = static_cast<uint8_t>(0xc0u | ((code >> 6u) & 0x1fu));
|
||||
tmp[1] = static_cast<uint8_t>(0x80u | (code & 0x3fu));
|
||||
len = 2;
|
||||
} else if (code <= 0xffff) {
|
||||
tmp[0] = static_cast<uint8_t>(0xe0u | ((code >> 12u) & 0x0fu));
|
||||
tmp[1] = static_cast<uint8_t>(0x80u | ((code >> 6u) & 0x3fu));
|
||||
tmp[2] = static_cast<uint8_t>(0x80u | (code & 0x3fu));
|
||||
len = 3;
|
||||
} else {
|
||||
tmp[0] = static_cast<uint8_t>(0xf0u | ((code >> 18u) & 0x07u));
|
||||
tmp[1] = static_cast<uint8_t>(0x80u | ((code >> 12u) & 0x3fu));
|
||||
tmp[2] = static_cast<uint8_t>(0x80u | ((code >> 6u) & 0x3fu));
|
||||
tmp[3] = static_cast<uint8_t>(0x80u | (code & 0x3fu));
|
||||
len = 4;
|
||||
}
|
||||
|
||||
if (utf8 != nullptr && utf8max >= len) {
|
||||
std::memcpy(utf8, tmp, len);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI CesUtf8ToSbc(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len,
|
||||
const uint8_t* profile, uint8_t* sbc) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (sbc == nullptr || profile == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t local_len = 0;
|
||||
const auto code = CesDecodeUtf8(utf8, utf8max, &local_len);
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = local_len;
|
||||
}
|
||||
*sbc = CesUnicodeToCp1252(code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI CesSbcToUtf8(const uint8_t* profile, uint8_t sbc, uint8_t* utf8,
|
||||
uint32_t utf8max, uint32_t* utf8_len) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (profile == nullptr || utf8 == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto code = CesCp1252ToUnicode(sbc);
|
||||
const auto len = CesEncodeUtf8(code, utf8, utf8max);
|
||||
if (utf8_len != nullptr) {
|
||||
*utf8_len = len;
|
||||
}
|
||||
|
||||
return utf8max >= len ? 0 : -1;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI CesStub(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3) {
|
||||
PRINT_NAME();
|
||||
|
||||
(void)arg0;
|
||||
(void)arg1;
|
||||
(void)arg2;
|
||||
(void)arg3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitNet_1_Ces) {
|
||||
LIB_FUNC("ZiDCxUUGbec", LibCes::CesStub);
|
||||
LIB_FUNC("538bRGc6Zo8", LibCes::CesStub);
|
||||
LIB_FUNC("LPzYZ+FR0BI", LibCes::CesRefersUcsProfileCp1252);
|
||||
LIB_FUNC("3Q1gOWWarcw", LibCes::CesUtf8ToSbc);
|
||||
LIB_FUNC("xTd54EEL1Ao", LibCes::CesSbcToUtf8);
|
||||
}
|
||||
|
||||
} // namespace LibCes
|
||||
|
||||
namespace LibNpGameIntent {
|
||||
|
||||
LIB_VERSION("NpGameIntent", 1, "NpGameIntent", 1, 1);
|
||||
@@ -3992,7 +3812,6 @@ LIB_DEFINE(InitNet_1) {
|
||||
LibNpAuth::InitNet_1_NpAuth(s);
|
||||
LibNpTrophy2::InitNet_1_NpTrophy2(s);
|
||||
LibNpUniversalDataSystem::InitNet_1_NpUniversalDataSystem(s);
|
||||
LibCes::InitNet_1_Ces(s);
|
||||
LibNpGameIntent::InitNet_1_NpGameIntent(s);
|
||||
LibNpWebApi2::InitNet_1_NpWebApi2(s);
|
||||
LibGameUpdate::InitNet_1_GameUpdate(s);
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace SystemService {
|
||||
[[maybe_unused]] constexpr int PARAM_ID_GAME_PARENTAL_LEVEL = 7;
|
||||
[[maybe_unused]] constexpr int PARAM_ID_CC_ENABLE = 100;
|
||||
[[maybe_unused]] constexpr int PARAM_ID_SCREEN_READER = 208;
|
||||
constexpr int PARAM_ID_LANG_PS5 = 400; // PPSA01325
|
||||
[[maybe_unused]] constexpr int PARAM_ID_ENTER_BUTTON_ASSIGN = 1000;
|
||||
|
||||
[[maybe_unused]] constexpr int PARAM_DATE_FORMAT_YYYYMMDD = 0;
|
||||
@@ -96,7 +97,8 @@ static int KYTY_SYSV_ABI SystemServiceParamGetInt(int param_id, int* value) {
|
||||
int v = 0;
|
||||
|
||||
switch (param_id) {
|
||||
case PARAM_ID_LANG: v = static_cast<int>(Config::GetConsoleLanguage()); break;
|
||||
case PARAM_ID_LANG:
|
||||
case PARAM_ID_LANG_PS5: v = static_cast<int>(Config::GetConsoleLanguage()); break;
|
||||
case PARAM_ID_DATE_FORMAT: v = PARAM_DATE_FORMAT_DDMMYYYY; break;
|
||||
case PARAM_ID_TIME_FORMAT: v = PARAM_TIME_FORMAT_24HOUR; break;
|
||||
case PARAM_ID_TIME_ZONE: v = +180; break;
|
||||
|
||||
@@ -42,6 +42,10 @@ namespace LibPsml {
|
||||
LIB_DEFINE(InitPsml_1);
|
||||
} // namespace LibPsml
|
||||
|
||||
namespace LibCes {
|
||||
LIB_DEFINE(InitCes_1);
|
||||
} // namespace LibCes
|
||||
|
||||
namespace LibC {
|
||||
LIB_DEFINE(InitLibC_1);
|
||||
} // namespace LibC
|
||||
@@ -103,6 +107,7 @@ void InitAll(Loader::SymbolDatabase* s) {
|
||||
LibContentExport::InitContentExport_1(s);
|
||||
LibContentSearch::InitContentSearch_1(s);
|
||||
LibC::InitLibC_1(s);
|
||||
LibCes::InitCes_1(s);
|
||||
InitDbgAddressSanitizer_1(s);
|
||||
LibRazorCpu::InitRazorCpu_1(s);
|
||||
InitDialog_1(s);
|
||||
|
||||
@@ -117,8 +117,7 @@ public:
|
||||
[[nodiscard]] Result DecodeInput(const Input& input, const FrameBuffer& frame_buffer,
|
||||
Output* output) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
*output = {};
|
||||
m_draining = false;
|
||||
*output = {};
|
||||
|
||||
AVPacket* packet = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
@@ -200,18 +199,7 @@ public:
|
||||
return Result::ApiFail;
|
||||
}
|
||||
|
||||
if (!m_draining) {
|
||||
const int send_result = avcodec_send_packet(m_codec, nullptr);
|
||||
if (send_result == 0 || send_result == AVERROR_EOF) {
|
||||
m_draining = true;
|
||||
} else if (send_result != AVERROR(EAGAIN)) {
|
||||
LOGF("Videodec2: flushing decoder failed: %s (%d)\n", AvErrorString(send_result),
|
||||
send_result);
|
||||
av_frame_free(&frame);
|
||||
return Result::ApiFail;
|
||||
}
|
||||
}
|
||||
|
||||
// Guest Flush collects available pictures between AUs while retaining reference frames.
|
||||
const int receive_result = avcodec_receive_frame(m_codec, frame);
|
||||
if (receive_result == AVERROR(EAGAIN) || receive_result == AVERROR_EOF) {
|
||||
av_frame_free(&frame);
|
||||
@@ -232,7 +220,6 @@ public:
|
||||
void ResetDecoder() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
avcodec_flush_buffers(m_codec);
|
||||
m_draining = false;
|
||||
ClearPictureMetadata();
|
||||
}
|
||||
|
||||
@@ -357,7 +344,6 @@ private:
|
||||
Config m_config;
|
||||
AVCodecContext* m_codec = nullptr;
|
||||
SwsContext* m_sws = nullptr;
|
||||
bool m_draining = false;
|
||||
std::mutex m_mutex;
|
||||
std::unordered_set<void*> m_picture_buffers;
|
||||
};
|
||||
|
||||
@@ -801,6 +801,42 @@ static bool KytyExceptionHandler(const Common::HostException::ExceptionInfo& exc
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Report whatever guest context can be read safely before terminating: which guest thread
|
||||
// faulted, the register file, the faulting code bytes and the top of its stack.
|
||||
{
|
||||
char thread_name[64] = "(host thread)";
|
||||
if (auto self = Libs::LibKernel::PthreadSelfOrNull(); self != nullptr) {
|
||||
if (Libs::LibKernel::PthreadGetname(self, thread_name) != 0) {
|
||||
std::snprintf(thread_name, sizeof(thread_name), "(unnamed guest thread)");
|
||||
}
|
||||
}
|
||||
std::printf("--- Guest fault context ---\n");
|
||||
std::printf("thread: %s\n", thread_name);
|
||||
std::printf("rax=%016" PRIx64 " rbx=%016" PRIx64 " rcx=%016" PRIx64 " rdx=%016" PRIx64 "\n"
|
||||
"rsi=%016" PRIx64 " rdi=%016" PRIx64 " rbp=%016" PRIx64 " rsp=%016" PRIx64 "\n"
|
||||
"r8 =%016" PRIx64 " r9 =%016" PRIx64 " r10=%016" PRIx64 " r11=%016" PRIx64 "\n"
|
||||
"r12=%016" PRIx64 " r13=%016" PRIx64 " r14=%016" PRIx64 " r15=%016" PRIx64 "\n",
|
||||
info->rax, info->rbx, info->rcx, info->rdx, info->rsi, info->rdi, info->rbp,
|
||||
info->rsp, info->r8, info->r9, info->r10, info->r11, info->r12, info->r13,
|
||||
info->r14, info->r15);
|
||||
if (IsReadableRange(info->exception_address - 48, 96)) {
|
||||
const auto* code = reinterpret_cast<const uint8_t*>(info->exception_address - 48);
|
||||
std::printf("code (pc-48 .. pc+48, fault at byte 48):");
|
||||
for (int i = 0; i < 96; i++) {
|
||||
std::printf("%s%02x", (i % 16 == 0) ? "\n " : " ", code[i]);
|
||||
}
|
||||
std::printf("\n");
|
||||
}
|
||||
if (IsReadableRange(info->rsp, 32 * sizeof(uint64_t))) {
|
||||
const auto* stack = reinterpret_cast<const uint64_t*>(info->rsp);
|
||||
std::printf("stack:");
|
||||
for (int i = 0; i < 32; i++) {
|
||||
std::printf("%s %016" PRIx64, (i % 4 == 0) ? "\n " : "", stack[i]);
|
||||
}
|
||||
std::printf("\n");
|
||||
}
|
||||
std::fflush(stdout);
|
||||
}
|
||||
EXIT("Unhandled host exception: type=%u code=%u pc=0x%016" PRIx64
|
||||
" access=%u address=0x%016" PRIx64 "\n",
|
||||
static_cast<unsigned>(info->type), info->native_code, info->exception_address,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/magicEnum.h"
|
||||
#include "common/platform/sysDbg.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/threads.h"
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#include "SDL.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/file.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "common/threads.h"
|
||||
#include "graphics/presentation/window/windowInternal.h"
|
||||
#include "kernel/fileSystem.h"
|
||||
#include "libs/errno.h"
|
||||
#include "libs/network.h"
|
||||
#include "loader/symbolDatabase.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -14,8 +17,15 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::LibKernelApr {
|
||||
void InitLibKernel_1_Apr(Loader::SymbolDatabase *symbols);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -79,6 +89,217 @@ void CheckSaveRename(const std::filesystem::path &root,
|
||||
"renamed save contents");
|
||||
}
|
||||
|
||||
void CheckMountRoot(const std::filesystem::path &root) {
|
||||
Common::File cache;
|
||||
Check(cache.Create(root / "rpf.cache"), "create directory listing fixture");
|
||||
cache.Close();
|
||||
FileSystem::Mount(root, "/app0");
|
||||
Check(FileSystem::GetRealFilename("/app0/rpf.cache") == root / "rpf.cache",
|
||||
"resolve mount descendant");
|
||||
Check(FileSystem::GetRealFilename("/app01/rpf.cache") == "/app01/rpf.cache",
|
||||
"mount prefix must end at a path component");
|
||||
|
||||
for (const char *path : {"/app0", "/app0/"}) {
|
||||
for (const int flags : {0, 0x00020000}) {
|
||||
const int fd = FileSystem::KernelOpen(path, flags, 0);
|
||||
Check(fd >= 3, "open mounted root with O_RDONLY or O_DIRECTORY");
|
||||
std::array<char, 512> entries {};
|
||||
const int size = FileSystem::KernelGetdents(fd, entries.data(), entries.size());
|
||||
Check(size > 0 && size <= entries.size(), "enumerate mounted root");
|
||||
bool found = false;
|
||||
for (int offset = 0; offset < size;) {
|
||||
// Directory record: inode, record length, type, name length, name.
|
||||
Check(size - offset >= 8, "directory record header fits");
|
||||
uint16_t length = 0;
|
||||
std::memcpy(&length, entries.data() + offset + 4, sizeof(length));
|
||||
const auto name_length = static_cast<uint8_t>(entries[offset + 7]);
|
||||
Check(length >= 8 + name_length + 1 && length <= size - offset,
|
||||
"directory record and name fit");
|
||||
if (std::string_view(entries.data() + offset + 8, name_length) == "rpf.cache") {
|
||||
Check(entries[offset + 6] == 8, "cache directory entry is a regular file");
|
||||
found = true;
|
||||
}
|
||||
offset += length;
|
||||
}
|
||||
Check(found, "mounted root listing contains rpf.cache");
|
||||
Check(FileSystem::KernelClose(fd) == OK, "close mounted root");
|
||||
}
|
||||
}
|
||||
FileSystem::Umount("/app0");
|
||||
}
|
||||
|
||||
void CheckDirectoryStream(const std::filesystem::path &root) {
|
||||
const auto directory = root / "directory-stream";
|
||||
Check(std::filesystem::create_directory(directory),
|
||||
"create seek fixture directory");
|
||||
for (int i = 0; i < 48; ++i) {
|
||||
Common::File fixture;
|
||||
Check(fixture.Create(directory / ("directory-entry-" + std::to_string(i))),
|
||||
"create enough entries to cross directory blocks");
|
||||
fixture.Close();
|
||||
}
|
||||
FileSystem::Mount(directory, "/app0");
|
||||
const int fd = FileSystem::KernelOpen("/app0/", 0, 0);
|
||||
Check(fd >= 3, "open directory as read-only asset");
|
||||
const auto end = FileSystem::KernelLseek(fd, 0, 2);
|
||||
Check(end > 512 && end % 512 == 0,
|
||||
"directory SEEK_END uses padded stream size");
|
||||
FileSystem::FileStat stat{};
|
||||
Check(FileSystem::KernelFstat(fd, &stat) == OK && stat.st_size == end &&
|
||||
stat.st_blksize == 512,
|
||||
"directory stat agrees with seek and enumeration");
|
||||
Check(FileSystem::KernelStat("/app0/", &stat) == OK && stat.st_size == end &&
|
||||
stat.st_blocks == end / 512,
|
||||
"path and descriptor directory stat agree");
|
||||
Check(FileSystem::KernelLseek(fd, 0, 0) == 0,
|
||||
"rewind directory for asset read");
|
||||
std::vector<char> raw(static_cast<size_t>(end));
|
||||
Check(FileSystem::KernelRead(fd, raw.data(), raw.size()) == end &&
|
||||
FileSystem::KernelRead(fd, raw.data(), 1) == 0,
|
||||
"raw directory read reaches EOF");
|
||||
Check(FileSystem::KernelLseek(fd, -end, 1) == 0,
|
||||
"directory SEEK_CUR uses the position advanced by read");
|
||||
|
||||
std::array<char, 512> block{};
|
||||
int64_t base = -1;
|
||||
for (int64_t offset = 0; offset < end; offset += block.size()) {
|
||||
Check(FileSystem::KernelGetdirentries(fd, block.data(), block.size(),
|
||||
&base) == block.size() &&
|
||||
base == offset &&
|
||||
std::memcmp(block.data(), raw.data() + offset, block.size()) == 0,
|
||||
"directory enumeration shares raw bytes and reports each block "
|
||||
"position");
|
||||
for (size_t pos = 0; pos < block.size();) {
|
||||
Check(block.size() - pos >= 8, "directory record header fits");
|
||||
uint16_t length = 0;
|
||||
std::memcpy(&length, block.data() + pos + 4, sizeof(length));
|
||||
const auto name_length = static_cast<uint8_t>(block[pos + 7]);
|
||||
Check(length >= 9 + name_length && length <= block.size() - pos &&
|
||||
block[pos + 8 + name_length] == '\0',
|
||||
"directory entries remain complete within every block");
|
||||
pos += length;
|
||||
}
|
||||
}
|
||||
Check(FileSystem::KernelGetdirentries(fd, block.data(), block.size(),
|
||||
&base) == 0 &&
|
||||
base == end,
|
||||
"directory enumeration reports EOF position");
|
||||
Check(FileSystem::KernelLseek(fd, 512, 0) == 512 &&
|
||||
FileSystem::KernelGetdirentries(fd, block.data(), block.size(),
|
||||
&base) == block.size() &&
|
||||
base == 512 &&
|
||||
std::memcmp(block.data(), raw.data() + 512, block.size()) == 0,
|
||||
"restore and reread a directory enumeration position");
|
||||
|
||||
const auto position = FileSystem::KernelLseek(fd, 0, 1);
|
||||
Check(
|
||||
FileSystem::KernelLseek(fd, 0, 9) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, -1, 0) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, std::numeric_limits<int64_t>::min(), 1) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, std::numeric_limits<int64_t>::max(), 1) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EOVERFLOW &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == position,
|
||||
"invalid and overflowing directory seeks preserve the position");
|
||||
Check(FileSystem::KernelLseek(fd, -19, 2) == end - 19 &&
|
||||
FileSystem::KernelRead(fd, block.data(), block.size()) == 19 &&
|
||||
std::memcmp(block.data(), raw.data() + end - 19, 19) == 0,
|
||||
"raw directory reads support byte positions and stop at EOF");
|
||||
Check(FileSystem::KernelLseek(fd, 1, 0) == 1 &&
|
||||
FileSystem::KernelGetdents(fd, block.data(), block.size()) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == 1,
|
||||
"directory enumeration rejects an incomplete record position");
|
||||
Check(FileSystem::KernelLseek(fd, 512, 2) == end + 512 &&
|
||||
FileSystem::KernelGetdirentries(fd, block.data(), block.size(),
|
||||
&base) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == end + 512,
|
||||
"directory enumeration rejects a position beyond EOF");
|
||||
Check(FileSystem::KernelRead(
|
||||
fd, block.data(),
|
||||
static_cast<size_t>(std::numeric_limits<int>::max()) + 1) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EINVAL &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == end + 512,
|
||||
"oversized read fails without changing the directory position");
|
||||
Check(FileSystem::KernelClose(fd) == OK, "close directory stream");
|
||||
Check(FileSystem::KernelLseek(fd, 0, 0) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_EBADF,
|
||||
"seek rejects a closed descriptor");
|
||||
FileSystem::Umount("/app0");
|
||||
}
|
||||
|
||||
void CheckAprPaths(const std::filesystem::path &root) {
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::LibKernelApr::InitLibKernel_1_Apr(&symbols);
|
||||
const auto *resolve_symbol = symbols.FindByNid("w5fcCG+t31g", Loader::SymbolType::Func);
|
||||
const auto *each_symbol = symbols.FindByNid("C+Khtbbx2g8", Loader::SymbolType::Func);
|
||||
Check(resolve_symbol && each_symbol, "APR path exports are registered");
|
||||
using Resolve = int (KYTY_SYSV_ABI *)(const char *, const char *const *, uint32_t,
|
||||
uint32_t *, uint64_t *, uint32_t *);
|
||||
using ResolveEach = int (KYTY_SYSV_ABI *)(const char *, const char *const *, uint32_t,
|
||||
uint32_t *, uint64_t *, int *);
|
||||
const auto resolve = reinterpret_cast<Resolve>(resolve_symbol->vaddr);
|
||||
const auto resolve_each = reinterpret_cast<ResolveEach>(each_symbol->vaddr);
|
||||
Common::File fixture;
|
||||
Check(fixture.Create(root / "apr.dat"), "create APR fixture");
|
||||
fixture.Write("APR", 3);
|
||||
fixture.Close();
|
||||
FileSystem::Mount(root, "/app0");
|
||||
|
||||
uint32_t expected_id = 0xffffffffu;
|
||||
for (const auto &parts : {std::array{"", "/app0/apr.dat"},
|
||||
std::array{"/app0/", "apr.dat"},
|
||||
std::array{"/", "app0/apr.dat"},
|
||||
std::array{"/app", "0/apr.dat"}}) {
|
||||
uint32_t id = 0xffffffffu, error_index = 0xffffffffu;
|
||||
uint64_t size = 0;
|
||||
Check(resolve(parts[0], &parts[1], 1, &id, &size, &error_index) == OK &&
|
||||
id != 0xffffffffu && size == 3,
|
||||
"APR concatenates empty, one-character and partial-component prefixes");
|
||||
if (expected_id == 0xffffffffu) {
|
||||
expected_id = id;
|
||||
}
|
||||
Check(id == expected_id, "equivalent APR paths return the same ID");
|
||||
}
|
||||
|
||||
const char *paths[] = {"/app0/missing.dat", "/app0/apr.dat"};
|
||||
uint32_t ids[2] = {}, error_index = 0xffffffffu;
|
||||
uint64_t sizes[2] = {1, 1};
|
||||
int results[2] = {};
|
||||
Check(resolve_each("", paths, 2, ids, sizes, results) == 1 &&
|
||||
results[0] == Libs::LibKernel::KERNEL_ERROR_ENOENT && results[1] == OK &&
|
||||
ids[0] == 0xffffffffu && ids[1] == expected_id && sizes[0] == 0 && sizes[1] == 3,
|
||||
"APR foreach reports a missing path and continues to the valid file");
|
||||
|
||||
// PATH_MAX includes NUL; all components remain below NAME_MAX (255).
|
||||
std::string longest = "/app0/";
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
longest += std::string(254, 'a') + '/';
|
||||
}
|
||||
longest += std::string(1023 - longest.size(), 'b');
|
||||
paths[0] = longest.c_str();
|
||||
Check(resolve("", paths, 1, ids, sizes, &error_index) == -1 &&
|
||||
*Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_ENOENT && error_index == 0,
|
||||
"APR accepts a pathname whose final NUL is at PATH_MAX minus one");
|
||||
Check(resolve("/", paths, 1, ids, sizes, &error_index) == -1 &&
|
||||
*Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_ENAMETOOLONG,
|
||||
"APR rejects concatenated paths exceeding PATH_MAX");
|
||||
std::array<char, 1024> unterminated;
|
||||
unterminated.fill('/');
|
||||
paths[0] = unterminated.data();
|
||||
Check(resolve("", paths, 1, ids, sizes, &error_index) == -1 &&
|
||||
*Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_ENAMETOOLONG,
|
||||
"APR rejects an unterminated pathname");
|
||||
paths[0] = "apr.dat";
|
||||
Check(resolve(unterminated.data(), paths, 1, ids, sizes, &error_index) == -1 &&
|
||||
*Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_ENAMETOOLONG,
|
||||
"APR rejects an unterminated prefix");
|
||||
FileSystem::Umount("/app0");
|
||||
}
|
||||
|
||||
void CheckSocketWakeup() {
|
||||
namespace Net = Libs::Network::Net;
|
||||
// Guest sockaddr_in: length, family, network-order port/address, padding.
|
||||
@@ -154,13 +375,26 @@ int main() {
|
||||
Config::Load(options);
|
||||
subsystems.Initialize<Log::Lifecycle>();
|
||||
|
||||
Check(SDL_InitSubSystem(SDL_INIT_VIDEO) == 0, "initialize Vulkan test video");
|
||||
auto graphics = std::make_unique<Libs::Graphics::WindowContext>();
|
||||
graphics->graphic_ctx.screen_width = 64;
|
||||
graphics->graphic_ctx.screen_height = 64;
|
||||
graphics->window = SDL_CreateWindow("KernelFileSystemTests", 0, 0, 64, 64,
|
||||
SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN);
|
||||
Check(graphics->window != nullptr, "create hidden Vulkan test window");
|
||||
graphics->CreateVulkan();
|
||||
|
||||
TempDirectory temporary;
|
||||
FileSystem::Initialize();
|
||||
CheckMountRoot(temporary.Path());
|
||||
CheckDirectoryStream(temporary.Path());
|
||||
CheckAprPaths(temporary.Path());
|
||||
FileSystem::Mount(temporary.Path(), "/savedata0");
|
||||
CheckSaveRename(temporary.Path(), "first-save");
|
||||
CheckSaveRename(temporary.Path(), "replacement-save");
|
||||
FileSystem::Shutdown();
|
||||
CheckSocketWakeup();
|
||||
graphics.reset();
|
||||
subsystems.Destroy();
|
||||
|
||||
std::printf("KernelFileSystemTests: all cases passed\n");
|
||||
|
||||
+1278
-101
File diff suppressed because it is too large.
Load diff
+75
-11
@@ -4033,6 +4033,16 @@ void TestNewShaderDecoderArchitecture() {
|
||||
Check(GetInstructionFamily(EncodeDs0(0x36)) == Family::DS,
|
||||
"decoder did not classify DS directly");
|
||||
|
||||
const uint32_t orn2_saveexec_code[] = {0xbeea407eu};
|
||||
Instruction orn2_saveexec;
|
||||
ShaderRecompiler::Decoder::DecodeInstruction(orn2_saveexec_code, 0u, orn2_saveexec);
|
||||
Check(orn2_saveexec.family == Family::SOP1 &&
|
||||
orn2_saveexec.opcode == Opcode::S_ORN2_SAVEEXEC_B32 &&
|
||||
orn2_saveexec.word_count == 1u && orn2_saveexec.src_count == 1u &&
|
||||
orn2_saveexec.dst.kind == OperandKind::VccLo &&
|
||||
orn2_saveexec.src0.kind == OperandKind::ExecLo,
|
||||
"decoder rejected or misdecoded captured S_ORN2_SAVEEXEC_B32");
|
||||
|
||||
const uint32_t offset_code[] = {0u, EncodeVop1(0x01, 2, 3)};
|
||||
Instruction direct;
|
||||
ShaderRecompiler::Decoder::DecodeInstruction(offset_code, 1u, direct);
|
||||
@@ -4224,6 +4234,19 @@ void TestNewShaderDecoderArchitecture() {
|
||||
Check(ds.opcode == Opcode::DS_READ_B32 && ds.gds,
|
||||
"DS decoder lost the GFX10 opcode or GDS fields");
|
||||
|
||||
for (bool decrement : {false, true}) {
|
||||
const uint32_t words[] = {decrement ? 0xd8920004u : 0xd88e0004u, 0x03000302u};
|
||||
Instruction atomic;
|
||||
ShaderRecompiler::Decoder::DecodeInstruction(words, 0u, atomic);
|
||||
Check(atomic.family == Family::DS &&
|
||||
atomic.opcode == (decrement ? Opcode::DS_DEC_RTN_U32 : Opcode::DS_INC_RTN_U32) &&
|
||||
atomic.word_count == 2u && atomic.src_count == 2u &&
|
||||
atomic.data_dwords == 1u && atomic.data_bits == 32u &&
|
||||
atomic.gds && atomic.offset == 4u && atomic.dst.reg == 3u &&
|
||||
atomic.src0.reg == 2u && atomic.src1.reg == 3u,
|
||||
"DS decoder misdecoded bounded atomic return fields");
|
||||
}
|
||||
|
||||
const uint32_t boot_ds[] = {0xd8d4c480u, 0x45000045u};
|
||||
Instruction boot;
|
||||
ShaderRecompiler::Decoder::DecodeInstruction(boot_ds, 0u, boot);
|
||||
@@ -4328,7 +4351,7 @@ void TestNewShaderRecompilerCapturedVopcSdwaCmpxClass() {
|
||||
Check(decoded.family == Decoder::Family::VOPC &&
|
||||
decoded.opcode == Decoder::Opcode::V_CMPX_CLASS_F32 &&
|
||||
decoded.opcode_id == 0x98u && decoded.word_count == 2u &&
|
||||
decoded.raw_count == 2u &&
|
||||
decoded.raw[0] == shader[0] && decoded.raw[1] == shader[1] &&
|
||||
decoded.dst.kind == Decoder::OperandKind::ExecLo &&
|
||||
decoded.src_count == 2u &&
|
||||
decoded.src0.kind == Decoder::OperandKind::Vgpr &&
|
||||
@@ -9117,6 +9140,8 @@ void TestMeshExportStorage() {
|
||||
EncodeExp0(0x14, 0x1), EncodeExp1(0, 0, 0, 0), // primitive
|
||||
EncodeSopp(0x01),
|
||||
};
|
||||
std::vector<uint32_t> monolithic(std::begin(front), std::begin(front) + 2);
|
||||
monolithic.insert(monolithic.end(), std::begin(back), std::end(back));
|
||||
ShaderVertexInputInfo input{};
|
||||
input.pa_cl_vs_out_cntl = (1u << 21u) | (1u << 18u);
|
||||
auto &mesh = input.mesh;
|
||||
@@ -9136,16 +9161,23 @@ void TestMeshExportStorage() {
|
||||
ShaderRecompiler::CompileOptions options{};
|
||||
options.stage = ShaderType::Mesh;
|
||||
options.input_info.vertex = &input;
|
||||
options.back_code = back;
|
||||
options.user_data = user_data;
|
||||
for (const auto [subgroup_size, push_data_start] :
|
||||
{std::pair{32u, PushData::MeshDrawDwordCount},
|
||||
std::pair{64u, PushData::MeshDrawDwordCount},
|
||||
std::pair{32u, PushData::DwordCount},
|
||||
std::pair{64u, PushData::DwordCount}}) {
|
||||
struct Case {
|
||||
uint32_t subgroup_size, push_data_start;
|
||||
bool split;
|
||||
};
|
||||
const Case cases[] = {
|
||||
{32u, PushData::MeshDrawDwordCount, true}, {64u, PushData::MeshDrawDwordCount, true},
|
||||
{32u, PushData::DwordCount, true}, {64u, PushData::DwordCount, true},
|
||||
{32u, PushData::MeshDrawDwordCount, false}, {64u, PushData::MeshDrawDwordCount, false},
|
||||
{32u, PushData::DwordCount, false}, {64u, PushData::DwordCount, false},
|
||||
};
|
||||
for (const auto [subgroup_size, push_data_start, split] : cases) {
|
||||
mesh.host_subgroup_size = subgroup_size;
|
||||
const auto result =
|
||||
RecompileForTest(front, options, nullptr, nullptr, push_data_start);
|
||||
options.back_code = split ? std::span{back} : std::span<const uint32_t>{};
|
||||
const auto result = RecompileForTest(
|
||||
split ? std::span{front} : std::span<const uint32_t>{monolithic},
|
||||
options, nullptr, nullptr, push_data_start);
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
const auto &layout = result.program.bindings;
|
||||
Check(layout.UsesPushData() == (push_data_start == PushData::MeshDrawDwordCount) &&
|
||||
@@ -9232,9 +9264,13 @@ void TestMergedShaderUserDataSnapshot() {
|
||||
const std::array<uint32_t, 4> front_data = {regs.gs_user_sgpr.value[0],
|
||||
regs.gs_user_sgpr.value[1], regs.gs_user_sgpr.value[2], regs.gs_user_sgpr.value[3]};
|
||||
ShaderMappedData mapped{};
|
||||
mapped.type = Prospero::ShaderBinaryType::kGsFront;
|
||||
mapped.code_size_bytes = sizeof(front);
|
||||
mapped.scratch_size_dwords = 3;
|
||||
ShaderMapUserData(regs.es_regs.data_addr, mapped);
|
||||
mapped.type = Prospero::ShaderBinaryType::kGsBack;
|
||||
mapped.code_size_bytes = sizeof(back);
|
||||
mapped.scratch_size_dwords = 7;
|
||||
ShaderMapUserData(regs.gs_regs.data_addr, mapped);
|
||||
HW::Context context;
|
||||
context.SetShaderStages(0x20);
|
||||
@@ -9255,8 +9291,9 @@ void TestMergedShaderUserDataSnapshot() {
|
||||
second.user_data[8] == front_data[0] + 1,
|
||||
"merged shader parameters did not snapshot ordinary user SGPRs at s8");
|
||||
Check(first.hash == second.hash &&
|
||||
MakeStageStaticKey(first_input) == MakeStageStaticKey(second_input),
|
||||
"a dynamic merged-shader user-data pointer changed shader identity");
|
||||
MakeStageStaticKey(first_input) == MakeStageStaticKey(second_input) &&
|
||||
first_input.mesh.scratch_size_dwords == 7,
|
||||
"merged shader identity or maximum scratch size changed with dynamic user data");
|
||||
CompileOptions options{};
|
||||
options.stage = ShaderType::Mesh;
|
||||
options.user_data_base = 0;
|
||||
@@ -9281,6 +9318,25 @@ void TestMergedShaderUserDataSnapshot() {
|
||||
std::equal(table.begin(), table.end(), back_descriptor.dwords.begin()),
|
||||
"merged shader resource plan did not follow the current s0:s1 pointer and s8 data");
|
||||
}
|
||||
const uint32_t monolithic[] = {
|
||||
EncodeMubuf0(0x1c), EncodeMubuf1(0, 2, 1), EncodeSopp(0x01),
|
||||
};
|
||||
regs.es_regs.data_addr = reinterpret_cast<uint64_t>(monolithic);
|
||||
mapped.type = Prospero::ShaderBinaryType::kGs;
|
||||
mapped.code_size_bytes = sizeof(monolithic);
|
||||
mapped.scratch_size_dwords = 3;
|
||||
ShaderMapUserData(regs.es_regs.data_addr, mapped);
|
||||
for (const auto back_address : {regs.gs_regs.data_addr, uint64_t{0}}) {
|
||||
regs.gs_regs.data_addr = back_address;
|
||||
ShaderVertexInputInfo input{};
|
||||
const auto params = PrepareProgram(regs, context, user_config, input);
|
||||
Check(params.back_code.empty() && params.hash == XXH3_64bits(monolithic, sizeof(monolithic)) &&
|
||||
input.mesh.scratch_size_dwords == 3 && params.user_data.size() == 12 &&
|
||||
params.user_data[0] == 0 && params.user_data[1] == 0 &&
|
||||
std::equal(second.user_data.begin() + 8, second.user_data.end(),
|
||||
params.user_data.begin() + 8),
|
||||
"monolithic NGG shader used stale GS-back state or lost its s8 user data");
|
||||
}
|
||||
}
|
||||
|
||||
void TestEmbeddedVertexFormatSwizzle() {
|
||||
@@ -9418,6 +9474,14 @@ void TestMeshInputAssembly() {
|
||||
0x40000305, 1, 2, 3, 0, 15, false},
|
||||
{Prospero::PrimitiveType::kTriStrip, 5, 8, 0, 1, 0, 0, 11,
|
||||
0x40000305, 2, 1, 3, 0, 12, false},
|
||||
{Prospero::PrimitiveType::kLineList, 5, 17, 3, 0, 0, 0, 11,
|
||||
0x40000204, 0, 1, 0, 0, 23, false},
|
||||
{Prospero::PrimitiveType::kLineList, 6, 17, 2, 4, 2, 0x1002, 11,
|
||||
0x40000205, 8, 9, 0, 32, 0xabd8, true},
|
||||
{Prospero::PrimitiveType::kLineList, 6, 17, 2, 5, 2, 0x1002, 0,
|
||||
0x40000205, 10, 11, 0, 36, 0, false},
|
||||
{Prospero::PrimitiveType::kLineList, 8, 180, 1, 64, 4, 0x1000, 0,
|
||||
0x41000000, 128, 129, 0, 288, 0, false},
|
||||
{Prospero::PrimitiveType::kPointList, 12, 265, 22, 0, 2, 0x1002, 0,
|
||||
0x40000101, 0, 0, 0, 528, 0xabcd, true},
|
||||
{Prospero::PrimitiveType::kPointList, 12, 265, 22, 1, 2, 0x1002, 0,
|
||||
|
||||
Reference in new issue
Block a user