From a950dff87dc147daded4bf7d2a334a99da915ecd Mon Sep 17 00:00:00 2001 From: Triang3l Date: Fri, 17 Dec 2021 13:49:36 +0300 Subject: [PATCH 01/88] [Vulkan] Update SPIRV-Tools fork to fix Linux building issue --- third_party/spirv-tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/spirv-tools b/third_party/spirv-tools index 27a2bbb86..b390553f4 160000 --- a/third_party/spirv-tools +++ b/third_party/spirv-tools @@ -1 +1 @@ -Subproject commit 27a2bbb865ef638afe4260bf214110425a2b904b +Subproject commit b390553f4229bb7790efab6121be2bb5e15e2433 From 701300e8e9be446e04afb4731b18100783032ff9 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sat, 18 Dec 2021 19:43:17 +0300 Subject: [PATCH 02/88] [Linux] Use sched_yield instead of the deprecated pthread_yield --- src/xenia/base/threading_posix.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index 05fc616b7..813758a4b 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -14,6 +14,7 @@ #include "xenia/base/platform.h" #include +#include #include #include #include @@ -28,7 +29,6 @@ #if XE_PLATFORM_ANDROID #include -#include #include "xenia/base/main_android.h" #include "xenia/base/string_util.h" @@ -128,11 +128,7 @@ uint32_t current_thread_system_id() { } void MaybeYield() { -#if XE_PLATFORM_ANDROID sched_yield(); -#else - pthread_yield(); -#endif __sync_synchronize(); } From f2c0ae46c11db7b0bfd4b4d638aaa3ff282d6ba0 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Mon, 20 Dec 2021 08:53:45 +0100 Subject: [PATCH 03/88] [Kernel] Added missing month to RtlTimeFieldsToTime Additionally added check for highest possible month day --- src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc index db3cef5db..18378b662 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc @@ -529,9 +529,10 @@ DECLARE_XBOXKRNL_EXPORT1(RtlTimeToTimeFields, kNone, kImplemented); dword_result_t RtlTimeFieldsToTime(pointer_t time_fields_ptr, lpqword_t time_ptr) { if (time_fields_ptr->year < 1601 || time_fields_ptr->month < 1 || - time_fields_ptr->month > 11 || time_fields_ptr->day < 1 || - time_fields_ptr->hour > 23 || time_fields_ptr->minute > 59 || - time_fields_ptr->second > 59 || time_fields_ptr->milliseconds > 999) { + time_fields_ptr->month > 12 || time_fields_ptr->day < 1 || + time_fields_ptr->day > 31 || time_fields_ptr->hour > 23 || + time_fields_ptr->minute > 59 || time_fields_ptr->second > 59 || + time_fields_ptr->milliseconds > 999) { return 0; } auto year = date::year{time_fields_ptr->year}; From b64b4c6761eb59da2d4ffb837c4187282498023c Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Tue, 28 Dec 2021 18:22:51 -0800 Subject: [PATCH 04/88] [x64] IsFeatureEnabled: Allow parallel feature checks Just checking if the resulting mask is non-zero means we cannot allow this function to check for multiple features in parallel. A hypothetical computer that supports FMA but not AVX2 will return `true` if you try to call `IsFeatureEnabled(kX64EmitFMA | kX64EmitAVX2)`. We should make sure all the masked flags return `true` rather than check for non-zero. This is ramping up to allow for particular subsets of AVX512 to be checked for in parallel with a single function call. --- src/xenia/cpu/backend/x64/x64_emitter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xenia/cpu/backend/x64/x64_emitter.h b/src/xenia/cpu/backend/x64/x64_emitter.h index 4a31543b6..088a56d6d 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.h +++ b/src/xenia/cpu/backend/x64/x64_emitter.h @@ -221,7 +221,7 @@ class X64Emitter : public Xbyak::CodeGenerator { Xbyak::Address StashConstantXmm(int index, const vec128_t& v); bool IsFeatureEnabled(uint32_t feature_flag) const { - return (feature_flags_ & feature_flag) != 0; + return (feature_flags_ & feature_flag) == feature_flag; } FunctionDebugInfo* debug_info() const { return debug_info_; } From 1a8068b151bfd524a13b9cbe99be95d0200760a5 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Thu, 30 Dec 2021 17:25:30 -0800 Subject: [PATCH 05/88] [Base] Add user-literals for several memory sizes Rather than using `n * 1024 * 1024`, this adds a convenient `_MiB`/`_KiB` user-literal to the new `literals.h` header to concisely describe units of memory in a much more readable way. Any other useful literals can be added to this header. These literals exist in the `xe::literals` namespace so they are opt-in, similar to `std::chrono` literals, and require a `using namespace xe::literals` statement to utilize it within the current scope. I've done a pass through the codebase to replace trivial instances of `1024 * 1024 * ...` expressions being used but avoided anything that added additional casting complexity from `size_t` to `uint32_t` and such to keep this commit concise. --- src/xenia/base/arena.cc | 2 +- src/xenia/base/arena.h | 6 ++- src/xenia/base/literals.h | 39 +++++++++++++++++++ src/xenia/base/logging.cc | 6 ++- src/xenia/base/string_buffer.cc | 7 +++- src/xenia/base/testing/threading_test.cc | 2 +- src/xenia/base/threading.h | 5 ++- src/xenia/cpu/backend/x64/x64_code_cache.cc | 7 +++- src/xenia/cpu/backend/x64/x64_emitter.cc | 4 +- src/xenia/cpu/ppc/testing/ppc_testing_main.cc | 4 +- src/xenia/cpu/processor.cc | 7 +++- src/xenia/emulator.cc | 6 ++- src/xenia/gpu/d3d12/deferred_command_list.h | 5 ++- src/xenia/gpu/vulkan/texture_cache.cc | 5 ++- .../gpu/vulkan/vulkan_command_processor.cc | 4 +- src/xenia/kernel/xthread.cc | 7 +++- src/xenia/ui/graphics_upload_buffer_pool.h | 6 ++- src/xenia/vfs/devices/disc_image_device.cc | 7 +++- src/xenia/vfs/vfs_dump.cc | 5 ++- 19 files changed, 109 insertions(+), 25 deletions(-) create mode 100644 src/xenia/base/literals.h diff --git a/src/xenia/base/arena.cc b/src/xenia/base/arena.cc index 66184f0f2..809b72af7 100644 --- a/src/xenia/base/arena.cc +++ b/src/xenia/base/arena.cc @@ -60,7 +60,7 @@ void* Arena::Alloc(size_t size, size_t align) { if (active_chunk_) { if (active_chunk_->capacity - active_chunk_->offset < - size + get_padding() + 4096) { + size + get_padding() + 4_KiB) { Chunk* next = active_chunk_->next; if (!next) { assert_true(size + get_padding() < chunk_size_, diff --git a/src/xenia/base/arena.h b/src/xenia/base/arena.h index 7dbdb7c2a..204fa555c 100644 --- a/src/xenia/base/arena.h +++ b/src/xenia/base/arena.h @@ -14,11 +14,15 @@ #include #include +#include "xenia/base/literals.h" + namespace xe { +using namespace xe::literals; + class Arena { public: - explicit Arena(size_t chunk_size = 4 * 1024 * 1024); + explicit Arena(size_t chunk_size = 4_MiB); ~Arena(); void Reset(); diff --git a/src/xenia/base/literals.h b/src/xenia/base/literals.h new file mode 100644 index 000000000..d4b0a66b6 --- /dev/null +++ b/src/xenia/base/literals.h @@ -0,0 +1,39 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_BASE_LITERALS_H_ +#define XENIA_BASE_LITERALS_H_ + +#include + +namespace xe::literals { + +constexpr size_t operator""_KiB(unsigned long long int x) { + return 1024ULL * x; +} + +constexpr size_t operator""_MiB(unsigned long long int x) { + return 1024_KiB * x; +} + +constexpr size_t operator""_GiB(unsigned long long int x) { + return 1024_MiB * x; +} + +constexpr size_t operator""_TiB(unsigned long long int x) { + return 1024_GiB * x; +} + +constexpr size_t operator""_PiB(unsigned long long int x) { + return 1024_TiB * x; +} + +} // namespace xe::literals + +#endif // XENIA_BASE_LITERALS_H_ diff --git a/src/xenia/base/logging.cc b/src/xenia/base/logging.cc index ef04ec192..d5b9ccef7 100644 --- a/src/xenia/base/logging.cc +++ b/src/xenia/base/logging.cc @@ -25,6 +25,7 @@ #include "xenia/base/cvar.h" #include "xenia/base/debugging.h" #include "xenia/base/filesystem.h" +#include "xenia/base/literals.h" #include "xenia/base/math.h" #include "xenia/base/memory.h" #include "xenia/base/platform.h" @@ -59,6 +60,7 @@ DEFINE_int32( "Logging"); namespace dp = disruptorplus; +using namespace xe::literals; namespace xe { @@ -74,7 +76,7 @@ struct LogLine { char prefix_char; }; -thread_local char thread_log_buffer_[64 * 1024]; +thread_local char thread_log_buffer_[64_KiB]; FileLogSink::~FileLogSink() { if (file_) { @@ -234,7 +236,7 @@ class Logger { } private: - static const size_t kBufferSize = 8 * 1024 * 1024; + static const size_t kBufferSize = 8_MiB; uint8_t buffer_[kBufferSize]; static const size_t kBlockSize = 256; diff --git a/src/xenia/base/string_buffer.cc b/src/xenia/base/string_buffer.cc index 050ddf9d8..b3a9270a8 100644 --- a/src/xenia/base/string_buffer.cc +++ b/src/xenia/base/string_buffer.cc @@ -13,12 +13,15 @@ #include #include "xenia/base/assert.h" +#include "xenia/base/literals.h" #include "xenia/base/math.h" namespace xe { +using namespace xe::literals; + StringBuffer::StringBuffer(size_t initial_capacity) { - buffer_capacity_ = std::max(initial_capacity, static_cast(16 * 1024)); + buffer_capacity_ = std::max(initial_capacity, static_cast(16_KiB)); buffer_ = reinterpret_cast(std::malloc(buffer_capacity_)); assert_not_null(buffer_); buffer_[0] = 0; @@ -40,7 +43,7 @@ void StringBuffer::Grow(size_t additional_length) { } size_t old_capacity = buffer_capacity_; size_t new_capacity = - std::max(xe::round_up(buffer_offset_ + additional_length, 16 * 1024), + std::max(xe::round_up(buffer_offset_ + additional_length, 16_KiB), old_capacity * 2); auto new_buffer = std::realloc(buffer_, new_capacity); assert_not_null(new_buffer); diff --git a/src/xenia/base/testing/threading_test.cc b/src/xenia/base/testing/threading_test.cc index 4bb87a518..d1380d602 100644 --- a/src/xenia/base/testing/threading_test.cc +++ b/src/xenia/base/testing/threading_test.cc @@ -824,7 +824,7 @@ TEST_CASE("Create and Run Thread", "[thread]") { } SECTION("16kb stack size") { - params.stack_size = 16 * 1024 * 1024; + params.stack_size = 16_MiB; thread = Thread::Create(params, [] { Thread::Exit(-1); FAIL("Function must not return"); diff --git a/src/xenia/base/threading.h b/src/xenia/base/threading.h index 520c2d531..63a63b4d4 100644 --- a/src/xenia/base/threading.h +++ b/src/xenia/base/threading.h @@ -25,11 +25,14 @@ #include #include "xenia/base/assert.h" +#include "xenia/base/literals.h" #include "xenia/base/platform.h" namespace xe { namespace threading { +using namespace xe::literals; + #if XE_PLATFORM_ANDROID void AndroidInitialize(); void AndroidShutdown(); @@ -368,7 +371,7 @@ struct ThreadPriority { class Thread : public WaitHandle { public: struct CreationParameters { - size_t stack_size = 4 * 1024 * 1024; + size_t stack_size = 4_MiB; bool create_suspended = false; int32_t initial_priority = 0; }; diff --git a/src/xenia/cpu/backend/x64/x64_code_cache.cc b/src/xenia/cpu/backend/x64/x64_code_cache.cc index beb715661..2b49e14a9 100644 --- a/src/xenia/cpu/backend/x64/x64_code_cache.cc +++ b/src/xenia/cpu/backend/x64/x64_code_cache.cc @@ -20,6 +20,7 @@ #include "third_party/fmt/include/fmt/format.h" #include "xenia/base/assert.h" #include "xenia/base/clock.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/memory.h" @@ -31,6 +32,8 @@ namespace cpu { namespace backend { namespace x64 { +using namespace xe::literals; + X64CodeCache::X64CodeCache() = default; X64CodeCache::~X64CodeCache() { @@ -227,7 +230,7 @@ void X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code, old_commit_mark = generated_code_commit_mark_; if (high_mark <= old_commit_mark) break; - new_commit_mark = old_commit_mark + 16 * 1024 * 1024; + new_commit_mark = old_commit_mark + 16_MiB; if (generated_code_execute_base_ == generated_code_write_base_) { xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark, xe::memory::AllocationType::kCommit, @@ -310,7 +313,7 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) { old_commit_mark = generated_code_commit_mark_; if (high_mark <= old_commit_mark) break; - new_commit_mark = old_commit_mark + 16 * 1024 * 1024; + new_commit_mark = old_commit_mark + 16_MiB; if (generated_code_execute_base_ == generated_code_write_base_) { xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark, xe::memory::AllocationType::kCommit, diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index f3419bb05..2e36b2285 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -18,6 +18,7 @@ #include "xenia/base/assert.h" #include "xenia/base/atomic.h" #include "xenia/base/debugging.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/memory.h" @@ -50,8 +51,9 @@ namespace x64 { using xe::cpu::hir::HIRBuilder; using xe::cpu::hir::Instr; +using namespace xe::literals; -static const size_t kMaxCodeSize = 1 * 1024 * 1024; +static const size_t kMaxCodeSize = 1_MiB; static const size_t kStashOffset = 32; // static const size_t kStashOffsetHigh = 32 + 32; diff --git a/src/xenia/cpu/ppc/testing/ppc_testing_main.cc b/src/xenia/cpu/ppc/testing/ppc_testing_main.cc index b2da8aff7..dbd184327 100644 --- a/src/xenia/cpu/ppc/testing/ppc_testing_main.cc +++ b/src/xenia/cpu/ppc/testing/ppc_testing_main.cc @@ -10,6 +10,7 @@ #include "xenia/base/console_app_main.h" #include "xenia/base/cvar.h" #include "xenia/base/filesystem.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/platform.h" @@ -36,6 +37,7 @@ namespace cpu { namespace test { using xe::cpu::ppc::PPCContext; +using namespace xe::literals; typedef std::vector> AnnotationList; @@ -177,7 +179,7 @@ class TestSuite { class TestRunner { public: - TestRunner() : memory_size_(64 * 1024 * 1024) { + TestRunner() : memory_size_(64_MiB) { memory_.reset(new Memory()); memory_->Initialize(); } diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 6aae55a6c..2fe459dc4 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -16,6 +16,7 @@ #include "xenia/base/cvar.h" #include "xenia/base/debugging.h" #include "xenia/base/exception_handler.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/memory.h" #include "xenia/base/profiling.h" @@ -57,6 +58,8 @@ namespace cpu { using xe::cpu::ppc::PPCOpcode; using xe::kernel::XThread; +using namespace xe::literals; + class BuiltinModule : public Module { public: explicit BuiltinModule(Processor* processor) @@ -142,8 +145,8 @@ bool Processor::Setup(std::unique_ptr backend) { // Open the trace data path, if requested. functions_trace_path_ = cvars::trace_function_data_path; if (!functions_trace_path_.empty()) { - functions_trace_file_ = ChunkedMappedMemoryWriter::Open( - functions_trace_path_, 32 * 1024 * 1024, true); + functions_trace_file_ = + ChunkedMappedMemoryWriter::Open(functions_trace_path_, 32_MiB, true); } return true; diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 7bb4109e4..0af32710c 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -20,6 +20,7 @@ #include "xenia/base/cvar.h" #include "xenia/base/debugging.h" #include "xenia/base/exception_handler.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/mapped_memory.h" #include "xenia/base/profiling.h" @@ -60,6 +61,8 @@ DEFINE_string( namespace xe { +using namespace xe::literals; + Emulator::Emulator(const std::filesystem::path& command_line, const std::filesystem::path& storage_root, const std::filesystem::path& content_root, @@ -415,8 +418,7 @@ bool Emulator::SaveToFile(const std::filesystem::path& path) { Pause(); filesystem::CreateFile(path); - auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite, 0, - 1024ull * 1024ull * 1024ull * 2ull); + auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite, 0, 2_GiB); if (!map) { return false; } diff --git a/src/xenia/gpu/d3d12/deferred_command_list.h b/src/xenia/gpu/d3d12/deferred_command_list.h index a21d0c3c4..22b4fc5da 100644 --- a/src/xenia/gpu/d3d12/deferred_command_list.h +++ b/src/xenia/gpu/d3d12/deferred_command_list.h @@ -16,6 +16,7 @@ #include #include "xenia/base/assert.h" +#include "xenia/base/literals.h" #include "xenia/base/math.h" #include "xenia/ui/d3d12/d3d12_api.h" @@ -23,12 +24,14 @@ namespace xe { namespace gpu { namespace d3d12 { +using namespace xe::literals; + class D3D12CommandProcessor; class DeferredCommandList { public: DeferredCommandList(const D3D12CommandProcessor& command_processor, - size_t initial_size_bytes = 1024 * 1024); + size_t initial_size_bytes = 1_MiB); void Reset(); void Execute(ID3D12GraphicsCommandList* command_list, diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index 4e26d0aa2..866c51bd2 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -12,6 +12,7 @@ #include #include "third_party/fmt/include/fmt/format.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/memory.h" @@ -36,8 +37,10 @@ namespace vulkan { using xe::ui::vulkan::CheckResult; +using namespace xe::literals; + constexpr uint32_t kMaxTextureSamplers = 32; -constexpr VkDeviceSize kStagingBufferSize = 64 * 1024 * 1024; +constexpr VkDeviceSize kStagingBufferSize = 64_MiB; const char* get_dimension_name(xenos::DataDimension dimension) { static const char* names[] = { diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index 41e91886c..d9da7d9bd 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -27,10 +27,12 @@ namespace xe { namespace gpu { namespace vulkan { +using namespace xe::literals; using namespace xe::gpu::xenos; + using xe::ui::vulkan::CheckResult; -constexpr size_t kDefaultBufferCacheCapacity = 256 * 1024 * 1024; +constexpr size_t kDefaultBufferCacheCapacity = 256_MiB; VulkanCommandProcessor::VulkanCommandProcessor( VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state) diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 43303c04e..9c269c6b7 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -14,6 +14,7 @@ #include "third_party/fmt/include/fmt/format.h" #include "xenia/base/byte_stream.h" #include "xenia/base/clock.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/profiling.h" @@ -41,6 +42,8 @@ const uint32_t XAPC::kDummyRundownRoutine; using xe::cpu::ppc::PPCOpcode; +using namespace xe::literals; + uint32_t next_xthread_id_ = 0; XThread::XThread(KernelState* kernel_state) @@ -370,7 +373,7 @@ X_STATUS XThread::Create() { RetainHandle(); xe::threading::Thread::CreationParameters params; - params.stack_size = 16 * 1024 * 1024; // Allocate a big host stack. + params.stack_size = 16_MiB; // Allocate a big host stack. params.create_suspended = true; thread_ = xe::threading::Thread::Create(params, [this]() { // Set thread ID override. This is used by logging. @@ -1018,7 +1021,7 @@ object_ref XThread::Restore(KernelState* kernel_state, xe::threading::Thread::CreationParameters params; params.create_suspended = true; // Not done restoring yet. - params.stack_size = 16 * 1024 * 1024; + params.stack_size = 16_MiB; thread->thread_ = xe::threading::Thread::Create(params, [thread, state]() { // Set thread ID override. This is used by logging. xe::threading::set_current_thread_id(thread->handle()); diff --git a/src/xenia/ui/graphics_upload_buffer_pool.h b/src/xenia/ui/graphics_upload_buffer_pool.h index 7f5a714d3..250eda70b 100644 --- a/src/xenia/ui/graphics_upload_buffer_pool.h +++ b/src/xenia/ui/graphics_upload_buffer_pool.h @@ -13,9 +13,13 @@ #include #include +#include "xenia/base/literals.h" + namespace xe { namespace ui { +using namespace xe::literals; + // Submission index is the fence value or a value derived from it (if reclaiming // less often than once per fence value, for instance). @@ -23,7 +27,7 @@ class GraphicsUploadBufferPool { public: // Taken from the Direct3D 12 MiniEngine sample (LinearAllocator // kCpuAllocatorPageSize). Large enough for most cases. - static constexpr size_t kDefaultPageSize = 2 * 1024 * 1024; + static constexpr size_t kDefaultPageSize = 2_MiB; virtual ~GraphicsUploadBufferPool(); diff --git a/src/xenia/vfs/devices/disc_image_device.cc b/src/xenia/vfs/devices/disc_image_device.cc index d5ae0c15f..561d0cef7 100644 --- a/src/xenia/vfs/devices/disc_image_device.cc +++ b/src/xenia/vfs/devices/disc_image_device.cc @@ -9,6 +9,7 @@ #include "xenia/vfs/devices/disc_image_device.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/vfs/devices/disc_image_entry.h" @@ -16,7 +17,9 @@ namespace xe { namespace vfs { -const size_t kXESectorSize = 2048; +using namespace xe::literals; + +const size_t kXESectorSize = 2_KiB; DiscImageDevice::DiscImageDevice(const std::string_view mount_path, const std::filesystem::path& host_path) @@ -89,7 +92,7 @@ DiscImageDevice::Error DiscImageDevice::Verify(ParseState* state) { state->root_size = xe::load(fs_ptr + 24); state->root_offset = state->game_offset + (state->root_sector * kXESectorSize); - if (state->root_size < 13 || state->root_size > 32 * 1024 * 1024) { + if (state->root_size < 13 || state->root_size > 32_MiB) { return Error::kErrorDamagedFile; } diff --git a/src/xenia/vfs/vfs_dump.cc b/src/xenia/vfs/vfs_dump.cc index 2bca814bb..f2416b59a 100644 --- a/src/xenia/vfs/vfs_dump.cc +++ b/src/xenia/vfs/vfs_dump.cc @@ -13,6 +13,7 @@ #include "xenia/base/console_app_main.h" #include "xenia/base/cvar.h" +#include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" @@ -22,6 +23,8 @@ namespace xe { namespace vfs { +using namespace xe::literals; + DEFINE_transient_path(source, "", "Specifies the file to dump from.", "General"); @@ -91,7 +94,7 @@ int vfs_dump_main(const std::vector& args) { } // Allocate a buffer rounded up to the nearest 512MB. - buffer_size = xe::round_up(entry->size(), 512 * 1024 * 1024); + buffer_size = xe::round_up(entry->size(), 512_MiB); buffer = new uint8_t[buffer_size]; } From 531790752393650a0c7d8d1585eeed7526908805 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Tue, 28 Dec 2021 18:11:44 -0800 Subject: [PATCH 06/88] [x64] Add `kX64EmitAVX512*` feature-flags Implements the detection of some baseline `AVX512` subsets and some common aliases into `X64EmitterFeatureFlags`. So far, `AVX512{F,VL,BW,DQ}` are the only subsets of `AVX512` that are detected with this PR since I anticipate these are the ones that will actually be used a lot in the x64 backend. Some aliases are also implemented such as `kX64EmitAVX512Ortho` which is `AVX512F` and `AVX512VL` combined which are the two subsets of AVX512 required to allow for `AVX512` operations upon `ymm` and `xmm` registers. These aliases can possibly be collapsed since we could just always require `AVX512VL` to be supported to allow for _any_ kind of `AVX512` to be used since we will practically always want to use `AVX512` on `xmm` registers at the very least as there is no use-case where we want to use the 512-bit `zmm` registers exclusively. --- src/xenia/cpu/backend/x64/x64_emitter.cc | 8 ++++++++ src/xenia/cpu/backend/x64/x64_emitter.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index 2e36b2285..8884b9a56 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -81,6 +81,14 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tBMI2) ? kX64EmitBMI2 : 0; feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tF16C) ? kX64EmitF16C : 0; feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tMOVBE) ? kX64EmitMovbe : 0; + feature_flags_ |= + cpu_.has(Xbyak::util::Cpu::tAVX512F) ? kX64EmitAVX512F : 0; + feature_flags_ |= + cpu_.has(Xbyak::util::Cpu::tAVX512VL) ? kX64EmitAVX512VL : 0; + feature_flags_ |= + cpu_.has(Xbyak::util::Cpu::tAVX512BW) ? kX64EmitAVX512BW : 0; + feature_flags_ |= + cpu_.has(Xbyak::util::Cpu::tAVX512DQ) ? kX64EmitAVX512DQ : 0; } if (!cpu_.has(Xbyak::util::Cpu::tAVX)) { diff --git a/src/xenia/cpu/backend/x64/x64_emitter.h b/src/xenia/cpu/backend/x64/x64_emitter.h index 088a56d6d..a00a68306 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.h +++ b/src/xenia/cpu/backend/x64/x64_emitter.h @@ -131,6 +131,14 @@ enum X64EmitterFeatureFlags { kX64EmitBMI2 = 1 << 4, kX64EmitF16C = 1 << 5, kX64EmitMovbe = 1 << 6, + + kX64EmitAVX512F = 1 << 7, + kX64EmitAVX512VL = 1 << 8, + kX64EmitAVX512Ortho = kX64EmitAVX512F | kX64EmitAVX512VL, + + kX64EmitAVX512BW = 1 << 9, + kX64EmitAVX512DQ = 1 << 10, + kX64EmitAVX512Ortho64 = kX64EmitAVX512Ortho | kX64EmitAVX512DQ }; class X64Emitter : public Xbyak::CodeGenerator { From f645c3ba316364ea31aa181a45b849c80f62ba8f Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Sun, 2 Jan 2022 12:45:13 -0800 Subject: [PATCH 07/88] [Base] Fix `to_hex_string` out-of-indexing for `vec128_t` type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trying to print five `{:08X}` when vec128_t only has four values. 🥴 --- src/xenia/base/string_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xenia/base/string_util.h b/src/xenia/base/string_util.h index 3c73f9c65..09575ff56 100644 --- a/src/xenia/base/string_util.h +++ b/src/xenia/base/string_util.h @@ -134,7 +134,7 @@ inline std::string to_hex_string(double value) { } inline std::string to_hex_string(const vec128_t& value) { - return fmt::format("[{:08X} {:08X} {:08X} {:08X} {:08X}]", value.u32[0], + return fmt::format("[{:08X} {:08X} {:08X} {:08X}]", value.u32[0], value.u32[1], value.u32[2], value.u32[3]); } From 13a48e13bd7bdcfa7f87abb21a9ee49eb27665f9 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Sun, 2 Jan 2022 12:40:02 -0800 Subject: [PATCH 08/88] [Base] Add `operator<<` string conversion for `vec128_t` This allows `catch` to print out the contents of a particular vector when diagnosing how a `REQUIRE` expression has failed. --- src/xenia/base/vec128.cc | 7 +++++++ src/xenia/base/vec128.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/xenia/base/vec128.cc b/src/xenia/base/vec128.cc index 70562dda1..5d91f7b5f 100644 --- a/src/xenia/base/vec128.cc +++ b/src/xenia/base/vec128.cc @@ -8,11 +8,13 @@ */ #include +#include #include #include "third_party/fmt/include/fmt/format.h" #include "xenia/base/math.h" #include "xenia/base/platform.h" +#include "xenia/base/string_util.h" #include "xenia/base/vec128.h" namespace xe { @@ -21,4 +23,9 @@ std::string to_string(const vec128_t& value) { return fmt::format("({}, {}, {}, {})", value.x, value.y, value.z, value.w); } +std::ostream& operator<<(std::ostream& os, const vec128_t& value) { + os << string_util::to_hex_string(value); + return os; +} + } // namespace xe diff --git a/src/xenia/base/vec128.h b/src/xenia/base/vec128.h index cfba512a0..c9fc4cc13 100644 --- a/src/xenia/base/vec128.h +++ b/src/xenia/base/vec128.h @@ -257,6 +257,8 @@ static inline vec128_t vec128b(uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, std::string to_string(const vec128_t& value); +std::ostream& operator<<(std::ostream& os, const vec128_t& value); + } // namespace xe #endif // XENIA_BASE_VEC128_H_ From 094c20bd4e9e1e57943880e8943f6409840c2818 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Sun, 2 Jan 2022 15:43:59 -0600 Subject: [PATCH 09/88] Update premake-core. --- third_party/premake-core | 2 +- tools/build/bin/premake5.exe | Bin 1395200 -> 1405440 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/premake-core b/third_party/premake-core index 7eba28258..fe71eb790 160000 --- a/third_party/premake-core +++ b/third_party/premake-core @@ -1 +1 @@ -Subproject commit 7eba2825887e49d3a72b30e0a7480bd427a5bab0 +Subproject commit fe71eb790c7d085cd3c6a7b71a50167b4da06e69 diff --git a/tools/build/bin/premake5.exe b/tools/build/bin/premake5.exe index 2a01f91d769c894088dcc4849ab7568a98b9936f..4196ad17537d34cc2681f37aeb06d2920ea33fff 100644 GIT binary patch delta 193683 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

O?AXzh*em=<@Q$ubP$IefP%#qO}nV; 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 RMYjy8AAL7FU!KwY{{dw)l(GN- delta 187701 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

Axai?kc;8%8>UfAlmP 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ν{@0R;?k!{j<(isF# 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 n+*X-IasCRhc7xfgGFhAS@S1e6 Date: Sun, 2 Jan 2022 15:49:16 -0600 Subject: [PATCH 10/88] Replace premake5 binary with CI artifact. --- tools/build/bin/premake5.exe | Bin 1405440 -> 1561600 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/build/bin/premake5.exe b/tools/build/bin/premake5.exe index 4196ad17537d34cc2681f37aeb06d2920ea33fff..68a7099346494655121a6cc560c82e749785a09a 100644 GIT binary patch literal 1561600 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%0D8(0W@}&b?KneDu8_C#zDV2Gmmp@p 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�|bP%_JT5y_gEq9CrzjNlVV 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�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%0DL@Gz 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

ypfj&4>ou}nEA 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 Date: Thu, 23 Sep 2021 15:41:12 +0200 Subject: [PATCH 11/88] [xenia-build] Update clang-format version to 13 --- xenia-build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xenia-build b/xenia-build index 0104eac7d..2efb2400e 100755 --- a/xenia-build +++ b/xenia-build @@ -430,7 +430,7 @@ def get_clang_format_binary(): attempts = [ 'C:\\Program Files\\LLVM\\bin\\clang-format.exe', 'C:\\Program Files (x86)\\LLVM\\bin\\clang-format.exe', - 'clang-format-9', + 'clang-format-13', 'clang-format', ] for binary in attempts: @@ -438,7 +438,7 @@ def get_clang_format_binary(): return binary print('ERROR: clang-format is not on PATH') print('LLVM is available from https://llvm.org/releases/download.html') - print('At least version 9 is required.') + print('At least version 13 is required.') print('See docs/style_guide.md for instructions on how to get it.') sys.exit(1) From 657645fb2c6bd214ee0b3d242bb53ef900c67c6b Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Thu, 23 Sep 2021 11:57:47 +0200 Subject: [PATCH 12/88] [CI, Drone] Add GCC builds * Switch to starlark language to simplify configuration * Use image `xeniaproject/buildenv:2022-01-01` --- .appveyor.yml | 3 +- .drone.star | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++ .drone.yml | 214 ------------------------------------------ 3 files changed, 255 insertions(+), 216 deletions(-) create mode 100644 .drone.star delete mode 100644 .drone.yml diff --git a/.appveyor.yml b/.appveyor.yml index 54ad4f01b..ad7a5db18 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -8,9 +8,8 @@ skip_tags: true skip_commits: files: - - .drone.yml + - .drone.star - .github/** - - .travis.yml - docs/** - src/**/*_posix.* - src/**/*_linux.* diff --git a/.drone.star b/.drone.star new file mode 100644 index 000000000..840008295 --- /dev/null +++ b/.drone.star @@ -0,0 +1,254 @@ +def main(ctx): + return [ + pipeline_lint(), + pipeline_linux_desktop('x86_64-linux-clang', image_linux_x86_64(), 'amd64', 'clang'), + pipeline_linux_desktop('x86_64-linux-gcc', image_linux_x86_64(), 'amd64', 'gcc'), + ] + +def image_linux_x86_64(): + return 'xeniaproject/buildenv:2022-01-01' + +def volume_build(toolchain, path='/drone/src/build'): + return { + 'name': 'build-' + toolchain, + 'path': path, + } + +def command_cc(cc): + # set CC, CXX, ... + return 'export $(cat /{}.env | sed \'s/#.*//g\' | xargs)'.format(cc) + +# Run lint in a separate pipeline so that it will try building even if lint fails +def pipeline_lint(): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': 'lint', + 'steps': [ + { + 'name': 'lint', + 'image': image_linux_x86_64(), + 'commands': [ + 'clang-format --version', + './xenia-build lint --all', + ], + }, + ], + } + +def pipeline_linux_desktop(name, image, arch, cc): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': name, + 'platform': { + 'os': 'linux', + 'arch': arch, + }, + # These volumes will be mounted at the build directory, allowing to + # run different premake toolchains from the same source tree + 'volumes': [ + { + 'name': 'build-premake', + 'temp': {}, + }, + { + 'name': 'build-cmake', + 'temp': {}, + }, + ], + + 'steps': [ + # + # Setup the source tree + # + { + 'name': 'clone-submodules', + 'image': image, + 'commands': [ + 'pwd', + # May miss recursive submodules (but faster than xb setup) + 'git submodule update --init --depth 1 -j $(nproc)', + ], + }, + + + # + # Setup the two build systems + # + + # Native premake Makefiles for production + { + 'name': 'toolchain-premake', + 'image': image, + 'volumes': [volume_build('premake')], + 'commands': [ + command_cc(cc), + '$CXX --version', + 'python3 --version', + './xenia-build premake --cc={}'.format(cc), + ], + 'depends_on': ['clone-submodules'], + }, + + # Development toolchain + { + 'name': 'toolchain-cmake', + 'image': image, + 'volumes': [volume_build('cmake')], + 'commands': [ + command_cc(cc), + ''' + ./xenia-build premake --cc={} --devenv=cmake + cd build + for c in Debug Release + do + mkdir cmake-$c + cd cmake-$c + cmake -DCMAKE_BUILD_TYPE=$c .. + cd .. + done + '''.format(cc), + ], + # Premake itself needs to be build first: + 'depends_on': ['toolchain-premake'], + }, + + # + # Building + # + { + 'name': 'build-premake-debug-all', + 'image': image, + 'volumes': [volume_build('premake')], + 'commands': [ + command_cc(cc), + './xenia-build build --no_premake -j$(nproc) --config=Debug', + ], + 'depends_on': ['toolchain-premake'], + }, + + { + 'name': 'build-premake-release-tests', + 'image': image, + 'volumes': [volume_build('premake')], + 'commands': [ + command_cc(cc), + './xenia-build build --no_premake -j$(nproc) --config=Release --target=xenia-base-tests', + ], + 'depends_on': ['toolchain-premake'], + }, + + { + 'name': 'build-premake-release-all', + 'image': image, + 'volumes': [volume_build('premake')], + 'commands': [ + command_cc(cc), + './xenia-build build --no_premake -j$(nproc) --config=Release', + ], + 'depends_on': ['build-premake-release-tests'], + }, + + { + 'name': 'build-cmake-debug-all', + 'image': image, + 'volumes': [volume_build('cmake')], + 'commands': [ + command_cc(cc), + 'cd build/cmake-Debug', + 'cmake --build . -j$(nproc)', + ], + 'depends_on': ['toolchain-cmake'], + }, + + { + 'name': 'build-cmake-release-tests', + 'image': image, + 'volumes': [volume_build('cmake')], + 'commands': [ + command_cc(cc), + 'cd build/cmake-Release', + 'cmake --build . -j$(nproc) --target xenia-base-tests', + ], + 'depends_on': ['toolchain-cmake'], + }, + + { + 'name': 'build-cmake-release-all', + 'image': image, + 'volumes': [volume_build('cmake')], + 'commands': [ + command_cc(cc), + 'cd build/cmake-Release', + 'cmake --build . -j$(nproc)', + ], + 'depends_on': ['build-cmake-release-tests'], + }, + + + # + # Tests + # + { + 'name': 'test-premake', + 'image': image, + 'volumes': [volume_build('premake')], + 'commands': [ + './build/bin/Linux/Release/xenia-base-tests', + ], + 'depends_on': ['build-premake-release-tests'], + }, + + { + 'name': 'test-cmake', + 'image': image, + 'volumes': [volume_build('cmake')], + 'commands': [ + './build/bin/Linux/Release/xenia-base-tests', + ], + 'depends_on': ['build-cmake-release-tests'], + }, + + + # + # Stat + # + { + 'name': 'stat', + 'image': image, + 'volumes': [ + volume_build('premake', '/build-premake'), + volume_build('cmake', '/build-cmake'), + ], + 'commands': [ + ''' + header() { + SEP='============================================================' + echo + echo $SEP + echo $@ + echo $SEP + } + + for v in premake cmake + do + for c in Debug Release + do + header $v $c + p=/build-$v/bin/Linux/$c + ls -la $p + sha256sum $p/* + done + done + ''' + ], + 'depends_on': [ + 'build-premake-debug-all', + 'build-premake-release-all', + 'build-cmake-debug-all', + 'build-cmake-release-all', + ], + }, + ], + } diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index a8c2c21d3..000000000 --- a/.drone.yml +++ /dev/null @@ -1,214 +0,0 @@ ---- -kind: pipeline -type: docker -name: lint - -# Run this in a separate pipeline so that it will build even if this fails -steps: -- name: lint - image: xeniaproject/buildenv:2021-06-21 - commands: - - clang-format --version - - ./xenia-build lint --all - ---- -kind: pipeline -type: docker -name: x86_64-linux -platform: - os: linux - arch: amd64 - -# Some expressions in this file are duplicates. Scripting support is -# available using jsonnet but increases complexity -# https://docs.drone.io/pipeline/scripting/jsonnet/ - -# These volumes will be mounted at the build directory, allowing to -# run different premake toolchains from the same source tree -volumes: -- name: build-premake - temp: {} -- name: build-cmake - temp: {} - -steps: -# -# Setup the source tree -# -- name: clone-submodules - image: xeniaproject/buildenv:2021-06-21 - commands: - - pwd - # May miss recursive submodules (but faster than xb setup) - - git submodule update --init --depth 1 -j $(nproc) - - -# -# Setup the two build systems -# - -# Native premake Makefiles for production -- name: toolchain-premake - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-premake - path: /drone/src/build - commands: - - $CXX --version - - $AR --version - - python3 --version - - ./xenia-build premake - depends_on: - - clone-submodules - -# Development toolchain -- name: toolchain-cmake - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-cmake - path: /drone/src/build - commands: - - | - ./xenia-build premake --devenv=cmake - cd build - for c in Debug Release - do - mkdir cmake-$c - cd cmake-$c - cmake -DCMAKE_BUILD_TYPE=$c .. - cd .. - done - depends_on: - # Premake itself needs to be build first: - - toolchain-premake - - -# -# Building -# - -- name: build-premake-debug-all - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-premake - path: /drone/src/build - commands: - - ./xenia-build build --no_premake -j$(nproc) --config=Debug - depends_on: - - toolchain-premake - -- name: build-premake-release-tests - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-premake - path: /drone/src/build - commands: - - ./xenia-build build --no_premake -j$(nproc) --config=Release --target=xenia-base-tests - depends_on: - - toolchain-premake - -- name: build-premake-release-all - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-premake - path: /drone/src/build - commands: - - ./xenia-build build --no_premake -j$(nproc) --config=Release - depends_on: - - build-premake-release-tests - -- name: build-cmake-debug-all - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-cmake - path: /drone/src/build - commands: - - cd build/cmake-Debug - - cmake --build . -j$(nproc) - depends_on: - - toolchain-cmake - -- name: build-cmake-release-tests - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-cmake - path: /drone/src/build - commands: - - cd build/cmake-Release - - cmake --build . -j$(nproc) --target xenia-base-tests - depends_on: - - toolchain-cmake - -- name: build-cmake-release-all - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-cmake - path: /drone/src/build - commands: - - cd build/cmake-Release - - cmake --build . -j$(nproc) - depends_on: - - build-cmake-release-tests - - -# -# Tests -# - -- name: test-premake - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-premake - path: /drone/src/build - commands: - - ./build/bin/Linux/Release/xenia-base-tests - depends_on: - - build-premake-release-tests - -- name: test-cmake - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-cmake - path: /drone/src/build - commands: - - ./build/bin/Linux/Release/xenia-base-tests - depends_on: - - build-cmake-release-tests - - -# -# Stat -# - -- name: stat - image: xeniaproject/buildenv:2021-06-21 - volumes: - - name: build-premake - path: /build-premake - - name: build-cmake - path: /build-cmake - commands: - - | - header() { - SEP='============================================================' - echo - echo $SEP - echo $@ - echo $SEP - } - - for v in premake cmake - do - for c in Debug Release - do - header $v $c - p=/build-$v/bin/Linux/$c - ls -la $p - sha256sum $p/* - done - done - depends_on: - - build-premake-debug-all - - build-premake-release-all - - build-cmake-debug-all - - build-cmake-release-all From 59cce10ae1bd3287af84cb4a743eff16d757902f Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Mon, 27 Dec 2021 21:36:58 +0100 Subject: [PATCH 13/88] [CI, Drone] Add Android NDK builds --- .drone.star | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/.drone.star b/.drone.star index 840008295..09d933902 100644 --- a/.drone.star +++ b/.drone.star @@ -3,6 +3,8 @@ def main(ctx): pipeline_lint(), pipeline_linux_desktop('x86_64-linux-clang', image_linux_x86_64(), 'amd64', 'clang'), pipeline_linux_desktop('x86_64-linux-gcc', image_linux_x86_64(), 'amd64', 'gcc'), + pipeline_android('x86_64-android', image_linux_x86_64(), 'amd64', 'Android-x86_64'), + pipeline_android('aarch64-android', image_linux_x86_64(), 'amd64', 'Android-ARM64'), ] def image_linux_x86_64(): @@ -18,6 +20,72 @@ def command_cc(cc): # set CC, CXX, ... return 'export $(cat /{}.env | sed \'s/#.*//g\' | xargs)'.format(cc) +def command_ndk_build(platform, configuration, target): + return '$ANDROID_NDK_ROOT/build/ndk-build NDK_PROJECT_PATH:=./bin/{configuration} NDK_APPLICATION_MK:=./xenia.Application.mk PREMAKE_ANDROIDNDK_PLATFORMS:={platform} PREMAKE_ANDROIDNDK_CONFIGURATIONS:={configuration} -j$(nproc) {target}'.format(platform=platform, configuration=configuration, target=target) + +def targets_android(platform): + targets = [ + 'aes_128', + 'capstone', + 'dxbc', + 'discord-rpc', + 'cxxopts', + 'cpptoml', + 'avcodec', + 'avutil', + 'fmt', + 'glslang-spirv', + 'imgui', + 'mspack', + 'snappy', + 'spirv-tools', + 'xxhash', + # 'xenia-core', + # 'xenia-app-discord', + # 'xenia-apu', + # 'xenia-apu-nop', + 'xenia-base', + 'xenia-base-tests', + # 'xenia-cpu', + # 'xenia-cpu-tests', + # 'xenia-cpu-ppc-tests', + # 'xenia-cpu-backend-x64', + # 'xenia-debug-ui', + # 'xenia-gpu', + # 'xenia-gpu-shader-compiler', + # 'xenia-gpu-null', + # 'xenia-gpu-vulkan', + # 'xenia-gpu-vulkan-trace-viewer', + # 'xenia-gpu-vulkan-trace-dump', + 'xenia-hid', + # 'xenia-hid-demo', + 'xenia-hid-nop', + # 'xenia-kernel', + 'xenia-ui', + 'xenia-ui-spirv', + # 'xenia-ui-vulkan', + # 'xenia-ui-window-vulkan-demo', + 'xenia-vfs', + 'xenia-vfs-dump', + ] + if platform == 'Android-x86_64': + targets.extend([ + 'xenia-core', + 'xenia-apu', + 'xenia-apu-nop', + 'xenia-cpu', + 'xenia-cpu-tests', + 'xenia-cpu-ppc-tests', + 'xenia-cpu-backend-x64', + 'xenia-debug-ui', + 'xenia-gpu', + 'xenia-gpu-null', + 'xenia-gpu-vulkan', + 'xenia-gpu-shader-compiler', + 'xenia-kernel', + ]) + return targets + # Run lint in a separate pipeline so that it will try building even if lint fails def pipeline_lint(): return { @@ -252,3 +320,103 @@ def pipeline_linux_desktop(name, image, arch, cc): }, ], } + + +def pipeline_android(name, image, arch, platform): + return { + 'kind': 'pipeline', + 'type': 'docker', + 'name': name, + 'platform': { + 'os': 'linux', + 'arch': arch, + }, + + 'steps': [ + # + # Setup the source tree + # + { + 'name': 'clone-submodules', + 'image': image, + 'commands': [ + 'pwd', + # May miss recursive submodules (but faster than xb setup) + 'git submodule update --init --depth 1 -j $(nproc)', + ], + }, + + + # + # Build premake and generate NDK makefiles + # + + # NDK Makefiles + { + 'name': 'toolchain', + 'image': image, + 'commands': [ + 'c++ --version', + 'python3 --version', + './xenia-build premake --target_os android', + ], + 'depends_on': ['clone-submodules'], + }, + + + # + # Building + # + { + 'name': 'build-debug', + 'image': image, + 'commands': [ + 'cd build', + command_ndk_build(platform, 'Debug', ' '.join(targets_android(platform))), + ], + 'depends_on': ['toolchain'], + }, + + { + 'name': 'build-release', + 'image': image, + 'commands': [ + 'cd build', + command_ndk_build(platform, 'Release', ' '.join(targets_android(platform))), + ], + 'depends_on': ['toolchain'], + }, + + + # + # Stat + # + { + 'name': 'stat', + 'image': image, + 'commands': [ + ''' + header() { + SEP='============================================================' + echo + echo $SEP + echo $@ + echo $SEP + } + + for c in Debug Release + do + header $c + p=build/bin/$c/obj/local/* + ls -la $p + sha256sum $p/* || true + done + ''' + ], + 'depends_on': [ + 'build-debug', + 'build-release', + ], + }, + ], + } From 4f258b2ee933648b4ef9dbddcfac75f58b465171 Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Tue, 28 Dec 2021 11:11:57 +0100 Subject: [PATCH 14/88] [GPU, Vulkan] Fix typo in non AMD64 code * `copy_and_swap_16_unaligned` -> `copy_cmp_swap_16_unaligned`. --- src/xenia/gpu/vulkan/buffer_cache.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xenia/gpu/vulkan/buffer_cache.cc b/src/xenia/gpu/vulkan/buffer_cache.cc index 426fad033..0e4b73163 100644 --- a/src/xenia/gpu/vulkan/buffer_cache.cc +++ b/src/xenia/gpu/vulkan/buffer_cache.cc @@ -70,7 +70,7 @@ void copy_cmp_swap_32_unaligned(void* dest_ptr, const void* src_ptr, } } #else -void copy_and_swap_16_unaligned(void* dest_ptr, const void* src_ptr, +void copy_cmp_swap_16_unaligned(void* dest_ptr, const void* src_ptr, uint16_t cmp_value, size_t count) { auto dest = reinterpret_cast(dest_ptr); auto src = reinterpret_cast(src_ptr); @@ -80,7 +80,7 @@ void copy_and_swap_16_unaligned(void* dest_ptr, const void* src_ptr, } } -void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr, +void copy_cmp_swap_32_unaligned(void* dest_ptr, const void* src_ptr, uint32_t cmp_value, size_t count) { auto dest = reinterpret_cast(dest_ptr); auto src = reinterpret_cast(src_ptr); From 0fdb855a11552d865beca9ba647502e53cf63ce1 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Sun, 2 Jan 2022 16:16:14 -0800 Subject: [PATCH 15/88] [JIT, x64] Add and implement `OPCODE_AND_NOT` Verified the x64 implementation using `xenia-cpu-ppc-tests`. --- src/xenia/cpu/backend/x64/x64_sequences.cc | 74 ++++++++++++++++++++++ src/xenia/cpu/hir/hir_builder.cc | 20 ++++++ src/xenia/cpu/hir/hir_builder.h | 1 + src/xenia/cpu/hir/opcodes.h | 1 + src/xenia/cpu/hir/opcodes.inl | 6 ++ src/xenia/cpu/ppc/ppc_emit_altivec.cc | 6 +- src/xenia/cpu/ppc/ppc_emit_alu.cc | 2 +- 7 files changed, 106 insertions(+), 4 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index 37a05c60b..90471b248 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -2627,6 +2627,80 @@ struct AND_V128 : Sequence> { }; EMITTER_OPCODE_TABLE(OPCODE_AND, AND_I8, AND_I16, AND_I32, AND_I64, AND_V128); +// ============================================================================ +// OPCODE_AND_NOT +// ============================================================================ +template +void EmitAndNotXX(X64Emitter& e, const ARGS& i) { + if (i.src1.is_constant) { + if (i.src2.is_constant) { + // Both constants. + e.mov(i.dest, i.src1.constant() & ~i.src2.constant()); + } else { + // src1 constant. + e.mov(i.dest, i.src2.constant()); + e.not_(i.dest); + e.and_(i.dest, i.src1); + } + } else if (i.src2.is_constant) { + // src2 constant. + if (i.dest == i.src1) { + auto temp = GetTempReg(e); + e.mov(temp, ~i.src2.constant()); + e.and_(i.dest, temp); + } else { + e.mov(i.dest, i.src1); + auto temp = GetTempReg(e); + e.mov(temp, ~i.src2.constant()); + e.and_(i.dest, temp); + } + } else { + // neither are constant + if (i.dest == i.src2) { + e.not_(i.dest); + e.and_(i.dest, i.src1); + } else { + e.mov(i.dest, i.src2); + e.not_(i.dest); + e.and_(i.dest, i.src1); + } + } +} +struct AND_NOT_I8 : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitAndNotXX(e, i); + } +}; +struct AND_NOT_I16 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitAndNotXX(e, i); + } +}; +struct AND_NOT_I32 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitAndNotXX(e, i); + } +}; +struct AND_NOT_I64 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitAndNotXX(e, i); + } +}; +struct AND_NOT_V128 + : Sequence> { + static void Emit(X64Emitter& e, const EmitArgType& i) { + EmitCommutativeBinaryXmmOp(e, i, + [](X64Emitter& e, Xmm dest, Xmm src1, Xmm src2) { + e.vpandn(dest, src2, src1); + }); + } +}; +EMITTER_OPCODE_TABLE(OPCODE_AND_NOT, AND_NOT_I8, AND_NOT_I16, AND_NOT_I32, + AND_NOT_I64, AND_NOT_V128); + // ============================================================================ // OPCODE_OR // ============================================================================ diff --git a/src/xenia/cpu/hir/hir_builder.cc b/src/xenia/cpu/hir/hir_builder.cc index fc64b7c2e..3cc2c9aaa 100644 --- a/src/xenia/cpu/hir/hir_builder.cc +++ b/src/xenia/cpu/hir/hir_builder.cc @@ -1759,6 +1759,26 @@ Value* HIRBuilder::And(Value* value1, Value* value2) { return i->dest; } +Value* HIRBuilder::AndNot(Value* value1, Value* value2) { + ASSERT_NON_FLOAT_TYPE(value1); + ASSERT_NON_FLOAT_TYPE(value2); + ASSERT_TYPES_EQUAL(value1, value2); + + if (value1 == value2) { + return LoadZero(value1->type); + } else if (value1->IsConstantZero()) { + return value1; + } else if (value2->IsConstantZero()) { + return value1; + } + + Instr* i = AppendInstr(OPCODE_AND_NOT_info, 0, AllocValue(value1->type)); + i->set_src1(value1); + i->set_src2(value2); + i->src3.value = NULL; + return i->dest; +} + Value* HIRBuilder::Or(Value* value1, Value* value2) { ASSERT_NON_FLOAT_TYPE(value1); ASSERT_NON_FLOAT_TYPE(value2); diff --git a/src/xenia/cpu/hir/hir_builder.h b/src/xenia/cpu/hir/hir_builder.h index 81c406c12..b2809d5d8 100644 --- a/src/xenia/cpu/hir/hir_builder.h +++ b/src/xenia/cpu/hir/hir_builder.h @@ -224,6 +224,7 @@ class HIRBuilder { Value* DotProduct4(Value* value1, Value* value2); Value* And(Value* value1, Value* value2); + Value* AndNot(Value* value1, Value* value2); Value* Or(Value* value1, Value* value2); Value* Xor(Value* value1, Value* value2); Value* Not(Value* value); diff --git a/src/xenia/cpu/hir/opcodes.h b/src/xenia/cpu/hir/opcodes.h index 1649ec9dc..6f45bb8da 100644 --- a/src/xenia/cpu/hir/opcodes.h +++ b/src/xenia/cpu/hir/opcodes.h @@ -255,6 +255,7 @@ enum Opcode { OPCODE_DOT_PRODUCT_3, OPCODE_DOT_PRODUCT_4, OPCODE_AND, + OPCODE_AND_NOT, OPCODE_OR, OPCODE_XOR, OPCODE_NOT, diff --git a/src/xenia/cpu/hir/opcodes.inl b/src/xenia/cpu/hir/opcodes.inl index 9ee033aa5..584b0ac55 100644 --- a/src/xenia/cpu/hir/opcodes.inl +++ b/src/xenia/cpu/hir/opcodes.inl @@ -524,6 +524,12 @@ DEFINE_OPCODE( OPCODE_SIG_V_V_V, OPCODE_FLAG_COMMUNATIVE) +DEFINE_OPCODE( + OPCODE_AND_NOT, + "and_not", + OPCODE_SIG_V_V_V, + 0) + DEFINE_OPCODE( OPCODE_OR, "or", diff --git a/src/xenia/cpu/ppc/ppc_emit_altivec.cc b/src/xenia/cpu/ppc/ppc_emit_altivec.cc index 93f4df2b7..331713f10 100644 --- a/src/xenia/cpu/ppc/ppc_emit_altivec.cc +++ b/src/xenia/cpu/ppc/ppc_emit_altivec.cc @@ -286,7 +286,7 @@ int InstrEmit_stvlx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, // mask = FFFF... >> eb Value* mask = f.Permute(f.LoadVectorShr(eb), f.LoadZeroVec128(), f.Not(f.LoadZeroVec128()), INT8_TYPE); - Value* v = f.Or(f.And(old_value, f.Not(mask)), f.And(new_value, mask)); + Value* v = f.Or(f.AndNot(old_value, mask), f.And(new_value, mask)); // ea &= ~0xF (handled above) f.Store(ea, f.ByteSwap(v)); return 0; @@ -328,7 +328,7 @@ int InstrEmit_stvrx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, // mask = ~FFFF... >> eb Value* mask = f.Permute(f.LoadVectorShr(eb), f.Not(f.LoadZeroVec128()), f.LoadZeroVec128(), INT8_TYPE); - Value* v = f.Or(f.And(old_value, f.Not(mask)), f.And(new_value, mask)); + Value* v = f.Or(f.AndNot(old_value, mask), f.And(new_value, mask)); // ea &= ~0xF (handled above) f.Store(ea, f.ByteSwap(v)); f.MarkLabel(skip_label); @@ -459,7 +459,7 @@ int InstrEmit_vand128(PPCHIRBuilder& f, const InstrData& i) { int InstrEmit_vandc_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // VD <- (VA) & ¬(VB) - Value* v = f.And(f.LoadVR(va), f.Not(f.LoadVR(vb))); + Value* v = f.AndNot(f.LoadVR(va), f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } diff --git a/src/xenia/cpu/ppc/ppc_emit_alu.cc b/src/xenia/cpu/ppc/ppc_emit_alu.cc index 78f51b106..4be752936 100644 --- a/src/xenia/cpu/ppc/ppc_emit_alu.cc +++ b/src/xenia/cpu/ppc/ppc_emit_alu.cc @@ -657,7 +657,7 @@ int InstrEmit_andx(PPCHIRBuilder& f, const InstrData& i) { int InstrEmit_andcx(PPCHIRBuilder& f, const InstrData& i) { // RA <- (RS) & ¬(RB) - Value* ra = f.And(f.LoadGPR(i.X.RT), f.Not(f.LoadGPR(i.X.RB))); + Value* ra = f.AndNot(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB)); f.StoreGPR(i.X.RA, ra); if (i.X.Rc) { f.UpdateCR(0, ra); From 3ab43d480d36c06a9cc818848388a20d7a3995db Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Sun, 2 Jan 2022 16:28:26 -0800 Subject: [PATCH 16/88] [x64] Add `kX64EmitBMI1` feature-flag and detection The `BMI1 feature` fits into the current pattern of `use_haswell_instructions` as BMI1 was only introduced in haswell. Also moved the aliases to the end of the enum rather than interleave it with the bit definitions. --- src/xenia/cpu/backend/x64/x64_emitter.cc | 1 + src/xenia/cpu/backend/x64/x64_emitter.h | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index 8884b9a56..ae9ba1eed 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -78,6 +78,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tAVX2) ? kX64EmitAVX2 : 0; feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tFMA) ? kX64EmitFMA : 0; feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tLZCNT) ? kX64EmitLZCNT : 0; + feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tBMI1) ? kX64EmitBMI1 : 0; feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tBMI2) ? kX64EmitBMI2 : 0; feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tF16C) ? kX64EmitF16C : 0; feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tMOVBE) ? kX64EmitMovbe : 0; diff --git a/src/xenia/cpu/backend/x64/x64_emitter.h b/src/xenia/cpu/backend/x64/x64_emitter.h index a00a68306..b84162d34 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.h +++ b/src/xenia/cpu/backend/x64/x64_emitter.h @@ -128,16 +128,18 @@ enum X64EmitterFeatureFlags { kX64EmitAVX2 = 1 << 1, kX64EmitFMA = 1 << 2, kX64EmitLZCNT = 1 << 3, - kX64EmitBMI2 = 1 << 4, - kX64EmitF16C = 1 << 5, - kX64EmitMovbe = 1 << 6, + kX64EmitBMI1 = 1 << 4, + kX64EmitBMI2 = 1 << 5, + kX64EmitF16C = 1 << 6, + kX64EmitMovbe = 1 << 7, + + kX64EmitAVX512F = 1 << 8, + kX64EmitAVX512VL = 1 << 9, + + kX64EmitAVX512BW = 1 << 10, + kX64EmitAVX512DQ = 1 << 11, - kX64EmitAVX512F = 1 << 7, - kX64EmitAVX512VL = 1 << 8, kX64EmitAVX512Ortho = kX64EmitAVX512F | kX64EmitAVX512VL, - - kX64EmitAVX512BW = 1 << 9, - kX64EmitAVX512DQ = 1 << 10, kX64EmitAVX512Ortho64 = kX64EmitAVX512Ortho | kX64EmitAVX512DQ }; From 24d4e1e0e55333cc4561bb39bd306f7a21af0506 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Sun, 2 Jan 2022 16:56:04 -0800 Subject: [PATCH 17/88] [x64] Add `BMI1`-based acceleration for `AndNot` In the case of having two register operands for `AndNot`, the `andn` instruction can be used when the host supports `BMI1`. `andn` only supports 32-bit and 64-bit operands, so some register up-casting is needed. --- src/xenia/cpu/backend/x64/x64_sequences.cc | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index 90471b248..15ddebef3 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -2656,13 +2656,23 @@ void EmitAndNotXX(X64Emitter& e, const ARGS& i) { } } else { // neither are constant - if (i.dest == i.src2) { - e.not_(i.dest); - e.and_(i.dest, i.src1); + if (e.IsFeatureEnabled(kX64EmitBMI1)) { + if (i.dest.reg().getBit() == 64) { + e.andn(i.dest.reg().cvt64(), i.src2.reg().cvt64(), + i.src1.reg().cvt64()); + } else { + e.andn(i.dest.reg().cvt32(), i.src2.reg().cvt32(), + i.src1.reg().cvt32()); + } } else { - e.mov(i.dest, i.src2); - e.not_(i.dest); - e.and_(i.dest, i.src1); + if (i.dest == i.src2) { + e.not_(i.dest); + e.and_(i.dest, i.src1); + } else { + e.mov(i.dest, i.src2); + e.not_(i.dest); + e.and_(i.dest, i.src1); + } } } } From 6ea8e043f3aee4de819231659bc86ad399baf5f7 Mon Sep 17 00:00:00 2001 From: Margen67 Date: Wed, 15 Dec 2021 04:45:29 -0800 Subject: [PATCH 18/88] [AppVeyor] Cleanup Remove unneeded init: As far as I can tell this is a leftover from the appveyor.yml Reference: https://www.appveyor.com/docs/appveyor-yml/ Cleanup command blocks: cmd is the default shell, so it doesn't have to be specified. Use proper multi-line syntax for install. Make configuration into one line: This reduces line count, but is mainly personal preference. --- .appveyor.yml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index ad7a5db18..429e0e6ce 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -25,24 +25,20 @@ pull_requests: os: Visual Studio 2019 -init: - - git config --global core.autocrlf input - install: - - cmd: vcpkg integrate remove - - cmd: xb setup + - | + vcpkg integrate remove + xb setup platform: Windows -configuration: - - Release - - Checked +configuration: [Release, Checked] build_script: - - cmd: xb build --config=%CONFIGURATION% --target=src\xenia-app --target=tests\xenia-base-tests --target=tests\xenia-cpu-ppc-tests --target=src\xenia-vfs-dump + - xb build --config=%CONFIGURATION% --target=src\xenia-app --target=tests\xenia-base-tests --target=tests\xenia-cpu-ppc-tests --target=src\xenia-vfs-dump after_build: - - cmd: | + - | IF NOT "%CONFIGURATION%"=="Checked" SET "ARCHIVE_SUFFIX=%APPVEYOR_REPO_BRANCH%" IF NOT "%CONFIGURATION%"=="Checked" SET "ARCHIVE_SWITCHES=--" IF "%CONFIGURATION%"=="Checked" SET "ARCHIVE_SUFFIX=%APPVEYOR_REPO_BRANCH%_FOR-DEVS-ONLY" @@ -51,10 +47,10 @@ after_build: 7z a xenia-vfs-dump_%ARCHIVE_SUFFIX%.zip %ARCHIVE_SWITCHES% LICENSE "%APPVEYOR_BUILD_FOLDER%\build\bin\%PLATFORM%\%CONFIGURATION%\xenia-vfs-dump.exe" "%APPVEYOR_BUILD_FOLDER%\build\bin\%PLATFORM%\%CONFIGURATION%\xenia-vfs-dump.pdb" before_test: - - cmd: xb gentests + - xb gentests test_script: - - cmd: xb test --config=%CONFIGURATION% --no_build + - xb test --config=%CONFIGURATION% --no_build artifacts: - path: '*.zip' From 1ba4fbec17c8bbcd643b99d39b7f230f91d50990 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Sun, 7 Nov 2021 22:30:17 +0100 Subject: [PATCH 19/88] [Kernel/XMP] Remove responsibility of stopping audio when controller is changed --- src/xenia/kernel/xam/apps/xmp_app.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/xenia/kernel/xam/apps/xmp_app.cc b/src/xenia/kernel/xam/apps/xmp_app.cc index c492192ab..142d579ae 100644 --- a/src/xenia/kernel/xam/apps/xmp_app.cc +++ b/src/xenia/kernel/xam/apps/xmp_app.cc @@ -406,9 +406,6 @@ X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t(args->controller), uint32_t(args->locked)); disabled_ = args->locked; - if (disabled_) { - XMPStop(0); - } kernel_state_->BroadcastNotification(kMsgDisableChanged, disabled_); return X_E_SUCCESS; } From 20fe7bc4b77ea9b5551bf870df4e49cfec4cfc3c Mon Sep 17 00:00:00 2001 From: Gliniak Date: Mon, 8 Nov 2021 09:24:04 +0100 Subject: [PATCH 20/88] [Kernel/XMP] Send correct notification when playback controller is changed - Changed locked into playback_client enumerator - Changed vague notification name to something more descriptive --- src/xenia/kernel/xam/apps/xmp_app.cc | 18 +++++++++--------- src/xenia/kernel/xam/apps/xmp_app.h | 8 ++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/xenia/kernel/xam/apps/xmp_app.cc b/src/xenia/kernel/xam/apps/xmp_app.cc index 142d579ae..2f42c1a12 100644 --- a/src/xenia/kernel/xam/apps/xmp_app.cc +++ b/src/xenia/kernel/xam/apps/xmp_app.cc @@ -21,11 +21,11 @@ namespace apps { XmpApp::XmpApp(KernelState* kernel_state) : App(kernel_state, 0xFA), state_(State::kIdle), - disabled_(0), + playback_client_(PlaybackClient::kTitle), playback_mode_(PlaybackMode::kUnknown), repeat_mode_(RepeatMode::kUnknown), unknown_flags_(0), - volume_(0.0f), + volume_(1.0f), active_playlist_(nullptr), active_song_index_(0), next_playlist_handle_(1), @@ -131,9 +131,8 @@ X_HRESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle, playlist = it->second; } - if (disabled_) { - // Ignored because we aren't enabled? - XELOGW("Ignoring XMPPlayTitlePlaylist because disabled"); + if (playback_client_ == PlaybackClient::kSystem) { + XELOGW("XMPPlayTitlePlaylist: System playback is enabled!"); return X_E_SUCCESS; } @@ -395,7 +394,7 @@ X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, struct { xe::be xmp_client; xe::be controller; - xe::be locked; + xe::be playback_client; }* args = memory_->TranslateVirtual(buffer_ptr); static_assert_size(decltype(*args), 12); @@ -403,10 +402,11 @@ X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, (args->xmp_client == 0x00000002 && args->controller == 0x00000000) || (args->xmp_client == 0x00000000 && args->controller == 0x00000001)); XELOGD("XMPSetPlaybackController({:08X}, {:08X})", - uint32_t(args->controller), uint32_t(args->locked)); + uint32_t(args->controller), uint32_t(args->playback_client)); - disabled_ = args->locked; - kernel_state_->BroadcastNotification(kMsgDisableChanged, disabled_); + playback_client_ = PlaybackClient(uint32_t(args->playback_client)); + kernel_state_->BroadcastNotification(kMsgPlaybackControllerChanged, + !args->playback_client); return X_E_SUCCESS; } case 0x0007001B: { diff --git a/src/xenia/kernel/xam/apps/xmp_app.h b/src/xenia/kernel/xam/apps/xmp_app.h index cb4966a00..6a365c2f2 100644 --- a/src/xenia/kernel/xam/apps/xmp_app.h +++ b/src/xenia/kernel/xam/apps/xmp_app.h @@ -34,6 +34,10 @@ class XmpApp : public App { kPlaying = 1, kPaused = 2, }; + enum class PlaybackClient : uint32_t { + kSystem = 0, + kTitle = 1, + }; enum class PlaybackMode : uint32_t { // kInOrder = ?, kUnknown = 0, @@ -90,12 +94,12 @@ class XmpApp : public App { private: static const uint32_t kMsgStateChanged = 0x0A000001; static const uint32_t kMsgPlaybackBehaviorChanged = 0x0A000002; - static const uint32_t kMsgDisableChanged = 0x0A000003; + static const uint32_t kMsgPlaybackControllerChanged = 0x0A000003; void OnStateChanged(); State state_; - uint32_t disabled_; + PlaybackClient playback_client_; PlaybackMode playback_mode_; RepeatMode repeat_mode_; uint32_t unknown_flags_; From 4303f6b200d991eb24be78d46065f36b8066eabf Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Thu, 6 Jan 2022 10:02:33 -0800 Subject: [PATCH 21/88] [x64] Fix OPCODE_AND_NOT src1-constant case Fix the the case where src1 is constant and src2 is non-constant causing an assert due to trying to call `.constant()` on the src2 operand. Interfaces with an issue Gliniak was encountering where title `4D53082D` encounters an assert. Also includes a BMI1-acceleration in the 64-bit case where a temporary register is needed(the `and` x86 instruction only supports immediate constants up to 32-bits). --- src/xenia/cpu/backend/x64/x64_sequences.cc | 26 +++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index 15ddebef3..638e63364 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -2638,9 +2638,29 @@ void EmitAndNotXX(X64Emitter& e, const ARGS& i) { e.mov(i.dest, i.src1.constant() & ~i.src2.constant()); } else { // src1 constant. - e.mov(i.dest, i.src2.constant()); - e.not_(i.dest); - e.and_(i.dest, i.src1); + + // `and` instruction only supports up to 32-bit immediate constants + // 64-bit constants will need a temp register + if (i.dest.reg().getBit() == 64) { + auto temp = GetTempReg(e); + e.mov(temp, i.src1.constant()); + + if (e.IsFeatureEnabled(kX64EmitBMI1)) { + if (i.dest.reg().getBit() == 64) { + e.andn(i.dest.reg().cvt64(), i.src2.reg().cvt64(), temp.cvt64()); + } else { + e.andn(i.dest.reg().cvt32(), i.src2.reg().cvt32(), temp.cvt32()); + } + } else { + e.mov(i.dest, i.src2); + e.not_(i.dest); + e.and_(i.dest, temp); + } + } else { + e.mov(i.dest, i.src2); + e.not_(i.dest); + e.and_(i.dest, uint32_t(i.src1.constant())); + } } } else if (i.src2.is_constant) { // src2 constant. From fa774f1d865172ecdd21236a1356452f7d101665 Mon Sep 17 00:00:00 2001 From: gibbed Date: Fri, 7 Jan 2022 06:14:44 -0600 Subject: [PATCH 22/88] [xboxkrnl] Fix up XexGetProcedureAddress logging. [xboxkrnl] Fix up XexGetProcedureAddress failure logging. --- src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc index 41f4b5d54..6d789079d 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc @@ -166,7 +166,15 @@ dword_result_t XexGetProcedureAddress(lpvoid_t hmodule, dword_t ordinal, *out_function_ptr = ptr; result = X_STATUS_SUCCESS; } else { - XELOGW("ERROR: XexGetProcedureAddress ordinal not found!"); + if (is_string_name) { + XELOGW("ERROR: XexGetProcedureAddress export '{}' in '{}' not found!", + string_name, module->name()); + } else { + XELOGW( + "ERROR: XexGetProcedureAddress ordinal {} (0x{:X}) in '{}' not " + "found!", + ordinal, ordinal, module->name()); + } *out_function_ptr = 0; result = X_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND; } From b4f35635c5661fc6f4669583830f0b7d36d904d5 Mon Sep 17 00:00:00 2001 From: gibbed Date: Fri, 7 Jan 2022 09:12:54 -0600 Subject: [PATCH 23/88] [xboxkrnl] ExAcquireReadWriteLockExclusive fixes. [xboxkrnl] ExAcquireReadWriteLockExclusive fixes: - Don't unnecessarily double-load lock count. - Don't release spin lock before we're done with the lock. --- src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index 3a323d579..178dbe447 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -1184,18 +1184,20 @@ DECLARE_XBOXKRNL_EXPORT1(ExInitializeReadWriteLock, kThreading, kImplemented); void ExAcquireReadWriteLockExclusive(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); - lock_ptr->lock_count++; - xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); - if (!lock_ptr->lock_count) { + int32_t lock_count = ++lock_ptr->lock_count; + if (!lock_count) { + xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); return; } lock_ptr->writers_waiting_count++; - xeKeWaitForSingleObject(&lock_ptr->writer_event, 0, 0, 0, nullptr); + + xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); + xeKeWaitForSingleObject(&lock_ptr->writer_event, 7, 0, 0, nullptr); } -DECLARE_XBOXKRNL_EXPORT3(ExAcquireReadWriteLockExclusive, kThreading, - kImplemented, kBlocking, kSketchy); +DECLARE_XBOXKRNL_EXPORT2(ExAcquireReadWriteLockExclusive, kThreading, + kImplemented, kBlocking); void ExReleaseReadWriteLock(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); From e795337071c162bfff276037801f49acfb9064c5 Mon Sep 17 00:00:00 2001 From: gibbed Date: Fri, 7 Jan 2022 09:18:10 -0600 Subject: [PATCH 24/88] [xboxkrnl] ExReleaseReadWriteLock fixes. [xboxkrnl] ExReleaseReadWriteLock fixes: - Don't unncessarily double-load lock members. - Reset readers entry count when lock count becomes negative. - Properly decrease writers waiting count when writer event fired. --- .../kernel/xboxkrnl/xboxkrnl_threading.cc | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index 178dbe447..62c445635 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -1201,9 +1201,11 @@ DECLARE_XBOXKRNL_EXPORT2(ExAcquireReadWriteLockExclusive, kThreading, void ExReleaseReadWriteLock(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); - lock_ptr->lock_count--; - if (lock_ptr->lock_count < 0) { + int32_t lock_count = --lock_ptr->lock_count; + + if (lock_count < 0) { + lock_ptr->readers_entry_count = 0; xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); return; } @@ -1220,14 +1222,17 @@ void ExReleaseReadWriteLock(pointer_t lock_ptr) { } } - auto count = lock_ptr->readers_entry_count--; - xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); - if (!count) { - xeKeSetEvent(&lock_ptr->writer_event, 1, 0); + auto readers_entry_count = --lock_ptr->readers_entry_count; + if (readers_entry_count) { + xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); + return; } + + lock_ptr->writers_waiting_count--; + xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); + xeKeSetEvent(&lock_ptr->writer_event, 1, 0); } -DECLARE_XBOXKRNL_EXPORT2(ExReleaseReadWriteLock, kThreading, kImplemented, - kSketchy); +DECLARE_XBOXKRNL_EXPORT1(ExReleaseReadWriteLock, kThreading, kImplemented); // NOTE: This function is very commonly inlined, and probably won't be called! pointer_result_t InterlockedPushEntrySList( From 3162a6435cef7211a7d4062ba2e76951e1bd9063 Mon Sep 17 00:00:00 2001 From: gibbed Date: Fri, 7 Jan 2022 09:19:15 -0600 Subject: [PATCH 25/88] [xboxkrnl] Implement ExTryToAcquireRWLExclusive. [xboxkrnl] Implement ExTryToAcquireReadWriteLockExclusive. --- .../kernel/xboxkrnl/xboxkrnl_threading.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index 62c445635..163118cfc 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -1199,6 +1199,24 @@ void ExAcquireReadWriteLockExclusive(pointer_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT2(ExAcquireReadWriteLockExclusive, kThreading, kImplemented, kBlocking); +dword_result_t ExTryToAcquireReadWriteLockExclusive( + pointer_t lock_ptr) { + auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); + + uint32_t result; + if (lock_ptr->lock_count < 0) { + lock_ptr->lock_count = 0; + result = 1; + } else { + result = 0; + } + + xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); + return result; +} +DECLARE_XBOXKRNL_EXPORT1(ExTryToAcquireReadWriteLockExclusive, kThreading, + kImplemented); + void ExReleaseReadWriteLock(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); From 1f9c434b5ecadc92496c906f8fbcd07a13bc3c42 Mon Sep 17 00:00:00 2001 From: gibbed Date: Fri, 7 Jan 2022 09:19:47 -0600 Subject: [PATCH 26/88] [xboxkrnl] Implement ExAcquireRWLShared. [xboxkrnl] Implement ExAcquireReadWriteLockShared. --- .../kernel/xboxkrnl/xboxkrnl_threading.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index 163118cfc..c59a21dc8 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -1217,6 +1217,25 @@ dword_result_t ExTryToAcquireReadWriteLockExclusive( DECLARE_XBOXKRNL_EXPORT1(ExTryToAcquireReadWriteLockExclusive, kThreading, kImplemented); +void ExAcquireReadWriteLockShared(pointer_t lock_ptr) { + auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); + + int32_t lock_count = ++lock_ptr->lock_count; + if (!lock_count || + (lock_ptr->readers_entry_count && !lock_ptr->writers_waiting_count)) { + lock_ptr->readers_entry_count++; + xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); + return; + } + + lock_ptr->readers_waiting_count++; + + xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); + xeKeWaitForSingleObject(&lock_ptr->reader_semaphore, 7, 0, 0, nullptr); +} +DECLARE_XBOXKRNL_EXPORT2(ExAcquireReadWriteLockShared, kThreading, kImplemented, + kBlocking); + void ExReleaseReadWriteLock(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); From 600c14b3f01803ea55653c59046a2250b9a0511d Mon Sep 17 00:00:00 2001 From: gibbed Date: Fri, 7 Jan 2022 09:20:09 -0600 Subject: [PATCH 27/88] [xboxknrl] Implement ExTryToAcquireRWLShared. [xboxknrl] Implement ExTryToAcquireReadWriteLockShared. --- .../kernel/xboxkrnl/xboxkrnl_threading.cc | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index c59a21dc8..0fa37bda8 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -1236,6 +1236,26 @@ void ExAcquireReadWriteLockShared(pointer_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT2(ExAcquireReadWriteLockShared, kThreading, kImplemented, kBlocking); +dword_result_t ExTryToAcquireReadWriteLockShared( + pointer_t lock_ptr) { + auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); + + uint32_t result; + if (lock_ptr->lock_count < 0 || + (lock_ptr->readers_entry_count && !lock_ptr->writers_waiting_count)) { + lock_ptr->lock_count++; + lock_ptr->readers_entry_count++; + result = 1; + } else { + result = 0; + } + + xeKeKfReleaseSpinLock(&lock_ptr->spin_lock, old_irql); + return result; +} +DECLARE_XBOXKRNL_EXPORT1(ExTryToAcquireReadWriteLockShared, kThreading, + kImplemented); + void ExReleaseReadWriteLock(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); From d6188c5d7ebcd1c2ee2a014dcbc6accb83b5ad7a Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 9 Jan 2022 14:58:38 +0300 Subject: [PATCH 28/88] [GPU] Reuse base+index*stride in vfetch_mini instead of reloading the index GPR The wheel shader in 4D530910 does vfetch_full to r0 with the index from r0.x, and then vfetch_mini. Thanks @Gliniak for the finding :3 Also small formatting cleanup in commented-out code. --- src/xenia/gpu/dxbc_shader_translator.cc | 4 +- src/xenia/gpu/dxbc_shader_translator.h | 4 +- src/xenia/gpu/dxbc_shader_translator_fetch.cc | 124 +++++++++++------- src/xenia/gpu/shader.h | 7 + src/xenia/gpu/shader_translator.cc | 2 +- src/xenia/gpu/shader_translator_disasm.cc | 9 +- src/xenia/gpu/ucode.h | 19 ++- 7 files changed, 103 insertions(+), 66 deletions(-) diff --git a/src/xenia/gpu/dxbc_shader_translator.cc b/src/xenia/gpu/dxbc_shader_translator.cc index 9f52bf046..350ea6895 100644 --- a/src/xenia/gpu/dxbc_shader_translator.cc +++ b/src/xenia/gpu/dxbc_shader_translator.cc @@ -849,7 +849,7 @@ void DxbcShaderTranslator::StartTranslation() { system_temp_aL_ = PushSystemTemp(0b1111); system_temp_loop_count_ = PushSystemTemp(0b1111); system_temp_grad_h_lod_ = PushSystemTemp(0b1111); - system_temp_grad_v_ = PushSystemTemp(0b0111); + system_temp_grad_v_vfetch_address_ = PushSystemTemp(0b1111); // Zero general-purpose registers to prevent crashes when the game // references them after only initializing them conditionally. @@ -1039,7 +1039,7 @@ void DxbcShaderTranslator::CompleteShaderCode() { // - system_temp_aL_. // - system_temp_loop_count_. // - system_temp_grad_h_lod_. - // - system_temp_grad_v_. + // - system_temp_grad_v_vfetch_address_. PopSystemTemp(6); // Write memexported data to the shared memory UAV. diff --git a/src/xenia/gpu/dxbc_shader_translator.h b/src/xenia/gpu/dxbc_shader_translator.h index 3bdde19be..0a25cef21 100644 --- a/src/xenia/gpu/dxbc_shader_translator.h +++ b/src/xenia/gpu/dxbc_shader_translator.h @@ -1104,7 +1104,9 @@ class DxbcShaderTranslator : public ShaderTranslator { uint32_t system_temp_loop_count_; // Explicitly set texture gradients and LOD. uint32_t system_temp_grad_h_lod_; - uint32_t system_temp_grad_v_; + // .w stores `base + index * stride` in bytes from the last vfetch_full as it + // may be needed by vfetch_mini. + uint32_t system_temp_grad_v_vfetch_address_; // The bool constant number containing the condition for the currently // processed exec (or the last - unless a label has reset this), or diff --git a/src/xenia/gpu/dxbc_shader_translator_fetch.cc b/src/xenia/gpu/dxbc_shader_translator_fetch.cc index 192b29a33..63480b76c 100644 --- a/src/xenia/gpu/dxbc_shader_translator_fetch.cc +++ b/src/xenia/gpu/dxbc_shader_translator_fetch.cc @@ -59,47 +59,67 @@ void DxbcShaderTranslator::ProcessVertexFetchInstruction( // fetch constants on the CPU when proper bound checks are added - vfetch may // be conditional, so fetch constants may also be used conditionally. - // - Load the byte address in physical memory to system_temp_result_.w (so - // it's not overwritten by data loads until the last one). + // - Load the part of the byte address in the physical memory that is the same + // in vfetch_full and vfetch_mini to system_temp_grad_v_vfetch_address_.w + // (the index operand GPR must not be reloaded in vfetch_mini because it + // might have been overwritten previously, but that shouldn't have effect on + // vfetch_mini). - dxbc::Dest address_dest(dxbc::Dest::R(system_temp_result_, 0b1000)); - dxbc::Src address_src(dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW)); - if (instr.attributes.stride) { - // Convert the index to an integer by flooring or by rounding to the nearest - // (as floor(index + 0.5) because rounding to the nearest even makes no - // sense for addressing, both 1.5 and 2.5 would be 2). - // http://web.archive.org/web/20100302145413/http://msdn.microsoft.com:80/en-us/library/bb313960.aspx - { - bool index_operand_temp_pushed = false; - dxbc::Src index_operand( - LoadOperand(instr.operands[0], 0b0001, index_operand_temp_pushed) - .SelectFromSwizzled(0)); - if (instr.attributes.is_index_rounded) { - a_.OpAdd(address_dest, index_operand, dxbc::Src::LF(0.5f)); - a_.OpRoundNI(address_dest, address_src); - } else { - a_.OpRoundNI(address_dest, index_operand); - } - if (index_operand_temp_pushed) { - PopSystemTemp(); + dxbc::Src address_src( + dxbc::Src::R(system_temp_grad_v_vfetch_address_, dxbc::Src::kWWWW)); + if (!instr.is_mini_fetch) { + dxbc::Dest address_dest( + dxbc::Dest::R(system_temp_grad_v_vfetch_address_, 0b1000)); + if (instr.attributes.stride) { + // Convert the index to an integer by flooring or by rounding to the + // nearest (as floor(index + 0.5) because rounding to the nearest even + // makes no sense for addressing, both 1.5 and 2.5 would be 2). + { + bool index_operand_temp_pushed = false; + dxbc::Src index_operand( + LoadOperand(instr.operands[0], 0b0001, index_operand_temp_pushed) + .SelectFromSwizzled(0)); + if (instr.attributes.is_index_rounded) { + a_.OpAdd(address_dest, index_operand, dxbc::Src::LF(0.5f)); + a_.OpRoundNI(address_dest, address_src); + } else { + a_.OpRoundNI(address_dest, index_operand); + } + if (index_operand_temp_pushed) { + PopSystemTemp(); + } } + a_.OpFToI(address_dest, address_src); + // Extract the byte address from the fetch constant to + // system_temp_result_.w (which is not used yet). + a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b1000), + fetch_constant_src.SelectFromSwizzled(0), + dxbc::Src::LU(~uint32_t(3))); + // Merge the index and the base address. + a_.OpIMAd(address_dest, address_src, + dxbc::Src::LU(instr.attributes.stride * sizeof(uint32_t)), + dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW)); + } else { + // Fetching from the same location - extract the byte address of the + // beginning of the buffer. + a_.OpAnd(address_dest, fetch_constant_src.SelectFromSwizzled(0), + dxbc::Src::LU(~uint32_t(3))); } - a_.OpFToI(address_dest, address_src); - // Extract the byte address from the fetch constant to - // system_temp_result_.z. - a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b0100), - fetch_constant_src.SelectFromSwizzled(0), - dxbc::Src::LU(~uint32_t(3))); - // Merge the index and the base address. - a_.OpIMAd(address_dest, address_src, - dxbc::Src::LU(instr.attributes.stride * sizeof(uint32_t)), - dxbc::Src::R(system_temp_result_, dxbc::Src::kZZZZ)); - } else { - // Fetching from the same location - extract the byte address of the - // beginning of the buffer. - a_.OpAnd(address_dest, fetch_constant_src.SelectFromSwizzled(0), - dxbc::Src::LU(~uint32_t(3))); } + + dxbc::Dest address_temp_dest(dxbc::Dest::R(system_temp_result_, 0b1000)); + dxbc::Src address_temp_src( + dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW)); + + // - From now on, if any additional offset must be applied to the + // `base + index * stride` part of the address, it must be done by writing + // to system_temp_result_.w (address_temp_dest) instead of + // system_temp_grad_v_vfetch_address_.w (since it must stay the same for the + // vfetch_full and all its vfetch_mini invocations), and changing + // address_src to address_temp_src afterwards. system_temp_result_.w can be + // used for this purpose safely because it won't be overwritten until the + // last dword is loaded (after which the address won't be needed anymore). + // Add the word offset from the instruction (signed), plus the offset of the // first needed word within the element. uint32_t first_word_index; @@ -108,8 +128,9 @@ void DxbcShaderTranslator::ProcessVertexFetchInstruction( instr.attributes.offset + int32_t(first_word_index); if (first_word_buffer_offset) { // Add the constant word offset. - a_.OpIAdd(address_dest, address_src, + a_.OpIAdd(address_temp_dest, address_src, dxbc::Src::LI(first_word_buffer_offset * sizeof(uint32_t))); + address_src = address_temp_src; } // - Load needed words to system_temp_result_, words 0, 1, 2, 3 to X, Y, Z, W @@ -159,9 +180,10 @@ void DxbcShaderTranslator::ProcessVertexFetchInstruction( ~((uint32_t(1) << (word_index + word_count)) - uint32_t(1)); if (word_index != word_index_previous) { // Go to the word in the buffer. - a_.OpIAdd(address_dest, address_src, + a_.OpIAdd(address_temp_dest, address_src, dxbc::Src::LU((word_index - word_index_previous) * sizeof(uint32_t))); + address_src = address_temp_src; word_index_previous = word_index; } // Can ld_raw either to the first multiple components, or to any scalar @@ -592,7 +614,7 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction( case FetchOpcode::kSetTextureGradientsVert: { bool grad_operand_temp_pushed = false; a_.OpMov( - dxbc::Dest::R(system_temp_grad_v_, 0b0111), + dxbc::Dest::R(system_temp_grad_v_vfetch_address_, 0b0111), LoadOperand(instr.operands[0], 0b0111, grad_operand_temp_pushed)); if (grad_operand_temp_pushed) { PopSystemTemp(); @@ -1521,15 +1543,15 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction( // Extract gradient exponent biases from the fetch constant and merge // them with the LOD bias. a_.OpIBFE(dxbc::Dest::R(grad_h_lod_temp, 0b0011), dxbc::Src::LU(5), - dxbc::Src::LU(22, 27, 0, 0), - RequestTextureFetchConstantWord(tfetch_index, 4)); + dxbc::Src::LU(22, 27, 0, 0), + RequestTextureFetchConstantWord(tfetch_index, 4)); a_.OpIMAd(dxbc::Dest::R(grad_h_lod_temp, 0b0011), - dxbc::Src::R(grad_h_lod_temp), dxbc::Src::LI(int32_t(1) << 23), - dxbc::Src::LF(1.0f)); + dxbc::Src::R(grad_h_lod_temp), + dxbc::Src::LI(int32_t(1) << 23), dxbc::Src::LF(1.0f)); a_.OpMul(dxbc::Dest::R(grad_v_temp, 0b1000), lod_src, - dxbc::Src::R(grad_h_lod_temp, dxbc::Src::kYYYY)); + dxbc::Src::R(grad_h_lod_temp, dxbc::Src::kYYYY)); a_.OpMul(lod_dest, lod_src, - dxbc::Src::R(grad_h_lod_temp, dxbc::Src::kXXXX)); + dxbc::Src::R(grad_h_lod_temp, dxbc::Src::kXXXX)); #endif // Obtain the gradients and apply biases to them. if (instr.attributes.use_register_gradients) { @@ -1540,11 +1562,11 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction( // done in getCompTexLOD, so don't do it here too. #if 0 a_.OpMul(dxbc::Dest::R(grad_v_temp, grad_mask), - dxbc::Src::R(system_temp_grad_v_), - dxbc::Src::R(grad_v_temp, dxbc::Src::kWWWW)); + dxbc::Src::R(system_temp_grad_v_vfetch_address_), + dxbc::Src::R(grad_v_temp, dxbc::Src::kWWWW)); #else a_.OpMul(dxbc::Dest::R(grad_v_temp, grad_mask), - dxbc::Src::R(system_temp_grad_v_), lod_src); + dxbc::Src::R(system_temp_grad_v_vfetch_address_), lod_src); #endif // TODO(Triang3l): Are cube map register gradients unnormalized if // the coordinates themselves are unnormalized? @@ -1586,8 +1608,8 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction( // done in getCompTexLOD, so don't do it here too. #if 0 a_.OpMul(dxbc::Dest::R(grad_v_temp, grad_mask), - dxbc::Src::R(grad_v_temp), - dxbc::Src::R(grad_v_temp, dxbc::Src::kWWWW)); + dxbc::Src::R(grad_v_temp), + dxbc::Src::R(grad_v_temp, dxbc::Src::kWWWW)); #else a_.OpMul(dxbc::Dest::R(grad_v_temp, grad_mask), dxbc::Src::R(grad_v_temp), lod_src); diff --git a/src/xenia/gpu/shader.h b/src/xenia/gpu/shader.h index f7c52cab0..2ce81409a 100644 --- a/src/xenia/gpu/shader.h +++ b/src/xenia/gpu/shader.h @@ -440,6 +440,13 @@ struct ParsedVertexFetchInstruction { // Number of source operands. size_t operand_count = 0; // Describes each source operand. + // Note that for vfetch_mini, which inherits the operands from vfetch_full, + // the index operand register may been overwritten between the vfetch_full and + // the vfetch_mini (happens in 4D530910 for wheels), but that should have no + // effect on the index actually used for fetching. A copy of the index + // therefore must be stored by vfetch_full (the base address, stride and + // rounding may be pre-applied to it since they will be the same in the + // vfetch_full and all its vfetch_mini instructions). InstructionOperand operands[2]; struct Attributes { diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index b1d1a060e..612edbfbf 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -876,7 +876,7 @@ bool ParseVertexFetchInstruction(const VertexFetchInstruction& op, instr.attributes.stride = full_op.stride(); instr.attributes.exp_adjust = op.exp_adjust(); instr.attributes.prefetch_count = op.prefetch_count(); - instr.attributes.is_index_rounded = op.is_index_rounded(); + instr.attributes.is_index_rounded = full_op.is_index_rounded(); instr.attributes.is_signed = op.is_signed(); instr.attributes.is_integer = !op.is_normalized(); instr.attributes.signed_rf_mode = op.signed_rf_mode(); diff --git a/src/xenia/gpu/shader_translator_disasm.cc b/src/xenia/gpu/shader_translator_disasm.cc index cf7e94d52..8dd72413a 100644 --- a/src/xenia/gpu/shader_translator_disasm.cc +++ b/src/xenia/gpu/shader_translator_disasm.cc @@ -328,13 +328,12 @@ void ParsedVertexFetchInstruction::Disassemble(StringBuffer* out) const { if (!is_mini_fetch) { out->Append(", "); DisassembleSourceOperand(operands[0], out); - out->Append(", "); - out->AppendFormat("vf{}", 95 - operands[1].storage_index); + out->AppendFormat(", vf{}", 95 - operands[1].storage_index); + if (attributes.is_index_rounded) { + out->Append(", RoundIndex=true"); + } } - if (attributes.is_index_rounded) { - out->Append(", RoundIndex=true"); - } if (attributes.exp_adjust) { out->AppendFormat(", ExpAdjust={}", attributes.exp_adjust); } diff --git a/src/xenia/gpu/ucode.h b/src/xenia/gpu/ucode.h index 798fd5367..e86387535 100644 --- a/src/xenia/gpu/ucode.h +++ b/src/xenia/gpu/ucode.h @@ -599,6 +599,8 @@ struct alignas(uint32_t) VertexFetchInstruction { // Required condition value of the comparision (true or false). bool predicate_condition() const { return data_.pred_condition == 1; } // Vertex fetch constant index [0-95]. + // Applicable only to vfetch_full (the address from vfetch_full is reused in + // vfetch_mini). uint32_t fetch_constant_index() const { return data_.const_index * 3 + data_.const_index_sel; } @@ -606,6 +608,8 @@ struct alignas(uint32_t) VertexFetchInstruction { uint32_t dest() const { return data_.dst_reg; } uint32_t dest_swizzle() const { return data_.dst_swiz; } bool is_dest_relative() const { return data_.dst_reg_am; } + // The source is applicable only to vfetch_full (the address from vfetch_full + // is reused in vfetch_mini). uint32_t src() const { return data_.src_reg; } uint32_t src_swizzle() const { return data_.src_swiz; } bool is_src_relative() const { return data_.src_reg_am; } @@ -644,18 +648,21 @@ struct alignas(uint32_t) VertexFetchInstruction { xenos::SignedRepeatingFractionMode signed_rf_mode() const { return data_.signed_rf_mode_all; } + // If true, the floating-point index is rounded to the nearest integer (likely + // as floor(index + 0.5) because rounding to the nearest even makes no sense + // for addressing, both 1.5 and 2.5 would be 2). + // Otherwise, it's floored (rounded towards negative infinity). + // Applicable only to vfetch_full (the address from vfetch_full is reused in + // vfetch_mini). + // http://web.archive.org/web/20090914055358/http://msdn.microsoft.com/en-us/library/bb313960.aspx bool is_index_rounded() const { return data_.is_index_rounded == 1; } // Dword stride, [0, 255]. + // Applicable only to vfetch_full (the address from vfetch_full is reused in + // vfetch_mini). uint32_t stride() const { return data_.stride; } // Dword offset, [-4194304, 4194303]. int32_t offset() const { return data_.offset; } - void AssignFromFull(const VertexFetchInstruction& full) { - data_.stride = full.data_.stride; - data_.const_index = full.data_.const_index; - data_.const_index_sel = full.data_.const_index_sel; - } - private: struct Data { struct { From 14b69fdb00e479b61839fd542a45ad837ca87088 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 9 Jan 2022 16:26:05 +0300 Subject: [PATCH 29/88] [GPU] vfetch_full fetching nothing still must calculate the address --- .../gpu/d3d12/d3d12_command_processor.cc | 73 +++++++++---------- src/xenia/gpu/dxbc_shader_translator_fetch.cc | 11 ++- src/xenia/gpu/shader.h | 8 ++ src/xenia/gpu/shader_translator.cc | 20 +++-- 4 files changed, 69 insertions(+), 43 deletions(-) diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.cc b/src/xenia/gpu/d3d12/d3d12_command_processor.cc index 5d9be3660..6a3f78bc0 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.cc +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.cc @@ -2008,46 +2008,45 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, // Ensure vertex buffers are resident. // TODO(Triang3l): Cache residency for ranges in a way similar to how texture // validity is tracked. - uint64_t vertex_buffers_resident[2] = {}; - for (const Shader::VertexBinding& vertex_binding : - vertex_shader->vertex_bindings()) { - uint32_t vfetch_index = vertex_binding.fetch_constant; - if (vertex_buffers_resident[vfetch_index >> 6] & - (uint64_t(1) << (vfetch_index & 63))) { - continue; - } - const auto& vfetch_constant = regs.Get( - XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + vfetch_index * 2); - switch (vfetch_constant.type) { - case xenos::FetchConstantType::kVertex: - break; - case xenos::FetchConstantType::kInvalidVertex: - if (cvars::gpu_allow_invalid_fetch_constants) { + const Shader::ConstantRegisterMap& constant_map_vertex = + vertex_shader->constant_register_map(); + for (uint32_t i = 0; i < xe::countof(constant_map_vertex.vertex_fetch_bitmap); + ++i) { + uint32_t vfetch_bits_remaining = constant_map_vertex.vertex_fetch_bitmap[i]; + uint32_t j; + while (xe::bit_scan_forward(vfetch_bits_remaining, &j)) { + vfetch_bits_remaining &= ~(uint32_t(1) << j); + uint32_t vfetch_index = i * 32 + j; + const auto& vfetch_constant = regs.Get( + XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + vfetch_index * 2); + switch (vfetch_constant.type) { + case xenos::FetchConstantType::kVertex: break; - } - XELOGW( - "Vertex fetch constant {} ({:08X} {:08X}) has \"invalid\" type! " - "This " - "is incorrect behavior, but you can try bypassing this by " - "launching Xenia with --gpu_allow_invalid_fetch_constants=true.", - vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1); - return false; - default: - XELOGW( - "Vertex fetch constant {} ({:08X} {:08X}) is completely invalid!", - vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1); + case xenos::FetchConstantType::kInvalidVertex: + if (cvars::gpu_allow_invalid_fetch_constants) { + break; + } + XELOGW( + "Vertex fetch constant {} ({:08X} {:08X}) has \"invalid\" type! " + "This is incorrect behavior, but you can try bypassing this by " + "launching Xenia with --gpu_allow_invalid_fetch_constants=true.", + vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1); + return false; + default: + XELOGW( + "Vertex fetch constant {} ({:08X} {:08X}) is completely invalid!", + vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1); + return false; + } + if (!shared_memory_->RequestRange(vfetch_constant.address << 2, + vfetch_constant.size << 2)) { + XELOGE( + "Failed to request vertex buffer at 0x{:08X} (size {}) in the " + "shared memory", + vfetch_constant.address << 2, vfetch_constant.size << 2); return false; + } } - if (!shared_memory_->RequestRange(vfetch_constant.address << 2, - vfetch_constant.size << 2)) { - XELOGE( - "Failed to request vertex buffer at 0x{:08X} (size {}) in the shared " - "memory", - vfetch_constant.address << 2, vfetch_constant.size << 2); - return false; - } - vertex_buffers_resident[vfetch_index >> 6] |= uint64_t(1) - << (vfetch_index & 63); } // Gather memexport ranges and ensure the heaps for them are resident, and diff --git a/src/xenia/gpu/dxbc_shader_translator_fetch.cc b/src/xenia/gpu/dxbc_shader_translator_fetch.cc index 63480b76c..016841bc5 100644 --- a/src/xenia/gpu/dxbc_shader_translator_fetch.cc +++ b/src/xenia/gpu/dxbc_shader_translator_fetch.cc @@ -35,7 +35,9 @@ void DxbcShaderTranslator::ProcessVertexFetchInstruction( uint32_t used_result_components = instr.result.GetUsedResultComponents(); uint32_t needed_words = xenos::GetVertexFormatNeededWords( instr.attributes.data_format, used_result_components); - if (!needed_words) { + // If this is vfetch_full, the address may still be needed for vfetch_mini - + // don't exit before calculating the address. + if (!needed_words && instr.is_mini_fetch) { // Nothing to load - just constant 0/1 writes, or the swizzle includes only // components that don't exist in the format (writing zero instead of them). // Unpacking assumes at least some word is needed. @@ -107,6 +109,13 @@ void DxbcShaderTranslator::ProcessVertexFetchInstruction( } } + if (!needed_words) { + // The vfetch_full address has been loaded for the subsequent vfetch_mini, + // but there's no data to load. + StoreResult(instr.result, dxbc::Src::LF(0.0f)); + return; + } + dxbc::Dest address_temp_dest(dxbc::Dest::R(system_temp_result_, 0b1000)); dxbc::Src address_temp_src( dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW)); diff --git a/src/xenia/gpu/shader.h b/src/xenia/gpu/shader.h index 2ce81409a..9603134d4 100644 --- a/src/xenia/gpu/shader.h +++ b/src/xenia/gpu/shader.h @@ -435,6 +435,11 @@ struct ParsedVertexFetchInstruction { bool predicate_condition = false; // Describes how the instruction result is stored. + // Note that if the result doesn't have any components to write the fetched + // value to, the address calculation in vfetch_full must still be performed + // because such a vfetch_full may be used to setup addressing for vfetch_mini + // (wires in the color pass of 5454082B do vfetch_full to r2.000_, and then a + // true vfetch_mini). InstructionResult result; // Number of source operands. @@ -696,6 +701,9 @@ class Shader { // Bitmap of all bool constants read by the shader. // Each bit corresponds to a storage index [0-255]. uint32_t bool_bitmap[256 / 32]; + // Bitmap of all vertex fetch constants read by the shader. + // Each bit corresponds to a storage index [0-95]. + uint32_t vertex_fetch_bitmap[96 / 32]; // Total number of kConstantFloat registers read by the shader. uint32_t float_count; diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index 612edbfbf..9c1837779 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -282,13 +282,19 @@ void Shader::GatherVertexFetchInformation( GatherFetchResultInformation(fetch_instr.result); - // Don't bother setting up a binding for an instruction that fetches nothing. - if (!fetch_instr.result.GetUsedResultComponents()) { - return; + // Mini-fetches inherit the operands from full fetches. + if (!fetch_instr.is_mini_fetch) { + for (size_t i = 0; i < fetch_instr.operand_count; ++i) { + GatherOperandInformation(fetch_instr.operands[i]); + } } - for (size_t i = 0; i < fetch_instr.operand_count; ++i) { - GatherOperandInformation(fetch_instr.operands[i]); + // Don't bother setting up a binding for an instruction that fetches nothing. + // In case of vfetch_full, however, it may still be used to set up addressing + // for the subsequent vfetch_mini, so operand information must still be + // gathered. + if (!fetch_instr.result.GetUsedResultComponents()) { + return; } // Try to allocate an attribute on an existing binding. @@ -434,6 +440,10 @@ void Shader::GatherOperandInformation(const InstructionOperand& operand) { constant_register_map_.float_dynamic_addressing = true; } break; + case InstructionStorageSource::kVertexFetchConstant: + constant_register_map_.vertex_fetch_bitmap[operand.storage_index >> 5] |= + uint32_t(1) << (operand.storage_index & 31); + break; default: break; } From ce1a84375b69de52db2e7cfae141b3912424d395 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Sun, 9 Jan 2022 12:06:34 -0600 Subject: [PATCH 30/88] Remove FUNDING.yml. File has been moved to organization-wide repository. https://github.com/xenia-project/.github/FUNDING.yml --- .github/FUNDING.yml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 62bcd516c..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,2 +0,0 @@ -patreon: xenia_project -github: [gibbed, JoelLinn, Razzile] From 3ad0a7dab2269467cc4d958a2deb88da5eb1669b Mon Sep 17 00:00:00 2001 From: gibbed Date: Sun, 9 Jan 2022 11:14:40 -0600 Subject: [PATCH 31/88] [Kernel] Suffix export functions with _entry. --- src/xenia/kernel/util/shim_utils.h | 6 +- src/xenia/kernel/xam/xam_avatar.cc | 6 +- src/xenia/kernel/xam/xam_content.cc | 105 +++---- src/xenia/kernel/xam/xam_content_aggregate.cc | 12 +- src/xenia/kernel/xam/xam_content_device.cc | 24 +- src/xenia/kernel/xam/xam_enum.cc | 21 +- src/xenia/kernel/xam/xam_info.cc | 84 +++--- src/xenia/kernel/xam/xam_input.cc | 41 +-- src/xenia/kernel/xam/xam_locale.cc | 54 ++-- src/xenia/kernel/xam/xam_msg.cc | 34 +-- src/xenia/kernel/xam/xam_net.cc | 213 +++++++------- src/xenia/kernel/xam/xam_notify.cc | 18 +- src/xenia/kernel/xam/xam_nui.cc | 8 +- src/xenia/kernel/xam/xam_party.cc | 12 +- src/xenia/kernel/xam/xam_task.cc | 10 +- src/xenia/kernel/xam/xam_ui.cc | 37 ++- src/xenia/kernel/xam/xam_user.cc | 98 +++---- src/xenia/kernel/xam/xam_video.cc | 6 +- src/xenia/kernel/xam/xam_voice.cc | 14 +- src/xenia/kernel/xbdm/xbdm_misc.cc | 35 +-- src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc | 24 +- .../kernel/xboxkrnl/xboxkrnl_audio_xma.cc | 56 ++-- src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc | 148 +++++----- src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc | 12 +- src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc | 4 +- src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc | 4 +- src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc | 4 +- src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc | 101 +++---- src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc | 8 +- src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc | 86 +++--- src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc | 4 +- src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc | 25 +- src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc | 48 ++-- src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc | 98 +++---- src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc | 24 +- .../kernel/xboxkrnl/xboxkrnl_threading.cc | 263 ++++++++++-------- src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc | 10 +- src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc | 83 +++--- src/xenia/kernel/xboxkrnl/xboxkrnl_video.h | 4 +- src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc | 9 +- 40 files changed, 965 insertions(+), 888 deletions(-) diff --git a/src/xenia/kernel/util/shim_utils.h b/src/xenia/kernel/util/shim_utils.h index 964845be9..e9e287fbc 100644 --- a/src/xenia/kernel/util/shim_utils.h +++ b/src/xenia/kernel/util/shim_utils.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -33,7 +33,7 @@ using PPCContext = xe::cpu::ppc::PPCContext; #define SHIM_SET_MAPPING(library_name, export_name, shim_data) \ export_resolver->SetFunctionMapping( \ library_name, ordinals::export_name, \ - (xe::cpu::xe_kernel_export_shim_fn)export_name##_shim); + (xe::cpu::xe_kernel_export_shim_fn)export_name##_entry); #define SHIM_MEM_ADDR(a) \ ((a) ? ppc_context->kernel_state->memory()->TranslateVirtual(a) : nullptr) @@ -559,7 +559,7 @@ using xe::cpu::ExportTag; const auto EXPORT_##module_name##_##name = RegisterExport_##module_name( \ xe::kernel::shim::RegisterExport< \ xe::kernel::shim::KernelModuleId::module_name, ordinals::name>( \ - &name, #name, \ + &name##_entry, #name, \ tags | (static_cast( \ xe::cpu::ExportCategory::category) \ << xe::cpu::ExportTag::CategoryShift))); diff --git a/src/xenia/kernel/xam/xam_avatar.cc b/src/xenia/kernel/xam/xam_avatar.cc index a949e91b2..c105aaefe 100644 --- a/src/xenia/kernel/xam/xam_avatar.cc +++ b/src/xenia/kernel/xam/xam_avatar.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,7 +17,7 @@ namespace xe { namespace kernel { namespace xam { -dword_result_t XamAvatarInitialize( +dword_result_t XamAvatarInitialize_entry( dword_t unk1, // 1, 4, etc dword_t unk2, // 0 or 1 dword_t processor_number, // for thread creation? @@ -30,7 +30,7 @@ dword_result_t XamAvatarInitialize( } DECLARE_XAM_EXPORT1(XamAvatarInitialize, kAvatars, kStub); -void XamAvatarShutdown() { +void XamAvatarShutdown_entry() { // No-op. } DECLARE_XAM_EXPORT1(XamAvatarShutdown, kAvatars, kStub); diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index dea81d46b..5f4c12111 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -31,8 +31,8 @@ namespace xe { namespace kernel { namespace xam { -dword_result_t XamContentGetLicenseMask(lpdword_t mask_ptr, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentGetLicenseMask_entry(lpdword_t mask_ptr, + lpunknown_t overlapped_ptr) { // Each bit in the mask represents a granted license. Available licenses // seems to vary from game to game, but most appear to use bit 0 to indicate // if the game is purchased or not. @@ -48,9 +48,11 @@ dword_result_t XamContentGetLicenseMask(lpdword_t mask_ptr, } DECLARE_XAM_EXPORT2(XamContentGetLicenseMask, kContent, kStub, kHighFrequency); -dword_result_t XamContentResolve(dword_t user_index, lpvoid_t content_data_ptr, - lpunknown_t buffer_ptr, dword_t buffer_size, - dword_t unk1, dword_t unk2, dword_t unk3) { +dword_result_t XamContentResolve_entry(dword_t user_index, + lpvoid_t content_data_ptr, + lpunknown_t buffer_ptr, + dword_t buffer_size, dword_t unk1, + dword_t unk2, dword_t unk3) { auto content_data = content_data_ptr.as(); // Result of buffer_ptr is sent to RtlInitAnsiString. @@ -64,12 +66,10 @@ DECLARE_XAM_EXPORT1(XamContentResolve, kContent, kStub); // https://github.com/MrColdbird/gameservice/blob/master/ContentManager.cpp // https://github.com/LestaD/SourceEngine2007/blob/master/se2007/engine/xboxsystem.cpp#L499 -dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id, - dword_t content_type, - dword_t content_flags, - dword_t items_per_enumerate, - lpdword_t buffer_size_ptr, - lpdword_t handle_out) { +dword_result_t XamContentCreateEnumerator_entry( + dword_t user_index, dword_t device_id, dword_t content_type, + dword_t content_flags, dword_t items_per_enumerate, + lpdword_t buffer_size_ptr, lpdword_t handle_out) { assert_not_null(handle_out); auto device_info = device_id == 0 ? nullptr : GetDummyDeviceInfo(device_id); @@ -226,12 +226,10 @@ dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name, } } -dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name, - lpvoid_t content_data_ptr, dword_t flags, - lpdword_t disposition_ptr, - lpdword_t license_mask_ptr, - dword_t cache_size, qword_t content_size, - lpvoid_t overlapped_ptr) { +dword_result_t XamContentCreateEx_entry( + dword_t user_index, lpstring_t root_name, lpvoid_t content_data_ptr, + dword_t flags, lpdword_t disposition_ptr, lpdword_t license_mask_ptr, + dword_t cache_size, qword_t content_size, lpvoid_t overlapped_ptr) { return xeXamContentCreate(user_index, root_name, content_data_ptr, sizeof(XCONTENT_DATA), flags, disposition_ptr, license_mask_ptr, cache_size, content_size, @@ -239,18 +237,18 @@ dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name, } DECLARE_XAM_EXPORT1(XamContentCreateEx, kContent, kImplemented); -dword_result_t XamContentCreate(dword_t user_index, lpstring_t root_name, - lpvoid_t content_data_ptr, dword_t flags, - lpdword_t disposition_ptr, - lpdword_t license_mask_ptr, - lpvoid_t overlapped_ptr) { +dword_result_t XamContentCreate_entry(dword_t user_index, lpstring_t root_name, + lpvoid_t content_data_ptr, dword_t flags, + lpdword_t disposition_ptr, + lpdword_t license_mask_ptr, + lpvoid_t overlapped_ptr) { return xeXamContentCreate(user_index, root_name, content_data_ptr, sizeof(XCONTENT_DATA), flags, disposition_ptr, license_mask_ptr, 0, 0, overlapped_ptr); } DECLARE_XAM_EXPORT1(XamContentCreate, kContent, kImplemented); -dword_result_t XamContentCreateInternal( +dword_result_t XamContentCreateInternal_entry( lpstring_t root_name, lpvoid_t content_data_ptr, dword_t flags, lpdword_t disposition_ptr, lpdword_t license_mask_ptr, dword_t cache_size, qword_t content_size, lpvoid_t overlapped_ptr) { @@ -261,18 +259,19 @@ dword_result_t XamContentCreateInternal( } DECLARE_XAM_EXPORT1(XamContentCreateInternal, kContent, kImplemented); -dword_result_t XamContentOpenFile(dword_t user_index, lpstring_t root_name, - lpstring_t path, dword_t flags, - lpdword_t disposition_ptr, - lpdword_t license_mask_ptr, - lpvoid_t overlapped_ptr) { +dword_result_t XamContentOpenFile_entry(dword_t user_index, + lpstring_t root_name, lpstring_t path, + dword_t flags, + lpdword_t disposition_ptr, + lpdword_t license_mask_ptr, + lpvoid_t overlapped_ptr) { // TODO(gibbed): arguments assumed based on XamContentCreate. return X_ERROR_FILE_NOT_FOUND; } DECLARE_XAM_EXPORT1(XamContentOpenFile, kContent, kStub); -dword_result_t XamContentFlush(lpstring_t root_name, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentFlush_entry(lpstring_t root_name, + lpunknown_t overlapped_ptr) { X_RESULT result = X_ERROR_SUCCESS; if (overlapped_ptr) { kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result); @@ -283,8 +282,8 @@ dword_result_t XamContentFlush(lpstring_t root_name, } DECLARE_XAM_EXPORT1(XamContentFlush, kContent, kStub); -dword_result_t XamContentClose(lpstring_t root_name, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentClose_entry(lpstring_t root_name, + lpunknown_t overlapped_ptr) { // Closes a previously opened root from XamContentCreate*. auto result = kernel_state()->content_manager()->CloseContent(root_name.value()); @@ -298,11 +297,11 @@ dword_result_t XamContentClose(lpstring_t root_name, } DECLARE_XAM_EXPORT1(XamContentClose, kContent, kImplemented); -dword_result_t XamContentGetCreator(dword_t user_index, - lpvoid_t content_data_ptr, - lpdword_t is_creator_ptr, - lpqword_t creator_xuid_ptr, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentGetCreator_entry(dword_t user_index, + lpvoid_t content_data_ptr, + lpdword_t is_creator_ptr, + lpqword_t creator_xuid_ptr, + lpunknown_t overlapped_ptr) { auto result = X_ERROR_SUCCESS; XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); @@ -336,11 +335,11 @@ dword_result_t XamContentGetCreator(dword_t user_index, } DECLARE_XAM_EXPORT1(XamContentGetCreator, kContent, kImplemented); -dword_result_t XamContentGetThumbnail(dword_t user_index, - lpvoid_t content_data_ptr, - lpvoid_t buffer_ptr, - lpdword_t buffer_size_ptr, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentGetThumbnail_entry(dword_t user_index, + lpvoid_t content_data_ptr, + lpvoid_t buffer_ptr, + lpdword_t buffer_size_ptr, + lpunknown_t overlapped_ptr) { assert_not_null(buffer_size_ptr); uint32_t buffer_size = *buffer_size_ptr; XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); @@ -375,10 +374,11 @@ dword_result_t XamContentGetThumbnail(dword_t user_index, } DECLARE_XAM_EXPORT1(XamContentGetThumbnail, kContent, kImplemented); -dword_result_t XamContentSetThumbnail(dword_t user_index, - lpvoid_t content_data_ptr, - lpvoid_t buffer_ptr, dword_t buffer_size, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentSetThumbnail_entry(dword_t user_index, + lpvoid_t content_data_ptr, + lpvoid_t buffer_ptr, + dword_t buffer_size, + lpunknown_t overlapped_ptr) { XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); // Buffer is PNG data. @@ -396,8 +396,9 @@ dword_result_t XamContentSetThumbnail(dword_t user_index, } DECLARE_XAM_EXPORT1(XamContentSetThumbnail, kContent, kImplemented); -dword_result_t XamContentDelete(dword_t user_index, lpvoid_t content_data_ptr, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentDelete_entry(dword_t user_index, + lpvoid_t content_data_ptr, + lpunknown_t overlapped_ptr) { XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); auto result = kernel_state()->content_manager()->DeleteContent(content_data); @@ -411,11 +412,11 @@ dword_result_t XamContentDelete(dword_t user_index, lpvoid_t content_data_ptr, } DECLARE_XAM_EXPORT1(XamContentDelete, kContent, kImplemented); -dword_result_t XamContentDeleteInternal(lpvoid_t content_data_ptr, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentDeleteInternal_entry(lpvoid_t content_data_ptr, + lpunknown_t overlapped_ptr) { // INFO: Analysis of xam.xex shows that "internal" functions are wrappers with // 0xFE as user_index - return XamContentDelete(0xFE, content_data_ptr, overlapped_ptr); + return XamContentDelete_entry(0xFE, content_data_ptr, overlapped_ptr); } DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented); diff --git a/src/xenia/kernel/xam/xam_content_aggregate.cc b/src/xenia/kernel/xam/xam_content_aggregate.cc index 765619002..970d80652 100644 --- a/src/xenia/kernel/xam/xam_content_aggregate.cc +++ b/src/xenia/kernel/xam/xam_content_aggregate.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -76,11 +76,11 @@ void AddODDContentTest(object_ref> e, } } -dword_result_t XamContentAggregateCreateEnumerator(qword_t xuid, - dword_t device_id, - dword_t content_type, - unknown_t unk3, - lpdword_t handle_out) { +dword_result_t XamContentAggregateCreateEnumerator_entry(qword_t xuid, + dword_t device_id, + dword_t content_type, + unknown_t unk3, + lpdword_t handle_out) { assert_not_null(handle_out); auto device_info = device_id == 0 ? nullptr : GetDummyDeviceInfo(device_id); diff --git a/src/xenia/kernel/xam/xam_content_device.cc b/src/xenia/kernel/xam/xam_content_device.cc index e4312de95..04bf93326 100644 --- a/src/xenia/kernel/xam/xam_content_device.cc +++ b/src/xenia/kernel/xam/xam_content_device.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -59,9 +59,9 @@ const DummyDeviceInfo* GetDummyDeviceInfo(uint32_t device_id) { return it == end ? nullptr : *it; } -dword_result_t XamContentGetDeviceName(dword_t device_id, - lpu16string_t name_buffer, - dword_t name_capacity) { +dword_result_t XamContentGetDeviceName_entry(dword_t device_id, + lpu16string_t name_buffer, + dword_t name_capacity) { auto device_info = GetDummyDeviceInfo(device_id); if (device_info == nullptr) { return X_ERROR_DEVICE_NOT_CONNECTED; @@ -75,8 +75,8 @@ dword_result_t XamContentGetDeviceName(dword_t device_id, } DECLARE_XAM_EXPORT1(XamContentGetDeviceName, kContent, kImplemented); -dword_result_t XamContentGetDeviceState(dword_t device_id, - lpunknown_t overlapped_ptr) { +dword_result_t XamContentGetDeviceState_entry(dword_t device_id, + lpunknown_t overlapped_ptr) { auto device_info = GetDummyDeviceInfo(device_id); if (device_info == nullptr) { if (overlapped_ptr) { @@ -110,7 +110,7 @@ typedef struct { } X_CONTENT_DEVICE_DATA; static_assert_size(X_CONTENT_DEVICE_DATA, 0x50); -dword_result_t XamContentGetDeviceData( +dword_result_t XamContentGetDeviceData_entry( dword_t device_id, pointer_t device_data) { auto device_info = GetDummyDeviceInfo(device_id); if (device_info == nullptr) { @@ -128,11 +128,11 @@ dword_result_t XamContentGetDeviceData( } DECLARE_XAM_EXPORT1(XamContentGetDeviceData, kContent, kImplemented); -dword_result_t XamContentCreateDeviceEnumerator(dword_t content_type, - dword_t content_flags, - dword_t max_count, - lpdword_t buffer_size_ptr, - lpdword_t handle_out) { +dword_result_t XamContentCreateDeviceEnumerator_entry(dword_t content_type, + dword_t content_flags, + dword_t max_count, + lpdword_t buffer_size_ptr, + lpdword_t handle_out) { assert_not_null(handle_out); if (buffer_size_ptr) { diff --git a/src/xenia/kernel/xam/xam_enum.cc b/src/xenia/kernel/xam/xam_enum.cc index 396d08dd7..eacec3630 100644 --- a/src/xenia/kernel/xam/xam_enum.cc +++ b/src/xenia/kernel/xam/xam_enum.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -69,9 +69,10 @@ uint32_t xeXamEnumerate(uint32_t handle, uint32_t flags, lpvoid_t buffer_ptr, } } -dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer, - dword_t buffer_length, lpdword_t items_returned, - pointer_t overlapped) { +dword_result_t XamEnumerate_entry(dword_t handle, dword_t flags, + lpvoid_t buffer, dword_t buffer_length, + lpdword_t items_returned, + pointer_t overlapped) { uint32_t dummy; auto result = xeXamEnumerate(handle, flags, buffer, buffer_length, !overlapped ? &dummy : nullptr, overlapped); @@ -82,16 +83,16 @@ dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer, } DECLARE_XAM_EXPORT1(XamEnumerate, kNone, kImplemented); -dword_result_t XamCreateEnumeratorHandle(unknown_t unk1, unknown_t unk2, - unknown_t unk3, unknown_t unk4, - unknown_t unk5, unknown_t unk6, - unknown_t unk7, unknown_t unk8) { +dword_result_t XamCreateEnumeratorHandle_entry(unknown_t unk1, unknown_t unk2, + unknown_t unk3, unknown_t unk4, + unknown_t unk5, unknown_t unk6, + unknown_t unk7, unknown_t unk8) { return X_ERROR_INVALID_PARAMETER; } DECLARE_XAM_EXPORT1(XamCreateEnumeratorHandle, kNone, kStub); -dword_result_t XamGetPrivateEnumStructureFromHandle(dword_t handle, - lpdword_t out_object_ptr) { +dword_result_t XamGetPrivateEnumStructureFromHandle_entry( + dword_t handle, lpdword_t out_object_ptr) { auto e = kernel_state()->object_table()->LookupObject(handle); if (!e) { return X_STATUS_INVALID_HANDLE; diff --git a/src/xenia/kernel/xam/xam_info.cc b/src/xenia/kernel/xam/xam_info.cc index f697ff658..76d749841 100644 --- a/src/xenia/kernel/xam/xam_info.cc +++ b/src/xenia/kernel/xam/xam_info.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -28,7 +28,7 @@ namespace xe { namespace kernel { namespace xam { -dword_result_t XamFeatureEnabled(dword_t unk) { return 0; } +dword_result_t XamFeatureEnabled_entry(dword_t unk) { return 0; } DECLARE_XAM_EXPORT1(XamFeatureEnabled, kNone, kStub); // Empty stub schema binary. @@ -39,7 +39,7 @@ uint8_t schema_bin[] = { 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, }; -dword_result_t XamGetOnlineSchema() { +dword_result_t XamGetOnlineSchema_entry() { static uint32_t schema_guest = 0; if (!schema_guest) { @@ -70,8 +70,8 @@ static SYSTEMTIME xeGetLocalSystemTime(uint64_t filetime) { } #endif -void XamFormatDateString(dword_t unk, qword_t filetime, lpvoid_t output_buffer, - dword_t output_count) { +void XamFormatDateString_entry(dword_t unk, qword_t filetime, + lpvoid_t output_buffer, dword_t output_count) { std::memset(output_buffer, 0, output_count * sizeof(char16_t)); // TODO: implement this for other platforms @@ -87,8 +87,8 @@ void XamFormatDateString(dword_t unk, qword_t filetime, lpvoid_t output_buffer, } DECLARE_XAM_EXPORT1(XamFormatDateString, kNone, kImplemented); -void XamFormatTimeString(dword_t unk, qword_t filetime, lpvoid_t output_buffer, - dword_t output_count) { +void XamFormatTimeString_entry(dword_t unk, qword_t filetime, + lpvoid_t output_buffer, dword_t output_count) { std::memset(output_buffer, 0, output_count * sizeof(char16_t)); // TODO: implement this for other platforms @@ -124,18 +124,19 @@ dword_result_t keXamBuildResourceLocator(uint64_t module, return 0; } -dword_result_t XamBuildResourceLocator(qword_t module, lpu16string_t container, - lpu16string_t resource, - lpvoid_t buffer_ptr, - dword_t buffer_count) { +dword_result_t XamBuildResourceLocator_entry(qword_t module, + lpu16string_t container, + lpu16string_t resource, + lpvoid_t buffer_ptr, + dword_t buffer_count) { return keXamBuildResourceLocator(module, container.value(), resource.value(), buffer_ptr, buffer_count); } DECLARE_XAM_EXPORT1(XamBuildResourceLocator, kNone, kImplemented); -dword_result_t XamBuildGamercardResourceLocator(lpu16string_t filename, - lpvoid_t buffer_ptr, - dword_t buffer_count) { +dword_result_t XamBuildGamercardResourceLocator_entry(lpu16string_t filename, + lpvoid_t buffer_ptr, + dword_t buffer_count) { // On an actual xbox these funcs would return a locator to xam.xex resources, // but for Xenia we can return a locator to the resources as local files. (big // thanks to MS for letting XamBuildResourceLocator return local file @@ -149,32 +150,32 @@ dword_result_t XamBuildGamercardResourceLocator(lpu16string_t filename, } DECLARE_XAM_EXPORT1(XamBuildGamercardResourceLocator, kNone, kImplemented); -dword_result_t XamBuildSharedSystemResourceLocator(lpu16string_t filename, - lpvoid_t buffer_ptr, - dword_t buffer_count) { +dword_result_t XamBuildSharedSystemResourceLocator_entry(lpu16string_t filename, + lpvoid_t buffer_ptr, + dword_t buffer_count) { // see notes inside XamBuildGamercardResourceLocator above return keXamBuildResourceLocator(0, u"shrdres", filename.value(), buffer_ptr, buffer_count); } DECLARE_XAM_EXPORT1(XamBuildSharedSystemResourceLocator, kNone, kImplemented); -dword_result_t XamBuildLegacySystemResourceLocator(lpu16string_t filename, - lpvoid_t buffer_ptr, - dword_t buffer_count) { - return XamBuildSharedSystemResourceLocator(filename, buffer_ptr, - buffer_count); +dword_result_t XamBuildLegacySystemResourceLocator_entry(lpu16string_t filename, + lpvoid_t buffer_ptr, + dword_t buffer_count) { + return XamBuildSharedSystemResourceLocator_entry(filename, buffer_ptr, + buffer_count); } DECLARE_XAM_EXPORT1(XamBuildLegacySystemResourceLocator, kNone, kImplemented); -dword_result_t XamBuildXamResourceLocator(lpu16string_t filename, - lpvoid_t buffer_ptr, - dword_t buffer_count) { +dword_result_t XamBuildXamResourceLocator_entry(lpu16string_t filename, + lpvoid_t buffer_ptr, + dword_t buffer_count) { return keXamBuildResourceLocator(0, u"xam", filename.value(), buffer_ptr, buffer_count); } DECLARE_XAM_EXPORT1(XamBuildXamResourceLocator, kNone, kImplemented); -dword_result_t XamGetSystemVersion() { +dword_result_t XamGetSystemVersion_entry() { // eh, just picking one. If we go too low we may break new games, but // this value seems to be used for conditionally loading symbols and if // we pretend to be old we have less to worry with implementing. @@ -184,12 +185,12 @@ dword_result_t XamGetSystemVersion() { } DECLARE_XAM_EXPORT1(XamGetSystemVersion, kNone, kStub); -void XCustomRegisterDynamicActions() { +void XCustomRegisterDynamicActions_entry() { // ??? } DECLARE_XAM_EXPORT1(XCustomRegisterDynamicActions, kNone, kStub); -dword_result_t XGetAVPack() { +dword_result_t XGetAVPack_entry() { // DWORD // Not sure what the values are for this, but 6 is VGA. // Other likely values are 3/4/8 for HDMI or something. @@ -201,10 +202,10 @@ DECLARE_XAM_EXPORT1(XGetAVPack, kNone, kStub); uint32_t xeXGetGameRegion() { return 0xFFFFu; } -dword_result_t XGetGameRegion() { return xeXGetGameRegion(); } +dword_result_t XGetGameRegion_entry() { return xeXGetGameRegion(); } DECLARE_XAM_EXPORT1(XGetGameRegion, kNone, kStub); -dword_result_t XGetLanguage() { +dword_result_t XGetLanguage_entry() { auto desired_language = XLanguage::kEnglish; // Switch the language based on game region. @@ -221,7 +222,7 @@ dword_result_t XGetLanguage() { } DECLARE_XAM_EXPORT1(XGetLanguage, kNone, kImplemented); -dword_result_t XamGetExecutionId(lpdword_t info_ptr) { +dword_result_t XamGetExecutionId_entry(lpdword_t info_ptr) { auto module = kernel_state()->GetExecutableModule(); assert_not_null(module); @@ -238,7 +239,7 @@ dword_result_t XamGetExecutionId(lpdword_t info_ptr) { } DECLARE_XAM_EXPORT1(XamGetExecutionId, kNone, kImplemented); -dword_result_t XamLoaderSetLaunchData(lpvoid_t data, dword_t size) { +dword_result_t XamLoaderSetLaunchData_entry(lpvoid_t data, dword_t size) { auto xam = kernel_state()->GetKernelModule("xam.xex"); auto& loader_data = xam->loader_data(); loader_data.launch_data_present = size ? true : false; @@ -248,7 +249,7 @@ dword_result_t XamLoaderSetLaunchData(lpvoid_t data, dword_t size) { } DECLARE_XAM_EXPORT1(XamLoaderSetLaunchData, kNone, kSketchy); -dword_result_t XamLoaderGetLaunchDataSize(lpdword_t size_ptr) { +dword_result_t XamLoaderGetLaunchDataSize_entry(lpdword_t size_ptr) { if (!size_ptr) { return X_ERROR_INVALID_PARAMETER; } @@ -265,8 +266,8 @@ dword_result_t XamLoaderGetLaunchDataSize(lpdword_t size_ptr) { } DECLARE_XAM_EXPORT1(XamLoaderGetLaunchDataSize, kNone, kSketchy); -dword_result_t XamLoaderGetLaunchData(lpvoid_t buffer_ptr, - dword_t buffer_size) { +dword_result_t XamLoaderGetLaunchData_entry(lpvoid_t buffer_ptr, + dword_t buffer_size) { auto xam = kernel_state()->GetKernelModule("xam.xex"); auto& loader_data = xam->loader_data(); if (!loader_data.launch_data_present) { @@ -280,7 +281,7 @@ dword_result_t XamLoaderGetLaunchData(lpvoid_t buffer_ptr, } DECLARE_XAM_EXPORT1(XamLoaderGetLaunchData, kNone, kSketchy); -void XamLoaderLaunchTitle(lpstring_t raw_name_ptr, dword_t flags) { +void XamLoaderLaunchTitle_entry(lpstring_t raw_name_ptr, dword_t flags) { auto xam = kernel_state()->GetKernelModule("xam.xex"); auto& loader_data = xam->loader_data(); @@ -309,13 +310,13 @@ void XamLoaderLaunchTitle(lpstring_t raw_name_ptr, dword_t flags) { } DECLARE_XAM_EXPORT1(XamLoaderLaunchTitle, kNone, kSketchy); -void XamLoaderTerminateTitle() { +void XamLoaderTerminateTitle_entry() { // This function does not return. kernel_state()->TerminateTitle(); } DECLARE_XAM_EXPORT1(XamLoaderTerminateTitle, kNone, kSketchy); -dword_result_t XamAlloc(dword_t unk, dword_t size, lpdword_t out_ptr) { +dword_result_t XamAlloc_entry(dword_t unk, dword_t size, lpdword_t out_ptr) { assert_true(unk == 0); // Allocate from the heap. Not sure why XAM does this specially, perhaps @@ -327,15 +328,16 @@ dword_result_t XamAlloc(dword_t unk, dword_t size, lpdword_t out_ptr) { } DECLARE_XAM_EXPORT1(XamAlloc, kMemory, kImplemented); -dword_result_t XamFree(lpdword_t ptr) { +dword_result_t XamFree_entry(lpdword_t ptr) { kernel_state()->memory()->SystemHeapFree(ptr.guest_address()); return X_ERROR_SUCCESS; } DECLARE_XAM_EXPORT1(XamFree, kMemory, kImplemented); -dword_result_t XamQueryLiveHiveW(lpu16string_t name, lpvoid_t out_buf, - dword_t out_size, dword_t type /* guess */) { +dword_result_t XamQueryLiveHiveW_entry(lpu16string_t name, lpvoid_t out_buf, + dword_t out_size, + dword_t type /* guess */) { return X_STATUS_INVALID_PARAMETER_1; } DECLARE_XAM_EXPORT1(XamQueryLiveHiveW, kNone, kStub); diff --git a/src/xenia/kernel/xam/xam_input.cc b/src/xenia/kernel/xam/xam_input.cc index 7aead257d..413b797a0 100644 --- a/src/xenia/kernel/xam/xam_input.cc +++ b/src/xenia/kernel/xam/xam_input.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -28,19 +28,20 @@ using xe::hid::X_INPUT_VIBRATION; constexpr uint32_t XINPUT_FLAG_GAMEPAD = 0x01; constexpr uint32_t XINPUT_FLAG_ANY_USER = 1 << 30; -void XamResetInactivity() { +void XamResetInactivity_entry() { // Do we need to do anything? } DECLARE_XAM_EXPORT1(XamResetInactivity, kInput, kStub); -dword_result_t XamEnableInactivityProcessing(dword_t unk, dword_t enable) { +dword_result_t XamEnableInactivityProcessing_entry(dword_t unk, + dword_t enable) { return X_ERROR_SUCCESS; } DECLARE_XAM_EXPORT1(XamEnableInactivityProcessing, kInput, kStub); // https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetcapabilities(v=vs.85).aspx -dword_result_t XamInputGetCapabilities(dword_t user_index, dword_t flags, - pointer_t caps) { +dword_result_t XamInputGetCapabilities_entry( + dword_t user_index, dword_t flags, pointer_t caps) { if (!caps) { return X_ERROR_BAD_ARGUMENTS; } @@ -61,9 +62,9 @@ dword_result_t XamInputGetCapabilities(dword_t user_index, dword_t flags, } DECLARE_XAM_EXPORT1(XamInputGetCapabilities, kInput, kSketchy); -dword_result_t XamInputGetCapabilitiesEx(dword_t unk, dword_t user_index, - dword_t flags, - pointer_t caps) { +dword_result_t XamInputGetCapabilitiesEx_entry( + dword_t unk, dword_t user_index, dword_t flags, + pointer_t caps) { if (!caps) { return X_ERROR_BAD_ARGUMENTS; } @@ -85,8 +86,8 @@ dword_result_t XamInputGetCapabilitiesEx(dword_t unk, dword_t user_index, DECLARE_XAM_EXPORT1(XamInputGetCapabilitiesEx, kInput, kSketchy); // https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetstate(v=vs.85).aspx -dword_result_t XamInputGetState(dword_t user_index, dword_t flags, - pointer_t input_state) { +dword_result_t XamInputGetState_entry(dword_t user_index, dword_t flags, + pointer_t input_state) { // Games call this with a NULL state ptr, probably as a query. if ((flags & 0xFF) && (flags & XINPUT_FLAG_GAMEPAD) == 0) { @@ -103,11 +104,11 @@ dword_result_t XamInputGetState(dword_t user_index, dword_t flags, auto input_system = kernel_state()->emulator()->input_system(); return input_system->GetState(user_index, input_state); } -DECLARE_XAM_EXPORT1(XamInputGetState, kInput, kImplemented); +DECLARE_XAM_EXPORT2(XamInputGetState, kInput, kImplemented, kHighFrequency); // https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputsetstate(v=vs.85).aspx -dword_result_t XamInputSetState(dword_t user_index, dword_t unk, - pointer_t vibration) { +dword_result_t XamInputSetState_entry(dword_t user_index, dword_t unk, + pointer_t vibration) { if (!vibration) { return X_ERROR_BAD_ARGUMENTS; } @@ -124,8 +125,8 @@ dword_result_t XamInputSetState(dword_t user_index, dword_t unk, DECLARE_XAM_EXPORT1(XamInputSetState, kInput, kImplemented); // https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinputgetkeystroke(v=vs.85).aspx -dword_result_t XamInputGetKeystroke(dword_t user_index, dword_t flags, - pointer_t keystroke) { +dword_result_t XamInputGetKeystroke_entry( + dword_t user_index, dword_t flags, pointer_t keystroke) { // https://github.com/CodeAsm/ffplay360/blob/master/Common/AtgXime.cpp // user index = index or XUSER_INDEX_ANY // flags = XINPUT_FLAG_GAMEPAD (| _ANYUSER | _ANYDEVICE) @@ -151,8 +152,9 @@ dword_result_t XamInputGetKeystroke(dword_t user_index, dword_t flags, DECLARE_XAM_EXPORT1(XamInputGetKeystroke, kInput, kImplemented); // Same as non-ex, just takes a pointer to user index. -dword_result_t XamInputGetKeystrokeEx(lpdword_t user_index_ptr, dword_t flags, - pointer_t keystroke) { +dword_result_t XamInputGetKeystrokeEx_entry( + lpdword_t user_index_ptr, dword_t flags, + pointer_t keystroke) { if (!keystroke) { return X_ERROR_BAD_ARGUMENTS; } @@ -177,8 +179,9 @@ dword_result_t XamInputGetKeystrokeEx(lpdword_t user_index_ptr, dword_t flags, } DECLARE_XAM_EXPORT1(XamInputGetKeystrokeEx, kInput, kImplemented); -X_HRESULT_result_t XamUserGetDeviceContext(dword_t user_index, dword_t unk, - lpdword_t out_ptr) { +X_HRESULT_result_t XamUserGetDeviceContext_entry(dword_t user_index, + dword_t unk, + lpdword_t out_ptr) { // Games check the result - usually with some masking. // If this function fails they assume zero, so let's fail AND // set zero just to be safe. diff --git a/src/xenia/kernel/xam/xam_locale.cc b/src/xenia/kernel/xam/xam_locale.cc index 880e5c003..573b41086 100644 --- a/src/xenia/kernel/xam/xam_locale.cc +++ b/src/xenia/kernel/xam/xam_locale.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -226,16 +226,19 @@ uint8_t xeXamGetLocale() { return xeXamGetLocaleEx(111, 43); } // Exports. -dword_result_t XamGetLocale() { return xeXamGetLocale(); } +dword_result_t XamGetLocale_entry() { + return static_cast(xeXamGetLocale()); +} DECLARE_XAM_EXPORT1(XamGetLocale, kLocale, kImplemented); -dword_result_t XamGetOnlineCountryFromLocale(dword_t id) { +dword_result_t XamGetOnlineCountryFromLocale_entry(dword_t id) { return xeXamGetOnlineCountryFromLocale(id); } DECLARE_XAM_EXPORT1(XamGetOnlineCountryFromLocale, kLocale, kImplemented); -dword_result_t XamGetOnlineCountryString(dword_t id, dword_t buffer_length, - lpu16string_t buffer) { +dword_result_t XamGetOnlineCountryString_entry(dword_t id, + dword_t buffer_length, + lpu16string_t buffer) { if (buffer_length >= 0x80000000u) { return X_E_INVALIDARG; } @@ -256,8 +259,8 @@ dword_result_t XamGetOnlineCountryString(dword_t id, dword_t buffer_length, } DECLARE_XAM_EXPORT1(XamGetOnlineCountryString, kLocale, kImplemented); -dword_result_t XamGetCountryString(dword_t id, dword_t buffer_length, - lpu16string_t buffer) { +dword_result_t XamGetCountryString_entry(dword_t id, dword_t buffer_length, + lpu16string_t buffer) { if (buffer_length >= 0x80000000u) { return X_E_INVALIDARG; } @@ -278,8 +281,8 @@ dword_result_t XamGetCountryString(dword_t id, dword_t buffer_length, } DECLARE_XAM_EXPORT1(XamGetCountryString, kLocale, kImplemented); -dword_result_t XamGetLanguageString(dword_t id, dword_t buffer_length, - lpu16string_t buffer) { +dword_result_t XamGetLanguageString_entry(dword_t id, dword_t buffer_length, + lpu16string_t buffer) { if (buffer_length >= 0x80000000u) { return X_E_INVALIDARG; } @@ -300,10 +303,10 @@ dword_result_t XamGetLanguageString(dword_t id, dword_t buffer_length, } DECLARE_XAM_EXPORT1(XamGetLanguageString, kLocale, kImplemented); -dword_result_t XamGetLanguageLocaleString(dword_t language_id, - dword_t locale_id, - dword_t buffer_length, - lpu16string_t buffer) { +dword_result_t XamGetLanguageLocaleString_entry(dword_t language_id, + dword_t locale_id, + dword_t buffer_length, + lpu16string_t buffer) { if (buffer_length >= 0x80000000u) { return X_E_INVALIDARG; } @@ -330,10 +333,9 @@ dword_result_t XamGetLanguageLocaleString(dword_t language_id, } DECLARE_XAM_EXPORT1(XamGetLanguageLocaleString, kLocale, kImplemented); -dword_result_t XamGetOnlineLanguageAndCountryString(dword_t language_id, - dword_t country_id, - dword_t buffer_length, - lpu16string_t buffer) { +dword_result_t XamGetOnlineLanguageAndCountryString_entry( + dword_t language_id, dword_t country_id, dword_t buffer_length, + lpu16string_t buffer) { if (buffer_length >= 0x80000000u) { return X_E_INVALIDARG; } @@ -363,8 +365,8 @@ dword_result_t XamGetOnlineLanguageAndCountryString(dword_t language_id, DECLARE_XAM_EXPORT1(XamGetOnlineLanguageAndCountryString, kLocale, kImplemented); -dword_result_t XamGetLocaleString(dword_t id, dword_t buffer_length, - lpu16string_t buffer) { +dword_result_t XamGetLocaleString_entry(dword_t id, dword_t buffer_length, + lpu16string_t buffer) { if (buffer_length >= 0x80000000u) { return X_E_INVALIDARG; } @@ -385,18 +387,19 @@ dword_result_t XamGetLocaleString(dword_t id, dword_t buffer_length, } DECLARE_XAM_EXPORT1(XamGetLocaleString, kLocale, kImplemented); -dword_result_t XamGetLocaleFromOnlineCountry(dword_t id) { +dword_result_t XamGetLocaleFromOnlineCountry_entry(dword_t id) { return xeXamGetLocaleFromOnlineCountry(static_cast(id)); } DECLARE_XAM_EXPORT1(XamGetLocaleFromOnlineCountry, kLocale, kImplemented); -dword_result_t XamGetLanguageFromOnlineLanguage(dword_t id) { +dword_result_t XamGetLanguageFromOnlineLanguage_entry(dword_t id) { return xeXamGetLanguageFromOnlineLanguage(static_cast(id)); } DECLARE_XAM_EXPORT1(XamGetLanguageFromOnlineLanguage, kLocale, kImplemented); -dword_result_t XamGetOnlineLanguageString(dword_t id, dword_t buffer_length, - lpu16string_t buffer) { +dword_result_t XamGetOnlineLanguageString_entry(dword_t id, + dword_t buffer_length, + lpu16string_t buffer) { if (buffer_length >= 0x80000000u) { return X_E_INVALIDARG; } @@ -417,12 +420,13 @@ dword_result_t XamGetOnlineLanguageString(dword_t id, dword_t buffer_length, } DECLARE_XAM_EXPORT1(XamGetOnlineLanguageString, kLocale, kImplemented); -dword_result_t XamGetCountryFromOnlineCountry(dword_t id) { +dword_result_t XamGetCountryFromOnlineCountry_entry(dword_t id) { return xeXamGetCountryFromOnlineCountry(static_cast(id)); } DECLARE_XAM_EXPORT1(XamGetCountryFromOnlineCountry, kLocale, kImplemented); -dword_result_t XamGetLocaleEx(dword_t max_country_id, dword_t max_locale_id) { +dword_result_t XamGetLocaleEx_entry(dword_t max_country_id, + dword_t max_locale_id) { return xeXamGetLocaleEx(static_cast(max_country_id), static_cast(max_locale_id)); } diff --git a/src/xenia/kernel/xam/xam_msg.cc b/src/xenia/kernel/xam/xam_msg.cc index aedac0573..3251b0d05 100644 --- a/src/xenia/kernel/xam/xam_msg.cc +++ b/src/xenia/kernel/xam/xam_msg.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -20,8 +20,8 @@ namespace xe { namespace kernel { namespace xam { -dword_result_t XMsgInProcessCall(dword_t app, dword_t message, dword_t arg1, - dword_t arg2) { +dword_result_t XMsgInProcessCall_entry(dword_t app, dword_t message, + dword_t arg1, dword_t arg2) { auto result = kernel_state()->app_manager()->DispatchMessageSync(app, message, arg1, arg2); if (result == X_ERROR_NOT_FOUND) { @@ -31,8 +31,9 @@ dword_result_t XMsgInProcessCall(dword_t app, dword_t message, dword_t arg1, } DECLARE_XAM_EXPORT1(XMsgInProcessCall, kNone, kImplemented); -dword_result_t XMsgSystemProcessCall(dword_t app, dword_t message, - dword_t buffer, dword_t buffer_length) { +dword_result_t XMsgSystemProcessCall_entry(dword_t app, dword_t message, + dword_t buffer, + dword_t buffer_length) { auto result = kernel_state()->app_manager()->DispatchMessageAsync( app, message, buffer, buffer_length); if (result == X_ERROR_NOT_FOUND) { @@ -68,7 +69,7 @@ X_HRESULT xeXMsgStartIORequestEx(uint32_t app, uint32_t message, return result; } -dword_result_t XMsgStartIORequestEx( +dword_result_t XMsgStartIORequestEx_entry( dword_t app, dword_t message, pointer_t overlapped_ptr, dword_t buffer_ptr, dword_t buffer_length, pointer_t unknown_ptr) { @@ -77,16 +78,16 @@ dword_result_t XMsgStartIORequestEx( } DECLARE_XAM_EXPORT1(XMsgStartIORequestEx, kNone, kImplemented); -dword_result_t XMsgStartIORequest(dword_t app, dword_t message, - pointer_t overlapped_ptr, - dword_t buffer_ptr, dword_t buffer_length) { +dword_result_t XMsgStartIORequest_entry( + dword_t app, dword_t message, pointer_t overlapped_ptr, + dword_t buffer_ptr, dword_t buffer_length) { return xeXMsgStartIORequestEx(app, message, overlapped_ptr, buffer_ptr, buffer_length, nullptr); } DECLARE_XAM_EXPORT1(XMsgStartIORequest, kNone, kImplemented); -dword_result_t XMsgCancelIORequest(pointer_t overlapped_ptr, - dword_t wait) { +dword_result_t XMsgCancelIORequest_entry( + pointer_t overlapped_ptr, dword_t wait) { X_HANDLE event_handle = XOverlappedGetEvent(overlapped_ptr); if (event_handle && wait) { auto ev = @@ -100,17 +101,18 @@ dword_result_t XMsgCancelIORequest(pointer_t overlapped_ptr, } DECLARE_XAM_EXPORT1(XMsgCancelIORequest, kNone, kImplemented); -dword_result_t XMsgCompleteIORequest(pointer_t overlapped_ptr, - dword_t result, dword_t extended_error, - dword_t length) { +dword_result_t XMsgCompleteIORequest_entry( + pointer_t overlapped_ptr, dword_t result, + dword_t extended_error, dword_t length) { kernel_state()->CompleteOverlappedImmediateEx(overlapped_ptr, result, extended_error, length); return X_ERROR_SUCCESS; } DECLARE_XAM_EXPORT2(XMsgCompleteIORequest, kNone, kImplemented, kSketchy); -dword_result_t XamGetOverlappedResult(pointer_t overlapped_ptr, - lpdword_t length_ptr, dword_t unknown) { +dword_result_t XamGetOverlappedResult_entry( + pointer_t overlapped_ptr, lpdword_t length_ptr, + dword_t unknown) { uint32_t result; if (overlapped_ptr->result != X_ERROR_IO_PENDING) { result = overlapped_ptr->result; diff --git a/src/xenia/kernel/xam/xam_net.cc b/src/xenia/kernel/xam/xam_net.cc index 4c5c0ead4..67af4622c 100644 --- a/src/xenia/kernel/xam/xam_net.cc +++ b/src/xenia/kernel/xam/xam_net.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -169,8 +169,8 @@ struct XNetStartupParams { XNetStartupParams xnet_startup_params = {0}; -dword_result_t NetDll_XNetStartup(dword_t caller, - pointer_t params) { +dword_result_t NetDll_XNetStartup_entry(dword_t caller, + pointer_t params) { if (params) { assert_true(params->cfgSizeOfStruct == sizeof(XNetStartupParams)); std::memcpy(&xnet_startup_params, params, sizeof(XNetStartupParams)); @@ -191,7 +191,7 @@ dword_result_t NetDll_XNetStartup(dword_t caller, } DECLARE_XAM_EXPORT1(NetDll_XNetStartup, kNetworking, kImplemented); -dword_result_t NetDll_XNetCleanup(dword_t caller, lpvoid_t params) { +dword_result_t NetDll_XNetCleanup_entry(dword_t caller, lpvoid_t params) { auto xam = kernel_state()->GetKernelModule("xam.xex"); // auto xnet = xam->xnet(); // xam->set_xnet(nullptr); @@ -203,8 +203,9 @@ dword_result_t NetDll_XNetCleanup(dword_t caller, lpvoid_t params) { } DECLARE_XAM_EXPORT1(NetDll_XNetCleanup, kNetworking, kImplemented); -dword_result_t NetDll_XNetGetOpt(dword_t one, dword_t option_id, - lpvoid_t buffer_ptr, lpdword_t buffer_size) { +dword_result_t NetDll_XNetGetOpt_entry(dword_t one, dword_t option_id, + lpvoid_t buffer_ptr, + lpdword_t buffer_size) { assert_true(one == 1); switch (option_id) { case 1: @@ -221,8 +222,8 @@ dword_result_t NetDll_XNetGetOpt(dword_t one, dword_t option_id, } DECLARE_XAM_EXPORT1(NetDll_XNetGetOpt, kNetworking, kSketchy); -dword_result_t NetDll_XNetRandom(dword_t caller, lpvoid_t buffer_ptr, - dword_t length) { +dword_result_t NetDll_XNetRandom_entry(dword_t caller, lpvoid_t buffer_ptr, + dword_t length) { // For now, constant values. // This makes replicating things easier. std::memset(buffer_ptr, 0xBB, length); @@ -231,8 +232,8 @@ dword_result_t NetDll_XNetRandom(dword_t caller, lpvoid_t buffer_ptr, } DECLARE_XAM_EXPORT1(NetDll_XNetRandom, kNetworking, kStub); -dword_result_t NetDll_WSAStartup(dword_t caller, word_t version, - pointer_t data_ptr) { +dword_result_t NetDll_WSAStartup_entry(dword_t caller, word_t version, + pointer_t data_ptr) { // TODO(benvanik): abstraction layer needed. #ifdef XE_PLATFORM_WIN32 WSADATA wsaData; @@ -281,22 +282,22 @@ dword_result_t NetDll_WSAStartup(dword_t caller, word_t version, } DECLARE_XAM_EXPORT1(NetDll_WSAStartup, kNetworking, kImplemented); -dword_result_t NetDll_WSACleanup(dword_t caller) { +dword_result_t NetDll_WSACleanup_entry(dword_t caller) { // This does nothing. Xenia needs WSA running. return 0; } DECLARE_XAM_EXPORT1(NetDll_WSACleanup, kNetworking, kImplemented); -dword_result_t NetDll_WSAGetLastError() { return XThread::GetLastError(); } +dword_result_t NetDll_WSAGetLastError_entry() { + return XThread::GetLastError(); +} DECLARE_XAM_EXPORT1(NetDll_WSAGetLastError, kNetworking, kImplemented); -dword_result_t NetDll_WSARecvFrom(dword_t caller, dword_t socket, - pointer_t buffers_ptr, - dword_t buffer_count, - lpdword_t num_bytes_recv, lpdword_t flags_ptr, - pointer_t from_addr, - pointer_t overlapped_ptr, - lpvoid_t completion_routine_ptr) { +dword_result_t NetDll_WSARecvFrom_entry( + dword_t caller, dword_t socket, pointer_t buffers_ptr, + dword_t buffer_count, lpdword_t num_bytes_recv, lpdword_t flags_ptr, + pointer_t from_addr, pointer_t overlapped_ptr, + lpvoid_t completion_routine_ptr) { if (overlapped_ptr) { // auto evt = kernel_state()->object_table()->LookupObject( // overlapped_ptr->event_handle); @@ -314,12 +315,11 @@ DECLARE_XAM_EXPORT2(NetDll_WSARecvFrom, kNetworking, kStub, kHighFrequency); // If the socket is a VDP socket, buffer 0 is the game data length, and buffer 1 // is the unencrypted game data. -dword_result_t NetDll_WSASendTo(dword_t caller, dword_t socket_handle, - pointer_t buffers, dword_t num_buffers, - lpdword_t num_bytes_sent, dword_t flags, - pointer_t to_ptr, dword_t to_len, - pointer_t overlapped, - lpvoid_t completion_routine) { +dword_result_t NetDll_WSASendTo_entry( + dword_t caller, dword_t socket_handle, pointer_t buffers, + dword_t num_buffers, lpdword_t num_bytes_sent, dword_t flags, + pointer_t to_ptr, dword_t to_len, + pointer_t overlapped, lpvoid_t completion_routine) { assert(!overlapped); assert(!completion_routine); @@ -357,11 +357,11 @@ dword_result_t NetDll_WSASendTo(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_WSASendTo, kNetworking, kImplemented); -dword_result_t NetDll_WSAWaitForMultipleEvents(dword_t num_events, - lpdword_t events, - dword_t wait_all, - dword_t timeout, - dword_t alertable) { +dword_result_t NetDll_WSAWaitForMultipleEvents_entry(dword_t num_events, + lpdword_t events, + dword_t wait_all, + dword_t timeout, + dword_t alertable) { if (num_events > 64) { XThread::SetLastError(87); // ERROR_INVALID_PARAMETER return ~0u; @@ -386,14 +386,14 @@ dword_result_t NetDll_WSAWaitForMultipleEvents(dword_t num_events, DECLARE_XAM_EXPORT2(NetDll_WSAWaitForMultipleEvents, kNetworking, kImplemented, kBlocking); -dword_result_t NetDll_WSACreateEvent() { +dword_result_t NetDll_WSACreateEvent_entry() { XEvent* ev = new XEvent(kernel_state()); ev->Initialize(true, false); return ev->handle(); } DECLARE_XAM_EXPORT1(NetDll_WSACreateEvent, kNetworking, kImplemented); -dword_result_t NetDll_WSACloseEvent(dword_t event_handle) { +dword_result_t NetDll_WSACloseEvent_entry(dword_t event_handle) { X_STATUS result = kernel_state()->object_table()->ReleaseHandle(event_handle); if (XFAILED(result)) { uint32_t error = xboxkrnl::xeRtlNtStatusToDosError(result); @@ -404,7 +404,7 @@ dword_result_t NetDll_WSACloseEvent(dword_t event_handle) { } DECLARE_XAM_EXPORT1(NetDll_WSACloseEvent, kNetworking, kImplemented); -dword_result_t NetDll_WSAResetEvent(dword_t event_handle) { +dword_result_t NetDll_WSAResetEvent_entry(dword_t event_handle) { X_STATUS result = xboxkrnl::xeNtClearEvent(event_handle); if (XFAILED(result)) { uint32_t error = xboxkrnl::xeRtlNtStatusToDosError(result); @@ -415,7 +415,7 @@ dword_result_t NetDll_WSAResetEvent(dword_t event_handle) { } DECLARE_XAM_EXPORT1(NetDll_WSAResetEvent, kNetworking, kImplemented); -dword_result_t NetDll_WSASetEvent(dword_t event_handle) { +dword_result_t NetDll_WSASetEvent_entry(dword_t event_handle) { X_STATUS result = xboxkrnl::xeNtSetEvent(event_handle, nullptr); if (XFAILED(result)) { uint32_t error = xboxkrnl::xeRtlNtStatusToDosError(result); @@ -449,8 +449,8 @@ struct XnAddrStatus { static const uint32_t XNET_GET_XNADDR_TROUBLESHOOT = 0x00008000; }; -dword_result_t NetDll_XNetGetTitleXnAddr(dword_t caller, - pointer_t addr_ptr) { +dword_result_t NetDll_XNetGetTitleXnAddr_entry(dword_t caller, + pointer_t addr_ptr) { // Just return a loopback address atm. addr_ptr->ina.s_addr = htonl(INADDR_LOOPBACK); addr_ptr->inaOnline.s_addr = 0; @@ -475,8 +475,8 @@ dword_result_t NetDll_XNetGetTitleXnAddr(dword_t caller, } DECLARE_XAM_EXPORT1(NetDll_XNetGetTitleXnAddr, kNetworking, kStub); -dword_result_t NetDll_XNetGetDebugXnAddr(dword_t caller, - pointer_t addr_ptr) { +dword_result_t NetDll_XNetGetDebugXnAddr_entry(dword_t caller, + pointer_t addr_ptr) { addr_ptr.Zero(); // XNET_GET_XNADDR_NONE causes caller to gracefully return. @@ -484,41 +484,43 @@ dword_result_t NetDll_XNetGetDebugXnAddr(dword_t caller, } DECLARE_XAM_EXPORT1(NetDll_XNetGetDebugXnAddr, kNetworking, kStub); -dword_result_t NetDll_XNetXnAddrToMachineId(dword_t caller, - pointer_t addr_ptr, - lpdword_t id_ptr) { +dword_result_t NetDll_XNetXnAddrToMachineId_entry(dword_t caller, + pointer_t addr_ptr, + lpdword_t id_ptr) { // Tell the caller we're not signed in to live (non-zero ret) return 1; } DECLARE_XAM_EXPORT1(NetDll_XNetXnAddrToMachineId, kNetworking, kStub); -void NetDll_XNetInAddrToString(dword_t caller, dword_t in_addr, - lpstring_t string_out, dword_t string_size) { +void NetDll_XNetInAddrToString_entry(dword_t caller, dword_t in_addr, + lpstring_t string_out, + dword_t string_size) { strncpy(string_out, "666.666.666.666", string_size); } DECLARE_XAM_EXPORT1(NetDll_XNetInAddrToString, kNetworking, kStub); // This converts a XNet address to an IN_ADDR. The IN_ADDR is used for // subsequent socket calls (like a handle to a XNet address) -dword_result_t NetDll_XNetXnAddrToInAddr(dword_t caller, - pointer_t xn_addr, - lpvoid_t xid, lpvoid_t in_addr) { +dword_result_t NetDll_XNetXnAddrToInAddr_entry(dword_t caller, + pointer_t xn_addr, + lpvoid_t xid, lpvoid_t in_addr) { return 1; } DECLARE_XAM_EXPORT1(NetDll_XNetXnAddrToInAddr, kNetworking, kStub); // Does the reverse of the above. // FIXME: Arguments may not be correct. -dword_result_t NetDll_XNetInAddrToXnAddr(dword_t caller, lpvoid_t in_addr, - pointer_t xn_addr, - lpvoid_t xid) { +dword_result_t NetDll_XNetInAddrToXnAddr_entry(dword_t caller, lpvoid_t in_addr, + pointer_t xn_addr, + lpvoid_t xid) { return 1; } DECLARE_XAM_EXPORT1(NetDll_XNetInAddrToXnAddr, kNetworking, kStub); // https://www.google.com/patents/WO2008112448A1?cl=en // Reserves a port for use by system link -dword_result_t NetDll_XNetSetSystemLinkPort(dword_t caller, dword_t port) { +dword_result_t NetDll_XNetSetSystemLinkPort_entry(dword_t caller, + dword_t port) { return 1; } DECLARE_XAM_EXPORT1(NetDll_XNetSetSystemLinkPort, kNetworking, kStub); @@ -532,11 +534,14 @@ struct XEthernetStatus { static const uint32_t XNET_ETHERNET_LINK_HALF_DUPLEX = 0x10; }; -dword_result_t NetDll_XNetGetEthernetLinkStatus(dword_t caller) { return 0; } +dword_result_t NetDll_XNetGetEthernetLinkStatus_entry(dword_t caller) { + return 0; +} DECLARE_XAM_EXPORT1(NetDll_XNetGetEthernetLinkStatus, kNetworking, kStub); -dword_result_t NetDll_XNetDnsLookup(dword_t caller, lpstring_t host, - dword_t event_handle, lpdword_t pdns) { +dword_result_t NetDll_XNetDnsLookup_entry(dword_t caller, lpstring_t host, + dword_t event_handle, + lpdword_t pdns) { // TODO(gibbed): actually implement this if (pdns) { auto dns_guest = kernel_memory()->SystemHeapAlloc(sizeof(XNDNS)); @@ -554,7 +559,8 @@ dword_result_t NetDll_XNetDnsLookup(dword_t caller, lpstring_t host, } DECLARE_XAM_EXPORT1(NetDll_XNetDnsLookup, kNetworking, kStub); -dword_result_t NetDll_XNetDnsRelease(dword_t caller, pointer_t dns) { +dword_result_t NetDll_XNetDnsRelease_entry(dword_t caller, + pointer_t dns) { if (!dns) { return X_STATUS_INVALID_PARAMETER; } @@ -563,9 +569,9 @@ dword_result_t NetDll_XNetDnsRelease(dword_t caller, pointer_t dns) { } DECLARE_XAM_EXPORT1(NetDll_XNetDnsRelease, kNetworking, kStub); -dword_result_t NetDll_XNetQosServiceLookup(dword_t caller, dword_t flags, - dword_t event_handle, - lpdword_t pqos) { +dword_result_t NetDll_XNetQosServiceLookup_entry(dword_t caller, dword_t flags, + dword_t event_handle, + lpdword_t pqos) { // Set pqos as some games will try accessing it despite non-successful result if (pqos) { auto qos_guest = kernel_memory()->SystemHeapAlloc(sizeof(XNQOS)); @@ -583,7 +589,8 @@ dword_result_t NetDll_XNetQosServiceLookup(dword_t caller, dword_t flags, } DECLARE_XAM_EXPORT1(NetDll_XNetQosServiceLookup, kNetworking, kStub); -dword_result_t NetDll_XNetQosRelease(dword_t caller, pointer_t qos) { +dword_result_t NetDll_XNetQosRelease_entry(dword_t caller, + pointer_t qos) { if (!qos) { return X_STATUS_INVALID_PARAMETER; } @@ -592,14 +599,14 @@ dword_result_t NetDll_XNetQosRelease(dword_t caller, pointer_t qos) { } DECLARE_XAM_EXPORT1(NetDll_XNetQosRelease, kNetworking, kStub); -dword_result_t NetDll_XNetQosListen(dword_t caller, lpvoid_t id, lpvoid_t data, - dword_t data_size, dword_t r7, - dword_t flags) { +dword_result_t NetDll_XNetQosListen_entry(dword_t caller, lpvoid_t id, + lpvoid_t data, dword_t data_size, + dword_t r7, dword_t flags) { return X_ERROR_FUNCTION_FAILED; } DECLARE_XAM_EXPORT1(NetDll_XNetQosListen, kNetworking, kStub); -dword_result_t NetDll_inet_addr(lpstring_t addr_ptr) { +dword_result_t NetDll_inet_addr_entry(lpstring_t addr_ptr) { if (!addr_ptr) { return -1; } @@ -616,8 +623,8 @@ dword_result_t NetDll_inet_addr(lpstring_t addr_ptr) { } DECLARE_XAM_EXPORT1(NetDll_inet_addr, kNetworking, kImplemented); -dword_result_t NetDll_socket(dword_t caller, dword_t af, dword_t type, - dword_t protocol) { +dword_result_t NetDll_socket_entry(dword_t caller, dword_t af, dword_t type, + dword_t protocol) { XSocket* socket = new XSocket(kernel_state()); X_STATUS result = socket->Initialize(XSocket::AddressFamily((uint32_t)af), XSocket::Type((uint32_t)type), @@ -635,7 +642,7 @@ dword_result_t NetDll_socket(dword_t caller, dword_t af, dword_t type, } DECLARE_XAM_EXPORT1(NetDll_socket, kNetworking, kImplemented); -dword_result_t NetDll_closesocket(dword_t caller, dword_t socket_handle) { +dword_result_t NetDll_closesocket_entry(dword_t caller, dword_t socket_handle) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -652,7 +659,8 @@ dword_result_t NetDll_closesocket(dword_t caller, dword_t socket_handle) { } DECLARE_XAM_EXPORT1(NetDll_closesocket, kNetworking, kImplemented); -int_result_t NetDll_shutdown(dword_t caller, dword_t socket_handle, int_t how) { +int_result_t NetDll_shutdown_entry(dword_t caller, dword_t socket_handle, + int_t how) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -674,9 +682,9 @@ int_result_t NetDll_shutdown(dword_t caller, dword_t socket_handle, int_t how) { } DECLARE_XAM_EXPORT1(NetDll_shutdown, kNetworking, kImplemented); -dword_result_t NetDll_setsockopt(dword_t caller, dword_t socket_handle, - dword_t level, dword_t optname, - lpvoid_t optval_ptr, dword_t optlen) { +dword_result_t NetDll_setsockopt_entry(dword_t caller, dword_t socket_handle, + dword_t level, dword_t optname, + lpvoid_t optval_ptr, dword_t optlen) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -690,8 +698,8 @@ dword_result_t NetDll_setsockopt(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_setsockopt, kNetworking, kImplemented); -dword_result_t NetDll_ioctlsocket(dword_t caller, dword_t socket_handle, - dword_t cmd, lpvoid_t arg_ptr) { +dword_result_t NetDll_ioctlsocket_entry(dword_t caller, dword_t socket_handle, + dword_t cmd, lpvoid_t arg_ptr) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -711,8 +719,9 @@ dword_result_t NetDll_ioctlsocket(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_ioctlsocket, kNetworking, kImplemented); -dword_result_t NetDll_bind(dword_t caller, dword_t socket_handle, - pointer_t name, dword_t namelen) { +dword_result_t NetDll_bind_entry(dword_t caller, dword_t socket_handle, + pointer_t name, + dword_t namelen) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -732,8 +741,9 @@ dword_result_t NetDll_bind(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_bind, kNetworking, kImplemented); -dword_result_t NetDll_connect(dword_t caller, dword_t socket_handle, - pointer_t name, dword_t namelen) { +dword_result_t NetDll_connect_entry(dword_t caller, dword_t socket_handle, + pointer_t name, + dword_t namelen) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -753,8 +763,8 @@ dword_result_t NetDll_connect(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_connect, kNetworking, kImplemented); -dword_result_t NetDll_listen(dword_t caller, dword_t socket_handle, - int_t backlog) { +dword_result_t NetDll_listen_entry(dword_t caller, dword_t socket_handle, + int_t backlog) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -773,9 +783,9 @@ dword_result_t NetDll_listen(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_listen, kNetworking, kImplemented); -dword_result_t NetDll_accept(dword_t caller, dword_t socket_handle, - pointer_t addr_ptr, - lpdword_t addrlen_ptr) { +dword_result_t NetDll_accept_entry(dword_t caller, dword_t socket_handle, + pointer_t addr_ptr, + lpdword_t addrlen_ptr) { if (!addr_ptr) { // WSAEFAULT XThread::SetLastError(0x271E); @@ -858,11 +868,11 @@ struct host_set { } }; -int_result_t NetDll_select(int_t caller, int_t nfds, - pointer_t readfds, - pointer_t writefds, - pointer_t exceptfds, - lpvoid_t timeout_ptr) { +int_result_t NetDll_select_entry(int_t caller, int_t nfds, + pointer_t readfds, + pointer_t writefds, + pointer_t exceptfds, + lpvoid_t timeout_ptr) { host_set host_readfds = {0}; fd_set native_readfds = {0}; if (readfds) { @@ -912,8 +922,9 @@ int_result_t NetDll_select(int_t caller, int_t nfds, } DECLARE_XAM_EXPORT1(NetDll_select, kNetworking, kImplemented); -dword_result_t NetDll_recv(dword_t caller, dword_t socket_handle, - lpvoid_t buf_ptr, dword_t buf_len, dword_t flags) { +dword_result_t NetDll_recv_entry(dword_t caller, dword_t socket_handle, + lpvoid_t buf_ptr, dword_t buf_len, + dword_t flags) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -926,10 +937,11 @@ dword_result_t NetDll_recv(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_recv, kNetworking, kImplemented); -dword_result_t NetDll_recvfrom(dword_t caller, dword_t socket_handle, - lpvoid_t buf_ptr, dword_t buf_len, dword_t flags, - pointer_t from_ptr, - lpdword_t fromlen_ptr) { +dword_result_t NetDll_recvfrom_entry(dword_t caller, dword_t socket_handle, + lpvoid_t buf_ptr, dword_t buf_len, + dword_t flags, + pointer_t from_ptr, + lpdword_t fromlen_ptr) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -970,8 +982,9 @@ dword_result_t NetDll_recvfrom(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_recvfrom, kNetworking, kImplemented); -dword_result_t NetDll_send(dword_t caller, dword_t socket_handle, - lpvoid_t buf_ptr, dword_t buf_len, dword_t flags) { +dword_result_t NetDll_send_entry(dword_t caller, dword_t socket_handle, + lpvoid_t buf_ptr, dword_t buf_len, + dword_t flags) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -984,9 +997,11 @@ dword_result_t NetDll_send(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_send, kNetworking, kImplemented); -dword_result_t NetDll_sendto(dword_t caller, dword_t socket_handle, - lpvoid_t buf_ptr, dword_t buf_len, dword_t flags, - pointer_t to_ptr, dword_t to_len) { +dword_result_t NetDll_sendto_entry(dword_t caller, dword_t socket_handle, + lpvoid_t buf_ptr, dword_t buf_len, + dword_t flags, + pointer_t to_ptr, + dword_t to_len) { auto socket = kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { @@ -1000,8 +1015,8 @@ dword_result_t NetDll_sendto(dword_t caller, dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll_sendto, kNetworking, kImplemented); -dword_result_t NetDll___WSAFDIsSet(dword_t socket_handle, - pointer_t fd_set) { +dword_result_t NetDll___WSAFDIsSet_entry(dword_t socket_handle, + pointer_t fd_set) { for (uint32_t i = 0; i < fd_set->fd_count.value; i++) { if (fd_set->fd_array[i] == socket_handle) { return 1; @@ -1011,7 +1026,7 @@ dword_result_t NetDll___WSAFDIsSet(dword_t socket_handle, } DECLARE_XAM_EXPORT1(NetDll___WSAFDIsSet, kNetworking, kImplemented); -void NetDll_WSASetLastError(dword_t error_code) { +void NetDll_WSASetLastError_entry(dword_t error_code) { XThread::SetLastError(error_code); } DECLARE_XAM_EXPORT1(NetDll_WSASetLastError, kNetworking, kImplemented); diff --git a/src/xenia/kernel/xam/xam_notify.cc b/src/xenia/kernel/xam/xam_notify.cc index 6d09df7f3..dc28466e9 100644 --- a/src/xenia/kernel/xam/xam_notify.cc +++ b/src/xenia/kernel/xam/xam_notify.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -36,20 +36,22 @@ uint32_t xeXamNotifyCreateListener(uint64_t mask, uint32_t is_system, return handle; } -dword_result_t XamNotifyCreateListener(qword_t mask, dword_t max_version) { +dword_result_t XamNotifyCreateListener_entry(qword_t mask, + dword_t max_version) { return xeXamNotifyCreateListener(mask, 0, max_version); } DECLARE_XAM_EXPORT1(XamNotifyCreateListener, kNone, kImplemented); -dword_result_t XamNotifyCreateListenerInternal(qword_t mask, dword_t is_system, - dword_t max_version) { +dword_result_t XamNotifyCreateListenerInternal_entry(qword_t mask, + dword_t is_system, + dword_t max_version) { return xeXamNotifyCreateListener(mask, is_system, max_version); } DECLARE_XAM_EXPORT1(XamNotifyCreateListenerInternal, kNone, kImplemented); // https://github.com/CodeAsm/ffplay360/blob/master/Common/AtgSignIn.cpp -dword_result_t XNotifyGetNext(dword_t handle, dword_t match_id, - lpdword_t id_ptr, lpdword_t param_ptr) { +dword_result_t XNotifyGetNext_entry(dword_t handle, dword_t match_id, + lpdword_t id_ptr, lpdword_t param_ptr) { if (param_ptr) { *param_ptr = 0; } @@ -89,13 +91,13 @@ dword_result_t XNotifyGetNext(dword_t handle, dword_t match_id, } DECLARE_XAM_EXPORT2(XNotifyGetNext, kNone, kImplemented, kHighFrequency); -dword_result_t XNotifyDelayUI(dword_t delay_ms) { +dword_result_t XNotifyDelayUI_entry(dword_t delay_ms) { // Ignored. return 0; } DECLARE_XAM_EXPORT1(XNotifyDelayUI, kNone, kStub); -void XNotifyPositionUI(dword_t position) { +void XNotifyPositionUI_entry(dword_t position) { // Ignored. } DECLARE_XAM_EXPORT1(XNotifyPositionUI, kNone, kStub); diff --git a/src/xenia/kernel/xam/xam_nui.cc b/src/xenia/kernel/xam/xam_nui.cc index d0f99087d..725517111 100644 --- a/src/xenia/kernel/xam/xam_nui.cc +++ b/src/xenia/kernel/xam/xam_nui.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -34,14 +34,14 @@ struct X_NUI_DEVICE_STATUS { }; static_assert(sizeof(X_NUI_DEVICE_STATUS) == 24, "Size matters"); -void XamNuiGetDeviceStatus(pointer_t status_ptr) { +void XamNuiGetDeviceStatus_entry(pointer_t status_ptr) { status_ptr.Zero(); status_ptr->status = 0; // Not connected. } DECLARE_XAM_EXPORT1(XamNuiGetDeviceStatus, kNone, kStub); -dword_result_t XamShowNuiTroubleshooterUI(unknown_t unk1, unknown_t unk2, - unknown_t unk3) { +dword_result_t XamShowNuiTroubleshooterUI_entry(unknown_t unk1, unknown_t unk2, + unknown_t unk3) { // unk1 is 0xFF - possibly user index? // unk2, unk3 appear to always be zero. diff --git a/src/xenia/kernel/xam/xam_party.cc b/src/xenia/kernel/xam/xam_party.cc index 5476f0b33..5e74dca02 100644 --- a/src/xenia/kernel/xam/xam_party.cc +++ b/src/xenia/kernel/xam/xam_party.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -16,24 +16,26 @@ namespace xe { namespace kernel { namespace xam { -dword_result_t XamPartyGetUserList(dword_t player_count, lpdword_t party_list) { +dword_result_t XamPartyGetUserList_entry(dword_t player_count, + lpdword_t party_list) { // 5345085D wants specifically this code to skip loading party data. // This code is not documented in NT_STATUS code list return 0x807D0003; } DECLARE_XAM_EXPORT1(XamPartyGetUserList, kNone, kStub); -dword_result_t XamPartySendGameInvites(dword_t r3, dword_t r4, dword_t r5) { +dword_result_t XamPartySendGameInvites_entry(dword_t r3, dword_t r4, + dword_t r5) { return X_ERROR_FUNCTION_FAILED; } DECLARE_XAM_EXPORT1(XamPartySendGameInvites, kNone, kStub); -dword_result_t XamPartySetCustomData(dword_t r3, dword_t r4, dword_t r5) { +dword_result_t XamPartySetCustomData_entry(dword_t r3, dword_t r4, dword_t r5) { return X_ERROR_FUNCTION_FAILED; } DECLARE_XAM_EXPORT1(XamPartySetCustomData, kNone, kStub); -dword_result_t XamPartyGetBandwidth(dword_t r3, dword_t r4) { +dword_result_t XamPartyGetBandwidth_entry(dword_t r3, dword_t r4) { return X_ERROR_FUNCTION_FAILED; } DECLARE_XAM_EXPORT1(XamPartyGetBandwidth, kNone, kStub); diff --git a/src/xenia/kernel/xam/xam_task.cc b/src/xenia/kernel/xam/xam_task.cc index 58f314079..82ae23025 100644 --- a/src/xenia/kernel/xam/xam_task.cc +++ b/src/xenia/kernel/xam/xam_task.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -39,9 +39,9 @@ struct XTASK_MESSAGE { }; static_assert_size(XTASK_MESSAGE, 0x1C); -dword_result_t XamTaskSchedule(lpvoid_t callback, - pointer_t message, - lpdword_t unknown, lpdword_t handle_ptr) { +dword_result_t XamTaskSchedule_entry(lpvoid_t callback, + pointer_t message, + lpdword_t unknown, lpdword_t handle_ptr) { // TODO(gibbed): figure out what this is for *handle_ptr = 12345; @@ -69,7 +69,7 @@ dword_result_t XamTaskSchedule(lpvoid_t callback, } DECLARE_XAM_EXPORT2(XamTaskSchedule, kNone, kImplemented, kSketchy); -dword_result_t XamTaskShouldExit(dword_t r3) { return 0; } +dword_result_t XamTaskShouldExit_entry(dword_t r3) { return 0; } DECLARE_XAM_EXPORT2(XamTaskShouldExit, kNone, kStub, kSketchy); void RegisterTaskExports(xe::cpu::ExportResolver* export_resolver, diff --git a/src/xenia/kernel/xam/xam_ui.cc b/src/xenia/kernel/xam/xam_ui.cc index 21b0590d6..4fc28fd7d 100644 --- a/src/xenia/kernel/xam/xam_ui.cc +++ b/src/xenia/kernel/xam/xam_ui.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -201,7 +201,7 @@ X_RESULT xeXamDispatchHeadlessEx( } } -dword_result_t XamIsUIActive() { return xeXamIsUIActive(); } +dword_result_t XamIsUIActive_entry() { return xeXamIsUIActive(); } DECLARE_XAM_EXPORT2(XamIsUIActive, kUI, kImplemented, kHighFrequency); class MessageBoxDialog : public XamDialog { @@ -263,11 +263,10 @@ class MessageBoxDialog : public XamDialog { }; // https://www.se7ensins.com/forums/threads/working-xshowmessageboxui.844116/ -dword_result_t XamShowMessageBoxUI(dword_t user_index, lpu16string_t title_ptr, - lpu16string_t text_ptr, dword_t button_count, - lpdword_t button_ptrs, dword_t active_button, - dword_t flags, lpdword_t result_ptr, - pointer_t overlapped) { +dword_result_t XamShowMessageBoxUI_entry( + dword_t user_index, lpu16string_t title_ptr, lpu16string_t text_ptr, + dword_t button_count, lpdword_t button_ptrs, dword_t active_button, + dword_t flags, lpdword_t result_ptr, pointer_t overlapped) { std::string title; if (title_ptr) { title = xe::to_utf8(title_ptr.value()); @@ -404,11 +403,10 @@ class KeyboardInputDialog : public XamDialog { }; // https://www.se7ensins.com/forums/threads/release-how-to-use-xshowkeyboardui-release.906568/ -dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags, - lpu16string_t default_text, - lpu16string_t title, lpu16string_t description, - lpu16string_t buffer, dword_t buffer_length, - pointer_t overlapped) { +dword_result_t XamShowKeyboardUI_entry( + dword_t user_index, dword_t flags, lpu16string_t default_text, + lpu16string_t title, lpu16string_t description, lpu16string_t buffer, + dword_t buffer_length, pointer_t overlapped) { if (!buffer) { return X_ERROR_INVALID_PARAMETER; } @@ -461,11 +459,10 @@ dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags, } DECLARE_XAM_EXPORT1(XamShowKeyboardUI, kUI, kImplemented); -dword_result_t XamShowDeviceSelectorUI(dword_t user_index, dword_t content_type, - dword_t content_flags, - qword_t total_requested, - lpdword_t device_id_ptr, - pointer_t overlapped) { +dword_result_t XamShowDeviceSelectorUI_entry( + dword_t user_index, dword_t content_type, dword_t content_flags, + qword_t total_requested, lpdword_t device_id_ptr, + pointer_t overlapped) { return xeXamDispatchHeadless( [device_id_ptr]() -> X_RESULT { // NOTE: 0x00000001 is our dummy device ID from xam_content.cc @@ -476,7 +473,7 @@ dword_result_t XamShowDeviceSelectorUI(dword_t user_index, dword_t content_type, } DECLARE_XAM_EXPORT1(XamShowDeviceSelectorUI, kUI, kImplemented); -void XamShowDirtyDiscErrorUI(dword_t user_index) { +void XamShowDirtyDiscErrorUI_entry(dword_t user_index) { if (cvars::headless) { assert_always(); exit(1); @@ -496,12 +493,12 @@ void XamShowDirtyDiscErrorUI(dword_t user_index) { } DECLARE_XAM_EXPORT1(XamShowDirtyDiscErrorUI, kUI, kImplemented); -dword_result_t XamShowPartyUI(unknown_t r3, unknown_t r4) { +dword_result_t XamShowPartyUI_entry(unknown_t r3, unknown_t r4) { return X_ERROR_FUNCTION_FAILED; } DECLARE_XAM_EXPORT1(XamShowPartyUI, kNone, kStub); -dword_result_t XamShowCommunitySessionsUI(unknown_t r3, unknown_t r4) { +dword_result_t XamShowCommunitySessionsUI_entry(unknown_t r3, unknown_t r4) { return X_ERROR_FUNCTION_FAILED; } DECLARE_XAM_EXPORT1(XamShowCommunitySessionsUI, kNone, kStub); diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 612cc326c..6da2d1635 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -23,8 +23,8 @@ namespace xe { namespace kernel { namespace xam { -X_HRESULT_result_t XamUserGetXUID(dword_t user_index, dword_t type_mask, - lpqword_t xuid_ptr) { +X_HRESULT_result_t XamUserGetXUID_entry(dword_t user_index, dword_t type_mask, + lpqword_t xuid_ptr) { assert_true(type_mask == 1 || type_mask == 2 || type_mask == 3 || type_mask == 4 || type_mask == 7); if (!xuid_ptr) { @@ -54,7 +54,7 @@ X_HRESULT_result_t XamUserGetXUID(dword_t user_index, dword_t type_mask, } DECLARE_XAM_EXPORT1(XamUserGetXUID, kUserProfiles, kImplemented); -dword_result_t XamUserGetSigninState(dword_t user_index) { +dword_result_t XamUserGetSigninState_entry(dword_t user_index) { // Yield, as some games spam this. xe::threading::MaybeYield(); uint32_t signin_state = 0; @@ -79,8 +79,8 @@ typedef struct { } X_USER_SIGNIN_INFO; static_assert_size(X_USER_SIGNIN_INFO, 40); -X_HRESULT_result_t XamUserGetSigninInfo(dword_t user_index, dword_t flags, - pointer_t info) { +X_HRESULT_result_t XamUserGetSigninInfo_entry( + dword_t user_index, dword_t flags, pointer_t info) { if (!info) { return X_E_INVALIDARG; } @@ -99,8 +99,8 @@ X_HRESULT_result_t XamUserGetSigninInfo(dword_t user_index, dword_t flags, } DECLARE_XAM_EXPORT1(XamUserGetSigninInfo, kUserProfiles, kImplemented); -dword_result_t XamUserGetName(dword_t user_index, lpstring_t buffer, - dword_t buffer_len) { +dword_result_t XamUserGetName_entry(dword_t user_index, lpstring_t buffer, + dword_t buffer_len) { if (user_index >= 4) { return X_E_INVALIDARG; } @@ -117,8 +117,9 @@ dword_result_t XamUserGetName(dword_t user_index, lpstring_t buffer, } DECLARE_XAM_EXPORT1(XamUserGetName, kUserProfiles, kImplemented); -dword_result_t XamUserGetGamerTag(dword_t user_index, lpu16string_t buffer, - dword_t buffer_len) { +dword_result_t XamUserGetGamerTag_entry(dword_t user_index, + lpu16string_t buffer, + dword_t buffer_len) { if (user_index >= 4) { return X_E_INVALIDARG; } @@ -304,7 +305,7 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, return X_ERROR_SUCCESS; } -dword_result_t XamUserReadProfileSettings( +dword_result_t XamUserReadProfileSettings_entry( dword_t title_id, dword_t user_index, dword_t xuid_count, lpqword_t xuids, dword_t setting_count, lpdword_t setting_ids, lpdword_t buffer_size_ptr, lpvoid_t buffer_ptr, dword_t overlapped_ptr) { @@ -314,7 +315,7 @@ dword_result_t XamUserReadProfileSettings( } DECLARE_XAM_EXPORT1(XamUserReadProfileSettings, kUserProfiles, kImplemented); -dword_result_t XamUserReadProfileSettingsEx( +dword_result_t XamUserReadProfileSettingsEx_entry( dword_t title_id, dword_t user_index, dword_t xuid_count, lpqword_t xuids, dword_t setting_count, lpdword_t setting_ids, lpdword_t buffer_size_ptr, dword_t unk_2, lpvoid_t buffer_ptr, dword_t overlapped_ptr) { @@ -347,7 +348,7 @@ typedef struct { }; } X_USER_WRITE_PROFILE_SETTING; -dword_result_t XamUserWriteProfileSettings( +dword_result_t XamUserWriteProfileSettings_entry( dword_t title_id, dword_t user_index, dword_t setting_count, pointer_t settings, dword_t overlapped_ptr) { if (!setting_count || !settings) { @@ -423,8 +424,8 @@ dword_result_t XamUserWriteProfileSettings( } DECLARE_XAM_EXPORT1(XamUserWriteProfileSettings, kUserProfiles, kImplemented); -dword_result_t XamUserCheckPrivilege(dword_t user_index, dword_t mask, - lpdword_t out_value) { +dword_result_t XamUserCheckPrivilege_entry(dword_t user_index, dword_t mask, + lpdword_t out_value) { // checking all users? if (user_index != 0xFF) { if (user_index >= 4) { @@ -442,8 +443,8 @@ dword_result_t XamUserCheckPrivilege(dword_t user_index, dword_t mask, } DECLARE_XAM_EXPORT1(XamUserCheckPrivilege, kUserProfiles, kStub); -dword_result_t XamUserContentRestrictionGetFlags(dword_t user_index, - lpdword_t out_flags) { +dword_result_t XamUserContentRestrictionGetFlags_entry(dword_t user_index, + lpdword_t out_flags) { if (user_index) { return X_ERROR_NO_SUCH_USER; } @@ -454,10 +455,10 @@ dword_result_t XamUserContentRestrictionGetFlags(dword_t user_index, } DECLARE_XAM_EXPORT1(XamUserContentRestrictionGetFlags, kUserProfiles, kStub); -dword_result_t XamUserContentRestrictionGetRating(dword_t user_index, - dword_t unk1, - lpdword_t out_unk2, - lpdword_t out_unk3) { +dword_result_t XamUserContentRestrictionGetRating_entry(dword_t user_index, + dword_t unk1, + lpdword_t out_unk2, + lpdword_t out_unk3) { if (user_index) { return X_ERROR_NO_SUCH_USER; } @@ -470,11 +471,9 @@ dword_result_t XamUserContentRestrictionGetRating(dword_t user_index, } DECLARE_XAM_EXPORT1(XamUserContentRestrictionGetRating, kUserProfiles, kStub); -dword_result_t XamUserContentRestrictionCheckAccess(dword_t user_index, - dword_t unk1, dword_t unk2, - dword_t unk3, dword_t unk4, - lpdword_t out_unk5, - dword_t overlapped_ptr) { +dword_result_t XamUserContentRestrictionCheckAccess_entry( + dword_t user_index, dword_t unk1, dword_t unk2, dword_t unk3, dword_t unk4, + lpdword_t out_unk5, dword_t overlapped_ptr) { *out_unk5 = 1; if (overlapped_ptr) { @@ -487,10 +486,10 @@ dword_result_t XamUserContentRestrictionCheckAccess(dword_t user_index, } DECLARE_XAM_EXPORT1(XamUserContentRestrictionCheckAccess, kUserProfiles, kStub); -dword_result_t XamUserIsOnlineEnabled(dword_t user_index) { return 1; } +dword_result_t XamUserIsOnlineEnabled_entry(dword_t user_index) { return 1; } DECLARE_XAM_EXPORT1(XamUserIsOnlineEnabled, kUserProfiles, kStub); -dword_result_t XamUserGetMembershipTier(dword_t user_index) { +dword_result_t XamUserGetMembershipTier_entry(dword_t user_index) { if (user_index >= 4) { return X_ERROR_INVALID_PARAMETER; } @@ -501,9 +500,9 @@ dword_result_t XamUserGetMembershipTier(dword_t user_index) { } DECLARE_XAM_EXPORT1(XamUserGetMembershipTier, kUserProfiles, kStub); -dword_result_t XamUserAreUsersFriends(dword_t user_index, dword_t unk1, - dword_t unk2, lpdword_t out_value, - dword_t overlapped_ptr) { +dword_result_t XamUserAreUsersFriends_entry(dword_t user_index, dword_t unk1, + dword_t unk2, lpdword_t out_value, + dword_t overlapped_ptr) { uint32_t are_friends = 0; X_RESULT result; @@ -545,7 +544,7 @@ dword_result_t XamUserAreUsersFriends(dword_t user_index, dword_t unk1, } DECLARE_XAM_EXPORT1(XamUserAreUsersFriends, kUserProfiles, kStub); -dword_result_t XamShowSigninUI(dword_t unk, dword_t unk_mask) { +dword_result_t XamShowSigninUI_entry(dword_t unk, dword_t unk_mask) { // Mask values vary. Probably matching user types? Local/remote? // To fix game modes that display a 4 profile signin UI (even if playing @@ -676,12 +675,10 @@ class XStaticAchievementEnumerator : public XEnumerator { size_t current_item_ = 0; }; -dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id, - dword_t user_index, - dword_t xuid, dword_t flags, - dword_t offset, dword_t count, - lpdword_t buffer_size_ptr, - lpdword_t handle_ptr) { +dword_result_t XamUserCreateAchievementEnumerator_entry( + dword_t title_id, dword_t user_index, dword_t xuid, dword_t flags, + dword_t offset, dword_t count, lpdword_t buffer_size_ptr, + lpdword_t handle_ptr) { if (!count || !buffer_size_ptr || !handle_ptr) { return X_ERROR_INVALID_PARAMETER; } @@ -727,8 +724,9 @@ dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id, DECLARE_XAM_EXPORT1(XamUserCreateAchievementEnumerator, kUserProfiles, kSketchy); -dword_result_t XamParseGamerTileKey(lpdword_t key_ptr, lpdword_t out1_ptr, - lpdword_t out2_ptr, lpdword_t out3_ptr) { +dword_result_t XamParseGamerTileKey_entry(lpdword_t key_ptr, lpdword_t out1_ptr, + lpdword_t out2_ptr, + lpdword_t out3_ptr) { *out1_ptr = 0xC0DE0001; *out2_ptr = 0xC0DE0002; *out3_ptr = 0xC0DE0003; @@ -736,10 +734,11 @@ dword_result_t XamParseGamerTileKey(lpdword_t key_ptr, lpdword_t out1_ptr, } DECLARE_XAM_EXPORT1(XamParseGamerTileKey, kUserProfiles, kStub); -dword_result_t XamReadTileToTexture(dword_t unknown, dword_t title_id, - qword_t tile_id, dword_t user_index, - lpvoid_t buffer_ptr, dword_t stride, - dword_t height, dword_t overlapped_ptr) { +dword_result_t XamReadTileToTexture_entry(dword_t unknown, dword_t title_id, + qword_t tile_id, dword_t user_index, + lpvoid_t buffer_ptr, dword_t stride, + dword_t height, + dword_t overlapped_ptr) { // TODO(gibbed): unknown=0,2,3,9 if (!tile_id) { return X_ERROR_INVALID_PARAMETER; @@ -757,9 +756,9 @@ dword_result_t XamReadTileToTexture(dword_t unknown, dword_t title_id, } DECLARE_XAM_EXPORT1(XamReadTileToTexture, kUserProfiles, kStub); -dword_result_t XamWriteGamerTile(dword_t arg1, dword_t arg2, dword_t arg3, - dword_t arg4, dword_t arg5, - dword_t overlapped_ptr) { +dword_result_t XamWriteGamerTile_entry(dword_t arg1, dword_t arg2, dword_t arg3, + dword_t arg4, dword_t arg5, + dword_t overlapped_ptr) { if (overlapped_ptr) { kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS); @@ -769,13 +768,14 @@ dword_result_t XamWriteGamerTile(dword_t arg1, dword_t arg2, dword_t arg3, } DECLARE_XAM_EXPORT1(XamWriteGamerTile, kUserProfiles, kStub); -dword_result_t XamSessionCreateHandle(lpdword_t handle_ptr) { +dword_result_t XamSessionCreateHandle_entry(lpdword_t handle_ptr) { *handle_ptr = 0xCAFEDEAD; return X_ERROR_SUCCESS; } DECLARE_XAM_EXPORT1(XamSessionCreateHandle, kUserProfiles, kStub); -dword_result_t XamSessionRefObjByHandle(dword_t handle, lpdword_t obj_ptr) { +dword_result_t XamSessionRefObjByHandle_entry(dword_t handle, + lpdword_t obj_ptr) { assert_true(handle == 0xCAFEDEAD); // TODO(PermaNull): Implement this properly, // For the time being returning 0xDEADF00D will prevent crashing. diff --git a/src/xenia/kernel/xam/xam_video.cc b/src/xenia/kernel/xam/xam_video.cc index 8cdf63869..06a1d0029 100644 --- a/src/xenia/kernel/xam/xam_video.cc +++ b/src/xenia/kernel/xam/xam_video.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -18,13 +18,13 @@ namespace xe { namespace kernel { namespace xam { -void XGetVideoMode(pointer_t video_mode) { +void XGetVideoMode_entry(pointer_t video_mode) { // TODO(benvanik): actually check to see if these are the same. xboxkrnl::VdQueryVideoMode(std::move(video_mode)); } DECLARE_XAM_EXPORT1(XGetVideoMode, kVideo, ExportTag::kSketchy); -dword_result_t XGetVideoCapabilities() { return 0; } +dword_result_t XGetVideoCapabilities_entry() { return 0; } DECLARE_XAM_EXPORT1(XGetVideoCapabilities, kVideo, kStub); void RegisterVideoExports(xe::cpu::ExportResolver* export_resolver, diff --git a/src/xenia/kernel/xam/xam_voice.cc b/src/xenia/kernel/xam/xam_voice.cc index 2cd70b2ca..3d3379424 100644 --- a/src/xenia/kernel/xam/xam_voice.cc +++ b/src/xenia/kernel/xam/xam_voice.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,25 +17,25 @@ namespace xe { namespace kernel { namespace xam { -dword_result_t XamVoiceIsActiveProcess() { +dword_result_t XamVoiceIsActiveProcess_entry() { // Returning 0 here will short-circuit a bunch of voice stuff. return 0; } DECLARE_XAM_EXPORT1(XamVoiceIsActiveProcess, kNone, kStub); -dword_result_t XamVoiceCreate(unknown_t unk1, // 0 - unknown_t unk2, // 0xF - lpdword_t out_voice_ptr) { +dword_result_t XamVoiceCreate_entry(unknown_t unk1, // 0 + unknown_t unk2, // 0xF + lpdword_t out_voice_ptr) { // Null out the ptr. out_voice_ptr.Zero(); return X_ERROR_ACCESS_DENIED; } DECLARE_XAM_EXPORT1(XamVoiceCreate, kNone, kStub); -dword_result_t XamVoiceClose(lpunknown_t voice_ptr) { return 0; } +dword_result_t XamVoiceClose_entry(lpunknown_t voice_ptr) { return 0; } DECLARE_XAM_EXPORT1(XamVoiceClose, kNone, kStub); -dword_result_t XamVoiceHeadsetPresent(lpunknown_t voice_ptr) { return 0; } +dword_result_t XamVoiceHeadsetPresent_entry(lpunknown_t voice_ptr) { return 0; } DECLARE_XAM_EXPORT1(XamVoiceHeadsetPresent, kNone, kStub); void RegisterVoiceExports(xe::cpu::ExportResolver* export_resolver, diff --git a/src/xenia/kernel/xbdm/xbdm_misc.cc b/src/xenia/kernel/xbdm/xbdm_misc.cc index e15af9eb6..8db457c70 100644 --- a/src/xenia/kernel/xbdm/xbdm_misc.cc +++ b/src/xenia/kernel/xbdm/xbdm_misc.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -18,22 +18,22 @@ namespace xe { namespace kernel { namespace xbdm { -#define MAKE_DUMMY_STUB_PTR(x) \ - dword_result_t x() { return 0; } \ +#define MAKE_DUMMY_STUB_PTR(x) \ + dword_result_t x##_entry() { return 0; } \ DECLARE_XBDM_EXPORT1(x, kDebug, kStub) -#define MAKE_DUMMY_STUB_STATUS(x) \ - dword_result_t x() { return X_STATUS_INVALID_PARAMETER; } \ +#define MAKE_DUMMY_STUB_STATUS(x) \ + dword_result_t x##_entry() { return X_STATUS_INVALID_PARAMETER; } \ DECLARE_XBDM_EXPORT1(x, kDebug, kStub) MAKE_DUMMY_STUB_PTR(DmAllocatePool); -void DmCloseLoadedModules(lpdword_t unk0_ptr) {} +void DmCloseLoadedModules_entry(lpdword_t unk0_ptr) {} DECLARE_XBDM_EXPORT1(DmCloseLoadedModules, kDebug, kStub); MAKE_DUMMY_STUB_STATUS(DmFreePool); -dword_result_t DmGetXbeInfo() { +dword_result_t DmGetXbeInfo_entry() { // TODO(gibbed): 4D5307DC appears to expect this as success? // Unknown arguments -- let's hope things don't explode. return 0x02DA0000; @@ -42,17 +42,17 @@ DECLARE_XBDM_EXPORT1(DmGetXbeInfo, kDebug, kStub); MAKE_DUMMY_STUB_STATUS(DmGetXboxName); -dword_result_t DmIsDebuggerPresent() { return 0; } +dword_result_t DmIsDebuggerPresent_entry() { return 0; } DECLARE_XBDM_EXPORT1(DmIsDebuggerPresent, kDebug, kStub); MAKE_DUMMY_STUB_STATUS(DmRegisterCommandProcessor); -void DmSendNotificationString(lpdword_t unk0_ptr) {} +void DmSendNotificationString_entry(lpdword_t unk0_ptr) {} DECLARE_XBDM_EXPORT1(DmSendNotificationString, kDebug, kStub); -dword_result_t DmRegisterCommandProcessorEx(lpdword_t name_ptr, - lpdword_t handler_fn, - dword_t unk3) { +dword_result_t DmRegisterCommandProcessorEx_entry(lpdword_t name_ptr, + lpdword_t handler_fn, + dword_t unk3) { // Return success to prevent some games from stalling return X_STATUS_SUCCESS; } @@ -61,7 +61,8 @@ DECLARE_XBDM_EXPORT1(DmRegisterCommandProcessorEx, kDebug, kStub); MAKE_DUMMY_STUB_STATUS(DmStartProfiling); MAKE_DUMMY_STUB_STATUS(DmStopProfiling); -dword_result_t DmCaptureStackBackTrace(lpdword_t unk0_ptr, lpdword_t unk1_ptr) { +dword_result_t DmCaptureStackBackTrace_entry(lpdword_t unk0_ptr, + lpdword_t unk1_ptr) { return X_STATUS_INVALID_PARAMETER; } DECLARE_XBDM_EXPORT1(DmCaptureStackBackTrace, kDebug, kStub); @@ -69,16 +70,18 @@ DECLARE_XBDM_EXPORT1(DmCaptureStackBackTrace, kDebug, kStub); MAKE_DUMMY_STUB_STATUS(DmGetThreadInfoEx); MAKE_DUMMY_STUB_STATUS(DmSetProfilingOptions); -dword_result_t DmWalkLoadedModules(lpdword_t unk0_ptr, lpdword_t unk1_ptr) { +dword_result_t DmWalkLoadedModules_entry(lpdword_t unk0_ptr, + lpdword_t unk1_ptr) { // Some games will loop forever unless this code is returned return 0x82DA0104; } DECLARE_XBDM_EXPORT1(DmWalkLoadedModules, kDebug, kStub); -void DmMapDevkitDrive() {} +void DmMapDevkitDrive_entry() {} DECLARE_XBDM_EXPORT1(DmMapDevkitDrive, kDebug, kStub); -dword_result_t DmFindPdbSignature(lpdword_t unk0_ptr, lpdword_t unk1_ptr) { +dword_result_t DmFindPdbSignature_entry(lpdword_t unk0_ptr, + lpdword_t unk1_ptr) { return X_STATUS_INVALID_PARAMETER; } DECLARE_XBDM_EXPORT1(DmFindPdbSignature, kDebug, kStub); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc index d9c861211..cbf7e3d64 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -19,14 +19,14 @@ namespace xe { namespace kernel { namespace xboxkrnl { -dword_result_t XAudioGetSpeakerConfig(lpdword_t config_ptr) { +dword_result_t XAudioGetSpeakerConfig_entry(lpdword_t config_ptr) { *config_ptr = 0x00010001; return X_ERROR_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XAudioGetSpeakerConfig, kAudio, kImplemented); -dword_result_t XAudioGetVoiceCategoryVolumeChangeMask(lpunknown_t driver_ptr, - lpdword_t out_ptr) { +dword_result_t XAudioGetVoiceCategoryVolumeChangeMask_entry( + lpunknown_t driver_ptr, lpdword_t out_ptr) { assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000); xe::threading::MaybeYield(); @@ -39,7 +39,8 @@ dword_result_t XAudioGetVoiceCategoryVolumeChangeMask(lpunknown_t driver_ptr, DECLARE_XBOXKRNL_EXPORT2(XAudioGetVoiceCategoryVolumeChangeMask, kAudio, kStub, kHighFrequency); -dword_result_t XAudioGetVoiceCategoryVolume(dword_t unk, lpfloat_t out_ptr) { +dword_result_t XAudioGetVoiceCategoryVolume_entry(dword_t unk, + lpfloat_t out_ptr) { // Expects a floating point single. Volume %? *out_ptr = 1.0f; @@ -48,11 +49,11 @@ dword_result_t XAudioGetVoiceCategoryVolume(dword_t unk, lpfloat_t out_ptr) { DECLARE_XBOXKRNL_EXPORT2(XAudioGetVoiceCategoryVolume, kAudio, kStub, kHighFrequency); -dword_result_t XAudioEnableDucker(dword_t unk) { return X_ERROR_SUCCESS; } +dword_result_t XAudioEnableDucker_entry(dword_t unk) { return X_ERROR_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XAudioEnableDucker, kAudio, kStub); -dword_result_t XAudioRegisterRenderDriverClient(lpdword_t callback_ptr, - lpdword_t driver_ptr) { +dword_result_t XAudioRegisterRenderDriverClient_entry(lpdword_t callback_ptr, + lpdword_t driver_ptr) { uint32_t callback = callback_ptr[0]; uint32_t callback_arg = callback_ptr[1]; @@ -71,7 +72,8 @@ dword_result_t XAudioRegisterRenderDriverClient(lpdword_t callback_ptr, DECLARE_XBOXKRNL_EXPORT1(XAudioRegisterRenderDriverClient, kAudio, kImplemented); -dword_result_t XAudioUnregisterRenderDriverClient(lpunknown_t driver_ptr) { +dword_result_t XAudioUnregisterRenderDriverClient_entry( + lpunknown_t driver_ptr) { assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000); auto audio_system = kernel_state()->emulator()->audio_system(); @@ -81,8 +83,8 @@ dword_result_t XAudioUnregisterRenderDriverClient(lpunknown_t driver_ptr) { DECLARE_XBOXKRNL_EXPORT1(XAudioUnregisterRenderDriverClient, kAudio, kImplemented); -dword_result_t XAudioSubmitRenderDriverFrame(lpunknown_t driver_ptr, - lpunknown_t samples_ptr) { +dword_result_t XAudioSubmitRenderDriverFrame_entry(lpunknown_t driver_ptr, + lpunknown_t samples_ptr) { assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000); auto audio_system = kernel_state()->emulator()->audio_system(); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc index f4c2c156e..f0b264778 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -58,7 +58,7 @@ using xe::apu::XMA_CONTEXT_DATA; // restrictions of frame/subframe/etc: // https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.xaudio2.xaudio2_buffer(v=vs.85).aspx -dword_result_t XMACreateContext(lpdword_t context_out_ptr) { +dword_result_t XMACreateContext_entry(lpdword_t context_out_ptr) { auto xma_decoder = kernel_state()->emulator()->audio_system()->xma_decoder(); uint32_t context_ptr = xma_decoder->AllocateContext(); *context_out_ptr = context_ptr; @@ -70,7 +70,7 @@ dword_result_t XMACreateContext(lpdword_t context_out_ptr) { DECLARE_XBOXKRNL_EXPORT2(XMACreateContext, kAudio, kImplemented, kHighFrequency); -dword_result_t XMAReleaseContext(lpvoid_t context_ptr) { +dword_result_t XMAReleaseContext_entry(lpvoid_t context_ptr) { auto xma_decoder = kernel_state()->emulator()->audio_system()->xma_decoder(); xma_decoder->ReleaseContext(context_ptr); return 0; @@ -117,8 +117,8 @@ struct XMA_CONTEXT_INIT { }; static_assert_size(XMA_CONTEXT_INIT, 56); -dword_result_t XMAInitializeContext(lpvoid_t context_ptr, - pointer_t context_init) { +dword_result_t XMAInitializeContext_entry( + lpvoid_t context_ptr, pointer_t context_init) { // Input buffers may be null (buffer 1 in 415607D4). // Convert to host endianness. uint32_t input_buffer_0_guest_ptr = context_init->input_buffer_0_ptr; @@ -193,8 +193,8 @@ dword_result_t XMAInitializeContext(lpvoid_t context_ptr, DECLARE_XBOXKRNL_EXPORT2(XMAInitializeContext, kAudio, kImplemented, kHighFrequency); -dword_result_t XMASetLoopData(lpvoid_t context_ptr, - pointer_t loop_data) { +dword_result_t XMASetLoopData_entry(lpvoid_t context_ptr, + pointer_t loop_data) { XMA_CONTEXT_DATA context(context_ptr); context.loop_start = loop_data->loop_start; @@ -209,15 +209,15 @@ dword_result_t XMASetLoopData(lpvoid_t context_ptr, } DECLARE_XBOXKRNL_EXPORT2(XMASetLoopData, kAudio, kImplemented, kHighFrequency); -dword_result_t XMAGetInputBufferReadOffset(lpvoid_t context_ptr) { +dword_result_t XMAGetInputBufferReadOffset_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); return context.input_buffer_read_offset; } DECLARE_XBOXKRNL_EXPORT2(XMAGetInputBufferReadOffset, kAudio, kImplemented, kHighFrequency); -dword_result_t XMASetInputBufferReadOffset(lpvoid_t context_ptr, - dword_t value) { +dword_result_t XMASetInputBufferReadOffset_entry(lpvoid_t context_ptr, + dword_t value) { XMA_CONTEXT_DATA context(context_ptr); context.input_buffer_read_offset = value; context.Store(context_ptr); @@ -227,8 +227,8 @@ dword_result_t XMASetInputBufferReadOffset(lpvoid_t context_ptr, DECLARE_XBOXKRNL_EXPORT2(XMASetInputBufferReadOffset, kAudio, kImplemented, kHighFrequency); -dword_result_t XMASetInputBuffer0(lpvoid_t context_ptr, lpvoid_t buffer, - dword_t packet_count) { +dword_result_t XMASetInputBuffer0_entry(lpvoid_t context_ptr, lpvoid_t buffer, + dword_t packet_count) { uint32_t buffer_physical_address = kernel_memory()->GetPhysicalAddress(buffer.guest_address()); assert_true(buffer_physical_address != UINT32_MAX); @@ -251,14 +251,14 @@ dword_result_t XMASetInputBuffer0(lpvoid_t context_ptr, lpvoid_t buffer, DECLARE_XBOXKRNL_EXPORT2(XMASetInputBuffer0, kAudio, kImplemented, kHighFrequency); -dword_result_t XMAIsInputBuffer0Valid(lpvoid_t context_ptr) { +dword_result_t XMAIsInputBuffer0Valid_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); return context.input_buffer_0_valid; } DECLARE_XBOXKRNL_EXPORT2(XMAIsInputBuffer0Valid, kAudio, kImplemented, kHighFrequency); -dword_result_t XMASetInputBuffer0Valid(lpvoid_t context_ptr) { +dword_result_t XMASetInputBuffer0Valid_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); context.input_buffer_0_valid = 1; context.Store(context_ptr); @@ -268,8 +268,8 @@ dword_result_t XMASetInputBuffer0Valid(lpvoid_t context_ptr) { DECLARE_XBOXKRNL_EXPORT2(XMASetInputBuffer0Valid, kAudio, kImplemented, kHighFrequency); -dword_result_t XMASetInputBuffer1(lpvoid_t context_ptr, lpvoid_t buffer, - dword_t packet_count) { +dword_result_t XMASetInputBuffer1_entry(lpvoid_t context_ptr, lpvoid_t buffer, + dword_t packet_count) { uint32_t buffer_physical_address = kernel_memory()->GetPhysicalAddress(buffer.guest_address()); assert_true(buffer_physical_address != UINT32_MAX); @@ -292,14 +292,14 @@ dword_result_t XMASetInputBuffer1(lpvoid_t context_ptr, lpvoid_t buffer, DECLARE_XBOXKRNL_EXPORT2(XMASetInputBuffer1, kAudio, kImplemented, kHighFrequency); -dword_result_t XMAIsInputBuffer1Valid(lpvoid_t context_ptr) { +dword_result_t XMAIsInputBuffer1Valid_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); return context.input_buffer_1_valid; } DECLARE_XBOXKRNL_EXPORT2(XMAIsInputBuffer1Valid, kAudio, kImplemented, kHighFrequency); -dword_result_t XMASetInputBuffer1Valid(lpvoid_t context_ptr) { +dword_result_t XMASetInputBuffer1Valid_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); context.input_buffer_1_valid = 1; context.Store(context_ptr); @@ -309,14 +309,14 @@ dword_result_t XMASetInputBuffer1Valid(lpvoid_t context_ptr) { DECLARE_XBOXKRNL_EXPORT2(XMASetInputBuffer1Valid, kAudio, kImplemented, kHighFrequency); -dword_result_t XMAIsOutputBufferValid(lpvoid_t context_ptr) { +dword_result_t XMAIsOutputBufferValid_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); return context.output_buffer_valid; } DECLARE_XBOXKRNL_EXPORT2(XMAIsOutputBufferValid, kAudio, kImplemented, kHighFrequency); -dword_result_t XMASetOutputBufferValid(lpvoid_t context_ptr) { +dword_result_t XMASetOutputBufferValid_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); context.output_buffer_valid = 1; context.Store(context_ptr); @@ -326,15 +326,15 @@ dword_result_t XMASetOutputBufferValid(lpvoid_t context_ptr) { DECLARE_XBOXKRNL_EXPORT2(XMASetOutputBufferValid, kAudio, kImplemented, kHighFrequency); -dword_result_t XMAGetOutputBufferReadOffset(lpvoid_t context_ptr) { +dword_result_t XMAGetOutputBufferReadOffset_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); return context.output_buffer_read_offset; } DECLARE_XBOXKRNL_EXPORT2(XMAGetOutputBufferReadOffset, kAudio, kImplemented, kHighFrequency); -dword_result_t XMASetOutputBufferReadOffset(lpvoid_t context_ptr, - dword_t value) { +dword_result_t XMASetOutputBufferReadOffset_entry(lpvoid_t context_ptr, + dword_t value) { XMA_CONTEXT_DATA context(context_ptr); context.output_buffer_read_offset = value; context.Store(context_ptr); @@ -344,27 +344,27 @@ dword_result_t XMASetOutputBufferReadOffset(lpvoid_t context_ptr, DECLARE_XBOXKRNL_EXPORT2(XMASetOutputBufferReadOffset, kAudio, kImplemented, kHighFrequency); -dword_result_t XMAGetOutputBufferWriteOffset(lpvoid_t context_ptr) { +dword_result_t XMAGetOutputBufferWriteOffset_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); return context.output_buffer_write_offset; } DECLARE_XBOXKRNL_EXPORT2(XMAGetOutputBufferWriteOffset, kAudio, kImplemented, kHighFrequency); -dword_result_t XMAGetPacketMetadata(lpvoid_t context_ptr) { +dword_result_t XMAGetPacketMetadata_entry(lpvoid_t context_ptr) { XMA_CONTEXT_DATA context(context_ptr); return context.packet_metadata; } DECLARE_XBOXKRNL_EXPORT1(XMAGetPacketMetadata, kAudio, kImplemented); -dword_result_t XMAEnableContext(lpvoid_t context_ptr) { +dword_result_t XMAEnableContext_entry(lpvoid_t context_ptr) { StoreXmaContextIndexedRegister(kernel_state(), 0x1940, context_ptr); return 0; } DECLARE_XBOXKRNL_EXPORT2(XMAEnableContext, kAudio, kImplemented, kHighFrequency); -dword_result_t XMADisableContext(lpvoid_t context_ptr, dword_t wait) { +dword_result_t XMADisableContext_entry(lpvoid_t context_ptr, dword_t wait) { X_HRESULT result = X_E_SUCCESS; StoreXmaContextIndexedRegister(kernel_state(), 0x1A40, context_ptr); if (!kernel_state() @@ -379,7 +379,7 @@ dword_result_t XMADisableContext(lpvoid_t context_ptr, dword_t wait) { DECLARE_XBOXKRNL_EXPORT2(XMADisableContext, kAudio, kImplemented, kHighFrequency); -dword_result_t XMABlockWhileInUse(lpvoid_t context_ptr) { +dword_result_t XMABlockWhileInUse_entry(lpvoid_t context_ptr) { do { XMA_CONTEXT_DATA context(context_ptr); if (!context.input_buffer_0_valid && !context.input_buffer_1_valid) { diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc index 4dbc217a6..64961fb3a 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** -* Copyright 2015 Ben Vanik. All rights reserved. * +* Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -43,8 +43,8 @@ typedef struct { } XECRYPT_RC4_STATE; static_assert_size(XECRYPT_RC4_STATE, 0x102); -void XeCryptRc4Key(pointer_t rc4_ctx, lpvoid_t key, - dword_t key_size) { +void XeCryptRc4Key_entry(pointer_t rc4_ctx, lpvoid_t key, + dword_t key_size) { // Setup RC4 state rc4_ctx->i = rc4_ctx->j = 0; for (uint32_t x = 0; x < 0x100; x++) { @@ -61,8 +61,8 @@ void XeCryptRc4Key(pointer_t rc4_ctx, lpvoid_t key, } DECLARE_XBOXKRNL_EXPORT1(XeCryptRc4Key, kNone, kImplemented); -void XeCryptRc4Ecb(pointer_t rc4_ctx, lpvoid_t data, - dword_t size) { +void XeCryptRc4Ecb_entry(pointer_t rc4_ctx, lpvoid_t data, + dword_t size) { // Crypt data for (uint32_t idx = 0; idx < size; idx++) { rc4_ctx->i = (rc4_ctx->i + 1) % 0x100; @@ -79,10 +79,11 @@ void XeCryptRc4Ecb(pointer_t rc4_ctx, lpvoid_t data, } DECLARE_XBOXKRNL_EXPORT1(XeCryptRc4Ecb, kNone, kImplemented); -void XeCryptRc4(lpvoid_t key, dword_t key_size, lpvoid_t data, dword_t size) { +void XeCryptRc4_entry(lpvoid_t key, dword_t key_size, lpvoid_t data, + dword_t size) { XECRYPT_RC4_STATE rc4_ctx; - XeCryptRc4Key(&rc4_ctx, key, key_size); - XeCryptRc4Ecb(&rc4_ctx, data, size); + XeCryptRc4Key_entry(&rc4_ctx, key, key_size); + XeCryptRc4Ecb_entry(&rc4_ctx, data, size); } DECLARE_XBOXKRNL_EXPORT1(XeCryptRc4, kNone, kImplemented); @@ -107,7 +108,7 @@ void StoreSha1(const sha1::SHA1* sha, XECRYPT_SHA_STATE* state) { std::copy_n(sha->getBlock(), sha->getBlockByteIndex(), state->buffer); } -void XeCryptShaInit(pointer_t sha_state) { +void XeCryptShaInit_entry(pointer_t sha_state) { sha_state.Zero(); sha_state->state[0] = 0x67452301; @@ -118,8 +119,8 @@ void XeCryptShaInit(pointer_t sha_state) { } DECLARE_XBOXKRNL_EXPORT1(XeCryptShaInit, kNone, kImplemented); -void XeCryptShaUpdate(pointer_t sha_state, lpvoid_t input, - dword_t input_size) { +void XeCryptShaUpdate_entry(pointer_t sha_state, + lpvoid_t input, dword_t input_size) { sha1::SHA1 sha; InitSha1(&sha, sha_state); @@ -129,8 +130,8 @@ void XeCryptShaUpdate(pointer_t sha_state, lpvoid_t input, } DECLARE_XBOXKRNL_EXPORT1(XeCryptShaUpdate, kNone, kImplemented); -void XeCryptShaFinal(pointer_t sha_state, - pointer_t out, dword_t out_size) { +void XeCryptShaFinal_entry(pointer_t sha_state, + pointer_t out, dword_t out_size) { sha1::SHA1 sha; InitSha1(&sha, sha_state); @@ -143,9 +144,10 @@ void XeCryptShaFinal(pointer_t sha_state, } DECLARE_XBOXKRNL_EXPORT1(XeCryptShaFinal, kNone, kImplemented); -void XeCryptSha(lpvoid_t input_1, dword_t input_1_size, lpvoid_t input_2, - dword_t input_2_size, lpvoid_t input_3, dword_t input_3_size, - lpvoid_t output, dword_t output_size) { +void XeCryptSha_entry(lpvoid_t input_1, dword_t input_1_size, lpvoid_t input_2, + dword_t input_2_size, lpvoid_t input_3, + dword_t input_3_size, lpvoid_t output, + dword_t output_size) { sha1::SHA1 sha; if (input_1 && input_1_size) { @@ -172,7 +174,7 @@ typedef struct { uint8_t buffer[64]; // 0x24 } XECRYPT_SHA256_STATE; -void XeCryptSha256Init(pointer_t sha_state) { +void XeCryptSha256Init_entry(pointer_t sha_state) { sha_state.Zero(); sha_state->state[0] = 0x6a09e667; @@ -186,8 +188,8 @@ void XeCryptSha256Init(pointer_t sha_state) { } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha256Init, kNone, kImplemented); -void XeCryptSha256Update(pointer_t sha_state, - lpvoid_t input, dword_t input_size) { +void XeCryptSha256Update_entry(pointer_t sha_state, + lpvoid_t input, dword_t input_size) { sha256::SHA256 sha; std::copy(std::begin(sha_state->state), std::end(sha_state->state), sha.getHashValues()); @@ -205,8 +207,8 @@ void XeCryptSha256Update(pointer_t sha_state, } DECLARE_XBOXKRNL_EXPORT1(XeCryptSha256Update, kNone, kImplemented); -void XeCryptSha256Final(pointer_t sha_state, - pointer_t out, dword_t out_size) { +void XeCryptSha256Final_entry(pointer_t sha_state, + pointer_t out, dword_t out_size) { sha256::SHA256 sha; std::copy(std::begin(sha_state->state), std::end(sha_state->state), sha.getHashValues()); @@ -224,8 +226,8 @@ void XeCryptSha256Final(pointer_t sha_state, DECLARE_XBOXKRNL_EXPORT1(XeCryptSha256Final, kNone, kImplemented); // Byteswaps each 8 bytes -void XeCryptBnQw_SwapDwQwLeBe(pointer_t qw_inp, - pointer_t qw_out, dword_t size) { +void XeCryptBnQw_SwapDwQwLeBe_entry(pointer_t qw_inp, + pointer_t qw_out, dword_t size) { xe::copy_and_swap(qw_out, qw_inp, size); } DECLARE_XBOXKRNL_EXPORT1(XeCryptBnQw_SwapDwQwLeBe, kNone, kImplemented); @@ -239,9 +241,9 @@ typedef struct { } XECRYPT_RSA; static_assert_size(XECRYPT_RSA, 0x10); -dword_result_t XeCryptBnQwNeRsaPubCrypt(pointer_t qw_a, - pointer_t qw_b, - pointer_t rsa) { +dword_result_t XeCryptBnQwNeRsaPubCrypt_entry(pointer_t qw_a, + pointer_t qw_b, + pointer_t rsa) { // 0 indicates failure (but not a BOOL return value) #ifndef XE_PLATFORM_WIN32 XELOGE( @@ -342,14 +344,14 @@ DECLARE_XBOXKRNL_EXPORT1(XeCryptBnQwNeRsaPubCrypt, kNone, kImplemented); DECLARE_XBOXKRNL_EXPORT1(XeCryptBnQwNeRsaPubCrypt, kNone, kStub); #endif -dword_result_t XeCryptBnDwLePkcs1Verify(lpvoid_t hash, lpvoid_t sig, - dword_t size) { +dword_result_t XeCryptBnDwLePkcs1Verify_entry(lpvoid_t hash, lpvoid_t sig, + dword_t size) { // BOOL return value return 1; } DECLARE_XBOXKRNL_EXPORT1(XeCryptBnDwLePkcs1Verify, kNone, kStub); -void XeCryptRandom(lpvoid_t buf, dword_t buf_size) { +void XeCryptRandom_entry(lpvoid_t buf, dword_t buf_size) { std::memset(buf, 0xFD, buf_size); } DECLARE_XBOXKRNL_EXPORT1(XeCryptRandom, kNone, kStub); @@ -359,7 +361,7 @@ struct XECRYPT_DES_STATE { }; // Sets bit 0 to make the parity odd -void XeCryptDesParity(lpvoid_t inp, dword_t inp_size, lpvoid_t out_ptr) { +void XeCryptDesParity_entry(lpvoid_t inp, dword_t inp_size, lpvoid_t out_ptr) { DES::set_parity(inp, inp_size, out_ptr); } DECLARE_XBOXKRNL_EXPORT1(XeCryptDesParity, kNone, kImplemented); @@ -368,7 +370,8 @@ struct XECRYPT_DES3_STATE { XECRYPT_DES_STATE des_state[3]; }; -void XeCryptDes3Key(pointer_t state_ptr, lpqword_t key) { +void XeCryptDes3Key_entry(pointer_t state_ptr, + lpqword_t key) { DES3 des3(key[0], key[1], key[2]); DES* des = des3.getDES(); @@ -379,8 +382,8 @@ void XeCryptDes3Key(pointer_t state_ptr, lpqword_t key) { } DECLARE_XBOXKRNL_EXPORT1(XeCryptDes3Key, kNone, kImplemented); -void XeCryptDes3Ecb(pointer_t state_ptr, lpqword_t inp, - lpqword_t out, dword_t encrypt) { +void XeCryptDes3Ecb_entry(pointer_t state_ptr, + lpqword_t inp, lpqword_t out, dword_t encrypt) { DES3 des3((ui64*)state_ptr->des_state[0].keytab, (ui64*)state_ptr->des_state[1].keytab, (ui64*)state_ptr->des_state[2].keytab); @@ -393,9 +396,9 @@ void XeCryptDes3Ecb(pointer_t state_ptr, lpqword_t inp, } DECLARE_XBOXKRNL_EXPORT1(XeCryptDes3Ecb, kNone, kImplemented); -void XeCryptDes3Cbc(pointer_t state_ptr, lpqword_t inp, - dword_t inp_size, lpqword_t out, lpqword_t feed, - dword_t encrypt) { +void XeCryptDes3Cbc_entry(pointer_t state_ptr, + lpqword_t inp, dword_t inp_size, lpqword_t out, + lpqword_t feed, dword_t encrypt) { DES3 des3((ui64*)state_ptr->des_state[0].keytab, (ui64*)state_ptr->des_state[1].keytab, (ui64*)state_ptr->des_state[2].keytab); @@ -429,7 +432,7 @@ static inline uint8_t xeXeCryptAesMul2(uint8_t a) { return (a & 0x80) ? ((a << 1) ^ 0x1B) : (a << 1); } -void XeCryptAesKey(pointer_t state_ptr, lpvoid_t key) { +void XeCryptAesKey_entry(pointer_t state_ptr, lpvoid_t key) { aes_key_schedule_128(key, reinterpret_cast(state_ptr->keytabenc)); // Decryption key schedule not needed by openluopworld/aes_128, but generated // to fill the context structure properly. @@ -494,8 +497,8 @@ void XeCryptAesKey(pointer_t state_ptr, lpvoid_t key) { } DECLARE_XBOXKRNL_EXPORT1(XeCryptAesKey, kNone, kImplemented); -void XeCryptAesEcb(pointer_t state_ptr, lpvoid_t inp_ptr, - lpvoid_t out_ptr, dword_t encrypt) { +void XeCryptAesEcb_entry(pointer_t state_ptr, + lpvoid_t inp_ptr, lpvoid_t out_ptr, dword_t encrypt) { const uint8_t* keytab = reinterpret_cast(state_ptr->keytabenc); if (encrypt) { @@ -506,9 +509,9 @@ void XeCryptAesEcb(pointer_t state_ptr, lpvoid_t inp_ptr, } DECLARE_XBOXKRNL_EXPORT1(XeCryptAesEcb, kNone, kImplemented); -void XeCryptAesCbc(pointer_t state_ptr, lpvoid_t inp_ptr, - dword_t inp_size, lpvoid_t out_ptr, lpvoid_t feed_ptr, - dword_t encrypt) { +void XeCryptAesCbc_entry(pointer_t state_ptr, + lpvoid_t inp_ptr, dword_t inp_size, lpvoid_t out_ptr, + lpvoid_t feed_ptr, dword_t encrypt) { const uint8_t* keytab = reinterpret_cast(state_ptr->keytabenc); const uint8_t* inp = inp_ptr.as(); @@ -541,10 +544,10 @@ void XeCryptAesCbc(pointer_t state_ptr, lpvoid_t inp_ptr, } DECLARE_XBOXKRNL_EXPORT1(XeCryptAesCbc, kNone, kImplemented); -void XeCryptHmacSha(lpvoid_t key, dword_t key_size_in, lpvoid_t inp_1, - dword_t inp_1_size, lpvoid_t inp_2, dword_t inp_2_size, - lpvoid_t inp_3, dword_t inp_3_size, lpvoid_t out, - dword_t out_size) { +void XeCryptHmacSha_entry(lpvoid_t key, dword_t key_size_in, lpvoid_t inp_1, + dword_t inp_1_size, lpvoid_t inp_2, + dword_t inp_2_size, lpvoid_t inp_3, + dword_t inp_3_size, lpvoid_t out, dword_t out_size) { uint32_t key_size = key_size_in; sha1::SHA1 sha; uint8_t kpad_i[0x40]; @@ -605,19 +608,19 @@ DECLARE_XBOXKRNL_EXPORT1(XeCryptHmacSha, kNone, kImplemented); static const uint8_t key19[] = {0xE1, 0xBC, 0x15, 0x9C, 0x73, 0xB1, 0xEA, 0xE9, 0xAB, 0x31, 0x70, 0xF3, 0xAD, 0x47, 0xEB, 0xF3}; -dword_result_t XeKeysHmacSha(dword_t key_num, lpvoid_t inp_1, - dword_t inp_1_size, lpvoid_t inp_2, - dword_t inp_2_size, lpvoid_t inp_3, - dword_t inp_3_size, lpvoid_t out, - dword_t out_size) { +dword_result_t XeKeysHmacSha_entry(dword_t key_num, lpvoid_t inp_1, + dword_t inp_1_size, lpvoid_t inp_2, + dword_t inp_2_size, lpvoid_t inp_3, + dword_t inp_3_size, lpvoid_t out, + dword_t out_size) { const uint8_t* key = nullptr; if (key_num == 0x19) { key = key19; } if (key) { - XeCryptHmacSha((void*)key, 0x10, inp_1, inp_1_size, inp_2, inp_2_size, - inp_3, inp_3_size, out, out_size); + XeCryptHmacSha_entry((void*)key, 0x10, inp_1, inp_1_size, inp_2, inp_2_size, + inp_3, inp_3_size, out, out_size); return X_STATUS_SUCCESS; } @@ -630,41 +633,42 @@ static const uint8_t xe_key_obfuscation_key[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -dword_result_t XeKeysAesCbcUsingKey(lpvoid_t obscured_key, lpvoid_t inp_ptr, - dword_t inp_size, lpvoid_t out_ptr, - lpvoid_t feed_ptr, dword_t encrypt) { +dword_result_t XeKeysAesCbcUsingKey_entry(lpvoid_t obscured_key, + lpvoid_t inp_ptr, dword_t inp_size, + lpvoid_t out_ptr, lpvoid_t feed_ptr, + dword_t encrypt) { uint8_t key[16]; // Deobscure key XECRYPT_AES_STATE aes; - XeCryptAesKey(&aes, (uint8_t*)xe_key_obfuscation_key); - XeCryptAesEcb(&aes, obscured_key, key, 0); + XeCryptAesKey_entry(&aes, (uint8_t*)xe_key_obfuscation_key); + XeCryptAesEcb_entry(&aes, obscured_key, key, 0); // Run CBC using deobscured key - XeCryptAesKey(&aes, key); - XeCryptAesCbc(&aes, inp_ptr, inp_size, out_ptr, feed_ptr, encrypt); + XeCryptAesKey_entry(&aes, key); + XeCryptAesCbc_entry(&aes, inp_ptr, inp_size, out_ptr, feed_ptr, encrypt); return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XeKeysAesCbcUsingKey, kNone, kImplemented); -dword_result_t XeKeysObscureKey(lpvoid_t input, lpvoid_t output) { +dword_result_t XeKeysObscureKey_entry(lpvoid_t input, lpvoid_t output) { // Based on HvxKeysObscureKey // Seems to encrypt input with per-console KEY_OBFUSCATION_KEY (key 0x18) XECRYPT_AES_STATE aes; - XeCryptAesKey(&aes, (uint8_t*)xe_key_obfuscation_key); - XeCryptAesEcb(&aes, input, output, 1); + XeCryptAesKey_entry(&aes, (uint8_t*)xe_key_obfuscation_key); + XeCryptAesEcb_entry(&aes, input, output, 1); return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XeKeysObscureKey, kNone, kImplemented); -dword_result_t XeKeysHmacShaUsingKey(lpvoid_t obscured_key, lpvoid_t inp_1, - dword_t inp_1_size, lpvoid_t inp_2, - dword_t inp_2_size, lpvoid_t inp_3, - dword_t inp_3_size, lpvoid_t out, - dword_t out_size) { +dword_result_t XeKeysHmacShaUsingKey_entry(lpvoid_t obscured_key, + lpvoid_t inp_1, dword_t inp_1_size, + lpvoid_t inp_2, dword_t inp_2_size, + lpvoid_t inp_3, dword_t inp_3_size, + lpvoid_t out, dword_t out_size) { if (!obscured_key) { return X_STATUS_INVALID_PARAMETER; } @@ -673,11 +677,11 @@ dword_result_t XeKeysHmacShaUsingKey(lpvoid_t obscured_key, lpvoid_t inp_1, // Deobscure key XECRYPT_AES_STATE aes; - XeCryptAesKey(&aes, (uint8_t*)xe_key_obfuscation_key); - XeCryptAesEcb(&aes, obscured_key, key, 0); + XeCryptAesKey_entry(&aes, (uint8_t*)xe_key_obfuscation_key); + XeCryptAesEcb_entry(&aes, obscured_key, key, 0); - XeCryptHmacSha(key, 0x10, inp_1, inp_1_size, inp_2, inp_2_size, inp_3, - inp_3_size, out, out_size); + XeCryptHmacSha_entry(key, 0x10, inp_1, inp_1_size, inp_2, inp_2_size, inp_3, + inp_3_size, out, out_size); return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(XeKeysHmacShaUsingKey, kNone, kImplemented); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc index 5dd7b3309..0eeb8f8f7 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -19,7 +19,7 @@ namespace xe { namespace kernel { namespace xboxkrnl { -void DbgBreakPoint() { xe::debugging::Break(); } +void DbgBreakPoint_entry() { xe::debugging::Break(); } DECLARE_XBOXKRNL_EXPORT2(DbgBreakPoint, kDebug, kStub, kImportant); // https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx @@ -123,7 +123,7 @@ void HandleCppException(pointer_t record) { xe::debugging::Break(); } -void RtlRaiseException(pointer_t record) { +void RtlRaiseException_entry(pointer_t record) { switch (record->exception_code) { case 0x406D1388: { HandleSetThreadName(record); @@ -141,8 +141,8 @@ void RtlRaiseException(pointer_t record) { } DECLARE_XBOXKRNL_EXPORT2(RtlRaiseException, kDebug, kStub, kImportant); -void KeBugCheckEx(dword_t code, dword_t param1, dword_t param2, dword_t param3, - dword_t param4) { +void KeBugCheckEx_entry(dword_t code, dword_t param1, dword_t param2, + dword_t param3, dword_t param4) { XELOGD("*** STOP: 0x{:08X} (0x{:08X}, 0x{:08X}, 0x{:08X}, 0x{:08X})", code, param1, param2, param3, param4); fflush(stdout); @@ -151,7 +151,7 @@ void KeBugCheckEx(dword_t code, dword_t param1, dword_t param2, dword_t param3, } DECLARE_XBOXKRNL_EXPORT2(KeBugCheckEx, kDebug, kStub, kImportant); -void KeBugCheck(dword_t code) { KeBugCheckEx(code, 0, 0, 0, 0); } +void KeBugCheck_entry(dword_t code) { KeBugCheckEx_entry(code, 0, 0, 0, 0); } DECLARE_XBOXKRNL_EXPORT2(KeBugCheck, kDebug, kImplemented, kImportant); void RegisterDebugExports(xe::cpu::ExportResolver* export_resolver, diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc index 3a27d490b..f1f9938d1 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -1011,7 +1011,7 @@ uint32_t xeRtlNtStatusToDosError(uint32_t source_status) { return 317; // ERROR_MR_MID_NOT_FOUND } -dword_result_t RtlNtStatusToDosError(dword_t source_status) { +dword_result_t RtlNtStatusToDosError_entry(dword_t source_status) { return xeRtlNtStatusToDosError(source_status); } DECLARE_XBOXKRNL_EXPORT3(RtlNtStatusToDosError, kNone, kImportant, diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc index 658cc0d24..8cf3a1cbb 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,7 +17,7 @@ namespace xe { namespace kernel { namespace xboxkrnl { -void HalReturnToFirmware(dword_t routine) { +void HalReturnToFirmware_entry(dword_t routine) { // void // IN FIRMWARE_REENTRY Routine diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc index 0ce308237..0234ee062 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,7 +17,7 @@ namespace xe { namespace kernel { namespace xboxkrnl { -dword_result_t HidReadKeys(dword_t unk1, unknown_t unk2, unknown_t unk3) { +dword_result_t HidReadKeys_entry(dword_t unk1, unknown_t unk2, unknown_t unk3) { /* TODO(gibbed): * Games check for the following errors: * 0xC000009D - translated to 0x48F - ERROR_DEVICE_NOT_CONNECTED diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index 37dc42aec..010dcf22b 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -95,13 +95,13 @@ static bool IsValidPath(const std::string_view s, bool is_pattern) { return true; } -dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access, - pointer_t object_attrs, - pointer_t io_status_block, - lpqword_t allocation_size_ptr, - dword_t file_attributes, dword_t share_access, - dword_t creation_disposition, - dword_t create_options) { +dword_result_t NtCreateFile_entry(lpdword_t handle_out, dword_t desired_access, + pointer_t object_attrs, + pointer_t io_status_block, + lpqword_t allocation_size_ptr, + dword_t file_attributes, dword_t share_access, + dword_t creation_disposition, + dword_t create_options) { uint64_t allocation_size = 0; // is this correct??? if (allocation_size_ptr) { allocation_size = *allocation_size_ptr; @@ -170,22 +170,22 @@ dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access, } DECLARE_XBOXKRNL_EXPORT1(NtCreateFile, kFileSystem, kImplemented); -dword_result_t NtOpenFile(lpdword_t handle_out, dword_t desired_access, - pointer_t object_attributes, - pointer_t io_status_block, - dword_t open_options) { - return NtCreateFile(handle_out, desired_access, object_attributes, - io_status_block, nullptr, 0, 0, - static_cast(xe::vfs::FileDisposition::kOpen), - open_options); +dword_result_t NtOpenFile_entry( + lpdword_t handle_out, dword_t desired_access, + pointer_t object_attributes, + pointer_t io_status_block, dword_t open_options) { + return NtCreateFile_entry( + handle_out, desired_access, object_attributes, io_status_block, nullptr, + 0, 0, static_cast(xe::vfs::FileDisposition::kOpen), + open_options); } DECLARE_XBOXKRNL_EXPORT1(NtOpenFile, kFileSystem, kImplemented); -dword_result_t NtReadFile(dword_t file_handle, dword_t event_handle, - lpvoid_t apc_routine_ptr, lpvoid_t apc_context, - pointer_t io_status_block, - lpvoid_t buffer, dword_t buffer_length, - lpqword_t byte_offset_ptr) { +dword_result_t NtReadFile_entry(dword_t file_handle, dword_t event_handle, + lpvoid_t apc_routine_ptr, lpvoid_t apc_context, + pointer_t io_status_block, + lpvoid_t buffer, dword_t buffer_length, + lpqword_t byte_offset_ptr) { X_STATUS result = X_STATUS_SUCCESS; bool signal_event = false; @@ -265,11 +265,10 @@ dword_result_t NtReadFile(dword_t file_handle, dword_t event_handle, } DECLARE_XBOXKRNL_EXPORT2(NtReadFile, kFileSystem, kImplemented, kHighFrequency); -dword_result_t NtReadFileScatter(dword_t file_handle, dword_t event_handle, - lpvoid_t apc_routine_ptr, lpvoid_t apc_context, - pointer_t io_status_block, - lpdword_t segment_array, dword_t length, - lpqword_t byte_offset_ptr) { +dword_result_t NtReadFileScatter_entry( + dword_t file_handle, dword_t event_handle, lpvoid_t apc_routine_ptr, + lpvoid_t apc_context, pointer_t io_status_block, + lpdword_t segment_array, dword_t length, lpqword_t byte_offset_ptr) { X_STATUS result = X_STATUS_SUCCESS; bool signal_event = false; @@ -352,11 +351,11 @@ dword_result_t NtReadFileScatter(dword_t file_handle, dword_t event_handle, } DECLARE_XBOXKRNL_EXPORT1(NtReadFileScatter, kFileSystem, kImplemented); -dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle, - function_t apc_routine, lpvoid_t apc_context, - pointer_t io_status_block, - lpvoid_t buffer, dword_t buffer_length, - lpqword_t byte_offset_ptr) { +dword_result_t NtWriteFile_entry(dword_t file_handle, dword_t event_handle, + function_t apc_routine, lpvoid_t apc_context, + pointer_t io_status_block, + lpvoid_t buffer, dword_t buffer_length, + lpqword_t byte_offset_ptr) { X_STATUS result = X_STATUS_SUCCESS; // Grab event to signal. @@ -430,10 +429,10 @@ dword_result_t NtWriteFile(dword_t file_handle, dword_t event_handle, } DECLARE_XBOXKRNL_EXPORT1(NtWriteFile, kFileSystem, kImplemented); -dword_result_t NtCreateIoCompletion(lpdword_t out_handle, - dword_t desired_access, - lpvoid_t object_attribs, - dword_t num_concurrent_threads) { +dword_result_t NtCreateIoCompletion_entry(lpdword_t out_handle, + dword_t desired_access, + lpvoid_t object_attribs, + dword_t num_concurrent_threads) { auto completion = new XIOCompletion(kernel_state()); if (out_handle) { *out_handle = completion->handle(); @@ -443,9 +442,10 @@ dword_result_t NtCreateIoCompletion(lpdword_t out_handle, } DECLARE_XBOXKRNL_EXPORT1(NtCreateIoCompletion, kFileSystem, kImplemented); -dword_result_t NtSetIoCompletion(dword_t handle, dword_t key_context, - dword_t apc_context, dword_t completion_status, - dword_t num_bytes) { +dword_result_t NtSetIoCompletion_entry(dword_t handle, dword_t key_context, + dword_t apc_context, + dword_t completion_status, + dword_t num_bytes) { auto port = kernel_state()->object_table()->LookupObject(handle); if (!port) { @@ -465,7 +465,7 @@ DECLARE_XBOXKRNL_EXPORT2(NtSetIoCompletion, kFileSystem, kImplemented, kHighFrequency); // Dequeues a packet from the completion port. -dword_result_t NtRemoveIoCompletion( +dword_result_t NtRemoveIoCompletion_entry( dword_t handle, lpdword_t key_context, lpdword_t apc_context, pointer_t io_status_block, lpqword_t timeout) { X_STATUS status = X_STATUS_SUCCESS; @@ -502,7 +502,7 @@ dword_result_t NtRemoveIoCompletion( DECLARE_XBOXKRNL_EXPORT2(NtRemoveIoCompletion, kFileSystem, kImplemented, kHighFrequency); -dword_result_t NtQueryFullAttributesFile( +dword_result_t NtQueryFullAttributesFile_entry( pointer_t obj_attribs, pointer_t file_info) { auto object_name = @@ -544,7 +544,7 @@ dword_result_t NtQueryFullAttributesFile( } DECLARE_XBOXKRNL_EXPORT1(NtQueryFullAttributesFile, kFileSystem, kImplemented); -dword_result_t NtQueryDirectoryFile( +dword_result_t NtQueryDirectoryFile_entry( dword_t file_handle, dword_t event_handle, function_t apc_routine, lpvoid_t apc_context, pointer_t io_status_block, pointer_t file_info_ptr, dword_t length, @@ -588,7 +588,7 @@ dword_result_t NtQueryDirectoryFile( } DECLARE_XBOXKRNL_EXPORT1(NtQueryDirectoryFile, kFileSystem, kImplemented); -dword_result_t NtFlushBuffersFile( +dword_result_t NtFlushBuffersFile_entry( dword_t file_handle, pointer_t io_status_block_ptr) { auto result = X_STATUS_SUCCESS; @@ -602,7 +602,7 @@ dword_result_t NtFlushBuffersFile( DECLARE_XBOXKRNL_EXPORT1(NtFlushBuffersFile, kFileSystem, kStub); // https://docs.microsoft.com/en-us/windows/win32/devnotes/ntopensymboliclinkobject -dword_result_t NtOpenSymbolicLinkObject( +dword_result_t NtOpenSymbolicLinkObject_entry( lpdword_t handle_out, pointer_t object_attrs) { if (!object_attrs) { return X_STATUS_INVALID_PARAMETER; @@ -646,8 +646,8 @@ dword_result_t NtOpenSymbolicLinkObject( DECLARE_XBOXKRNL_EXPORT1(NtOpenSymbolicLinkObject, kFileSystem, kImplemented); // https://docs.microsoft.com/en-us/windows/win32/devnotes/ntquerysymboliclinkobject -dword_result_t NtQuerySymbolicLinkObject(dword_t handle, - pointer_t target) { +dword_result_t NtQuerySymbolicLinkObject_entry( + dword_t handle, pointer_t target) { auto symlink = kernel_state()->object_table()->LookupObject(handle); if (!symlink) { @@ -664,17 +664,17 @@ dword_result_t NtQuerySymbolicLinkObject(dword_t handle, } DECLARE_XBOXKRNL_EXPORT1(NtQuerySymbolicLinkObject, kFileSystem, kImplemented); -dword_result_t FscGetCacheElementCount(dword_t r3) { return 0; } +dword_result_t FscGetCacheElementCount_entry(dword_t r3) { return 0; } DECLARE_XBOXKRNL_EXPORT1(FscGetCacheElementCount, kFileSystem, kStub); -dword_result_t FscSetCacheElementCount(dword_t unk_0, dword_t unk_1) { +dword_result_t FscSetCacheElementCount_entry(dword_t unk_0, dword_t unk_1) { // unk_0 = 0 // unk_1 looks like a count? in what units? 256 is a common value return X_STATUS_SUCCESS; } DECLARE_XBOXKRNL_EXPORT1(FscSetCacheElementCount, kFileSystem, kStub); -dword_result_t NtDeviceIoControlFile( +dword_result_t NtDeviceIoControlFile_entry( dword_t handle, dword_t event_handle, dword_t apc_routine, dword_t apc_context, dword_t io_status_block, dword_t io_control_code, lpvoid_t input_buffer, dword_t input_buffer_len, lpvoid_t output_buffer, @@ -712,8 +712,9 @@ dword_result_t NtDeviceIoControlFile( } DECLARE_XBOXKRNL_EXPORT1(NtDeviceIoControlFile, kFileSystem, kStub); -dword_result_t IoCreateDevice(dword_t device_struct, dword_t r4, dword_t r5, - dword_t r6, dword_t r7, lpdword_t out_struct) { +dword_result_t IoCreateDevice_entry(dword_t device_struct, dword_t r4, + dword_t r5, dword_t r6, dword_t r7, + lpdword_t out_struct) { // Called from XMountUtilityDrive XAM-task code // That code tries writing things to a pointer at out_struct+0x18 // We'll alloc some scratch space for it so it doesn't cause any exceptions diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc index e43db31e6..2b9c9832b 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -54,7 +54,7 @@ uint32_t GetQueryFileInfoMinimumLength(uint32_t info_class) { } } -dword_result_t NtQueryInformationFile( +dword_result_t NtQueryInformationFile_entry( dword_t file_handle, pointer_t io_status_block_ptr, lpvoid_t info_ptr, dword_t info_length, dword_t info_class) { uint32_t minimum_length = GetQueryFileInfoMinimumLength(info_class); @@ -179,7 +179,7 @@ uint32_t GetSetFileInfoMinimumLength(uint32_t info_class) { } } -dword_result_t NtSetInformationFile( +dword_result_t NtSetInformationFile_entry( dword_t file_handle, pointer_t io_status_block, lpvoid_t info_ptr, dword_t info_length, dword_t info_class) { uint32_t minimum_length = GetSetFileInfoMinimumLength(info_class); @@ -277,7 +277,7 @@ uint32_t GetQueryVolumeInfoMinimumLength(uint32_t info_class) { } } -dword_result_t NtQueryVolumeInformationFile( +dword_result_t NtQueryVolumeInformationFile_entry( dword_t file_handle, pointer_t io_status_block_ptr, lpvoid_t info_ptr, dword_t info_length, dword_t info_class) { uint32_t minimum_length = GetQueryVolumeInfoMinimumLength(info_class); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc index d53539286..97c2875d7 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -57,10 +57,11 @@ uint32_t FromXdkProtectFlags(uint32_t protect) { return result; } -dword_result_t NtAllocateVirtualMemory(lpdword_t base_addr_ptr, - lpdword_t region_size_ptr, - dword_t alloc_type, dword_t protect_bits, - dword_t debug_memory) { +dword_result_t NtAllocateVirtualMemory_entry(lpdword_t base_addr_ptr, + lpdword_t region_size_ptr, + dword_t alloc_type, + dword_t protect_bits, + dword_t debug_memory) { // NTSTATUS // _Inout_ PVOID *BaseAddress, // _Inout_ PSIZE_T RegionSize, @@ -189,11 +190,11 @@ dword_result_t NtAllocateVirtualMemory(lpdword_t base_addr_ptr, } DECLARE_XBOXKRNL_EXPORT1(NtAllocateVirtualMemory, kMemory, kImplemented); -dword_result_t NtProtectVirtualMemory(lpdword_t base_addr_ptr, - lpdword_t region_size_ptr, - dword_t protect_bits, - lpdword_t old_protect, - dword_t debug_memory) { +dword_result_t NtProtectVirtualMemory_entry(lpdword_t base_addr_ptr, + lpdword_t region_size_ptr, + dword_t protect_bits, + lpdword_t old_protect, + dword_t debug_memory) { // Set to TRUE when this memory refers to devkit memory area. assert_true(debug_memory == 0); @@ -239,9 +240,10 @@ dword_result_t NtProtectVirtualMemory(lpdword_t base_addr_ptr, } DECLARE_XBOXKRNL_EXPORT1(NtProtectVirtualMemory, kMemory, kImplemented); -dword_result_t NtFreeVirtualMemory(lpdword_t base_addr_ptr, - lpdword_t region_size_ptr, dword_t free_type, - dword_t debug_memory) { +dword_result_t NtFreeVirtualMemory_entry(lpdword_t base_addr_ptr, + lpdword_t region_size_ptr, + dword_t free_type, + dword_t debug_memory) { uint32_t base_addr_value = *base_addr_ptr; uint32_t region_size_value = *region_size_ptr; // X_MEM_DECOMMIT | X_MEM_RELEASE @@ -293,7 +295,7 @@ struct X_MEMORY_BASIC_INFORMATION { be type; }; -dword_result_t NtQueryVirtualMemory( +dword_result_t NtQueryVirtualMemory_entry( dword_t base_address, pointer_t memory_basic_information_ptr) { auto heap = kernel_state()->memory()->LookupHeap(base_address); @@ -328,11 +330,9 @@ dword_result_t NtQueryVirtualMemory( } DECLARE_XBOXKRNL_EXPORT1(NtQueryVirtualMemory, kMemory, kImplemented); -dword_result_t MmAllocatePhysicalMemoryEx(dword_t flags, dword_t region_size, - dword_t protect_bits, - dword_t min_addr_range, - dword_t max_addr_range, - dword_t alignment) { +dword_result_t MmAllocatePhysicalMemoryEx_entry( + dword_t flags, dword_t region_size, dword_t protect_bits, + dword_t min_addr_range, dword_t max_addr_range, dword_t alignment) { // Type will usually be 0 (user request?), where 1 and 2 are sometimes made // by D3D/etc. @@ -392,14 +392,15 @@ dword_result_t MmAllocatePhysicalMemoryEx(dword_t flags, dword_t region_size, } DECLARE_XBOXKRNL_EXPORT1(MmAllocatePhysicalMemoryEx, kMemory, kImplemented); -dword_result_t MmAllocatePhysicalMemory(dword_t flags, dword_t region_size, - dword_t protect_bits) { - return MmAllocatePhysicalMemoryEx(flags, region_size, protect_bits, 0, - 0xFFFFFFFFu, 0); +dword_result_t MmAllocatePhysicalMemory_entry(dword_t flags, + dword_t region_size, + dword_t protect_bits) { + return MmAllocatePhysicalMemoryEx_entry(flags, region_size, protect_bits, 0, + 0xFFFFFFFFu, 0); } DECLARE_XBOXKRNL_EXPORT1(MmAllocatePhysicalMemory, kMemory, kImplemented); -void MmFreePhysicalMemory(dword_t type, dword_t base_address) { +void MmFreePhysicalMemory_entry(dword_t type, dword_t base_address) { // base_address = result of MmAllocatePhysicalMemory. assert_true((base_address & 0x1F) == 0); @@ -409,7 +410,7 @@ void MmFreePhysicalMemory(dword_t type, dword_t base_address) { } DECLARE_XBOXKRNL_EXPORT1(MmFreePhysicalMemory, kMemory, kImplemented); -dword_result_t MmQueryAddressProtect(dword_t base_address) { +dword_result_t MmQueryAddressProtect_entry(dword_t base_address) { auto heap = kernel_state()->memory()->LookupHeap(base_address); uint32_t access; if (!heap->QueryProtect(base_address, &access)) { @@ -422,8 +423,8 @@ dword_result_t MmQueryAddressProtect(dword_t base_address) { DECLARE_XBOXKRNL_EXPORT2(MmQueryAddressProtect, kMemory, kImplemented, kHighFrequency); -void MmSetAddressProtect(lpvoid_t base_address, dword_t region_size, - dword_t protect_bits) { +void MmSetAddressProtect_entry(lpvoid_t base_address, dword_t region_size, + dword_t protect_bits) { if (!protect_bits) { XELOGE("MmSetAddressProtect: Failed due to incorrect protect_bits"); return; @@ -435,7 +436,7 @@ void MmSetAddressProtect(lpvoid_t base_address, dword_t region_size, } DECLARE_XBOXKRNL_EXPORT1(MmSetAddressProtect, kMemory, kImplemented); -dword_result_t MmQueryAllocationSize(lpvoid_t base_address) { +dword_result_t MmQueryAllocationSize_entry(lpvoid_t base_address) { auto heap = kernel_state()->memory()->LookupHeap(base_address); uint32_t size; if (!heap->QuerySize(base_address, &size)) { @@ -471,7 +472,7 @@ struct X_MM_QUERY_STATISTICS_RESULT { }; static_assert_size(X_MM_QUERY_STATISTICS_RESULT, 104); -dword_result_t MmQueryStatistics( +dword_result_t MmQueryStatistics_entry( pointer_t stats_ptr) { if (!stats_ptr) { return X_STATUS_INVALID_PARAMETER; @@ -550,7 +551,7 @@ dword_result_t MmQueryStatistics( DECLARE_XBOXKRNL_EXPORT1(MmQueryStatistics, kMemory, kImplemented); // https://msdn.microsoft.com/en-us/library/windows/hardware/ff554547(v=vs.85).aspx -dword_result_t MmGetPhysicalAddress(dword_t base_address) { +dword_result_t MmGetPhysicalAddress_entry(dword_t base_address) { // PHYSICAL_ADDRESS MmGetPhysicalAddress( // _In_ PVOID BaseAddress // ); @@ -564,8 +565,8 @@ dword_result_t MmGetPhysicalAddress(dword_t base_address) { } DECLARE_XBOXKRNL_EXPORT1(MmGetPhysicalAddress, kMemory, kImplemented); -dword_result_t MmMapIoSpace(dword_t unk0, lpvoid_t src_address, dword_t size, - dword_t flags) { +dword_result_t MmMapIoSpace_entry(dword_t unk0, lpvoid_t src_address, + dword_t size, dword_t flags) { // I've only seen this used to map XMA audio contexts. // The code seems fine with taking the src address, so this just returns that. // If others start using it there could be problems. @@ -577,8 +578,8 @@ dword_result_t MmMapIoSpace(dword_t unk0, lpvoid_t src_address, dword_t size, } DECLARE_XBOXKRNL_EXPORT1(MmMapIoSpace, kMemory, kImplemented); -dword_result_t ExAllocatePoolTypeWithTag(dword_t size, dword_t tag, - dword_t zero) { +dword_result_t ExAllocatePoolTypeWithTag_entry(dword_t size, dword_t tag, + dword_t zero) { uint32_t alignment = 8; uint32_t adjusted_size = size; if (adjusted_size < 4 * 1024) { @@ -594,33 +595,33 @@ dword_result_t ExAllocatePoolTypeWithTag(dword_t size, dword_t tag, } DECLARE_XBOXKRNL_EXPORT1(ExAllocatePoolTypeWithTag, kMemory, kImplemented); -dword_result_t ExAllocatePool(dword_t size) { +dword_result_t ExAllocatePool_entry(dword_t size) { const uint32_t none = 0x656E6F4E; // 'None' - return ExAllocatePoolTypeWithTag(size, none, 0); + return ExAllocatePoolTypeWithTag_entry(size, none, 0); } DECLARE_XBOXKRNL_EXPORT1(ExAllocatePool, kMemory, kImplemented); -void ExFreePool(lpvoid_t base_address) { +void ExFreePool_entry(lpvoid_t base_address) { kernel_state()->memory()->SystemHeapFree(base_address); } DECLARE_XBOXKRNL_EXPORT1(ExFreePool, kMemory, kImplemented); -dword_result_t KeGetImagePageTableEntry(lpvoid_t address) { +dword_result_t KeGetImagePageTableEntry_entry(lpvoid_t address) { // Unknown return 1; } DECLARE_XBOXKRNL_EXPORT1(KeGetImagePageTableEntry, kMemory, kStub); -dword_result_t KeLockL2() { +dword_result_t KeLockL2_entry() { // TODO return 0; } DECLARE_XBOXKRNL_EXPORT1(KeLockL2, kMemory, kStub); -void KeUnlockL2() {} +void KeUnlockL2_entry() {} DECLARE_XBOXKRNL_EXPORT1(KeUnlockL2, kMemory, kStub); -dword_result_t MmCreateKernelStack(dword_t stack_size, dword_t r4) { +dword_result_t MmCreateKernelStack_entry(dword_t stack_size, dword_t r4) { assert_zero(r4); // Unknown argument. auto stack_size_aligned = (stack_size + 0xFFF) & 0xFFFFF000; @@ -637,7 +638,8 @@ dword_result_t MmCreateKernelStack(dword_t stack_size, dword_t r4) { } DECLARE_XBOXKRNL_EXPORT1(MmCreateKernelStack, kMemory, kImplemented); -dword_result_t MmDeleteKernelStack(lpvoid_t stack_base, lpvoid_t stack_end) { +dword_result_t MmDeleteKernelStack_entry(lpvoid_t stack_base, + lpvoid_t stack_end) { // Release the stack (where stack_end is the low address) if (kernel_memory()->LookupHeap(0x70000000)->Release(stack_end)) { return X_STATUS_SUCCESS; diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc index 834b5102f..9f80c9d6d 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -18,7 +18,7 @@ namespace xe { namespace kernel { namespace xboxkrnl { -void KeEnableFpuExceptions(dword_t enabled) { +void KeEnableFpuExceptions_entry(dword_t enabled) { // TODO(benvanik): can we do anything about exceptions? } DECLARE_XBOXKRNL_EXPORT1(KeEnableFpuExceptions, kNone, kStub); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc index 6d789079d..02380283d 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -19,7 +19,7 @@ namespace xe { namespace kernel { namespace xboxkrnl { -dword_result_t XexCheckExecutablePrivilege(dword_t privilege) { +dword_result_t XexCheckExecutablePrivilege_entry(dword_t privilege) { // BOOL // DWORD Privilege @@ -39,8 +39,8 @@ dword_result_t XexCheckExecutablePrivilege(dword_t privilege) { } DECLARE_XBOXKRNL_EXPORT1(XexCheckExecutablePrivilege, kModules, kImplemented); -dword_result_t XexGetModuleHandle(lpstring_t module_name, - lpdword_t hmodule_ptr) { +dword_result_t XexGetModuleHandle_entry(lpstring_t module_name, + lpdword_t hmodule_ptr) { object_ref module; if (!module_name) { @@ -61,8 +61,9 @@ dword_result_t XexGetModuleHandle(lpstring_t module_name, } DECLARE_XBOXKRNL_EXPORT1(XexGetModuleHandle, kModules, kImplemented); -dword_result_t XexGetModuleSection(lpvoid_t hmodule, lpstring_t name, - lpdword_t data_ptr, lpdword_t size_ptr) { +dword_result_t XexGetModuleSection_entry(lpvoid_t hmodule, lpstring_t name, + lpdword_t data_ptr, + lpdword_t size_ptr) { X_STATUS result = X_STATUS_SUCCESS; auto module = XModule::GetFromHModule(kernel_state(), hmodule); @@ -82,8 +83,8 @@ dword_result_t XexGetModuleSection(lpvoid_t hmodule, lpstring_t name, } DECLARE_XBOXKRNL_EXPORT1(XexGetModuleSection, kModules, kImplemented); -dword_result_t XexLoadImage(lpstring_t module_name, dword_t module_flags, - dword_t min_version, lpdword_t hmodule_ptr) { +dword_result_t XexLoadImage_entry(lpstring_t module_name, dword_t module_flags, + dword_t min_version, lpdword_t hmodule_ptr) { X_STATUS result = X_STATUS_NO_SUCH_FILE; uint32_t hmodule = 0; @@ -117,7 +118,7 @@ dword_result_t XexLoadImage(lpstring_t module_name, dword_t module_flags, } DECLARE_XBOXKRNL_EXPORT1(XexLoadImage, kModules, kImplemented); -dword_result_t XexUnloadImage(lpvoid_t hmodule) { +dword_result_t XexUnloadImage_entry(lpvoid_t hmodule) { auto module = XModule::GetFromHModule(kernel_state(), hmodule); if (!module) { return X_STATUS_INVALID_HANDLE; @@ -138,8 +139,8 @@ dword_result_t XexUnloadImage(lpvoid_t hmodule) { } DECLARE_XBOXKRNL_EXPORT1(XexUnloadImage, kModules, kImplemented); -dword_result_t XexGetProcedureAddress(lpvoid_t hmodule, dword_t ordinal, - lpdword_t out_function_ptr) { +dword_result_t XexGetProcedureAddress_entry(lpvoid_t hmodule, dword_t ordinal, + lpdword_t out_function_ptr) { // May be entry point? assert_not_zero(ordinal); @@ -184,7 +185,7 @@ dword_result_t XexGetProcedureAddress(lpvoid_t hmodule, dword_t ordinal, } DECLARE_XBOXKRNL_EXPORT1(XexGetProcedureAddress, kModules, kImplemented); -void ExRegisterTitleTerminateNotification( +void ExRegisterTitleTerminateNotification_entry( pointer_t reg, dword_t create) { if (create) { // Adding. diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc index 68564a055..1937d85af 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -20,9 +20,9 @@ namespace xe { namespace kernel { namespace xboxkrnl { -dword_result_t ObOpenObjectByName(lpunknown_t obj_attributes_ptr, - lpunknown_t object_type_ptr, dword_t unk, - lpdword_t handle_ptr) { +dword_result_t ObOpenObjectByName_entry(lpunknown_t obj_attributes_ptr, + lpunknown_t object_type_ptr, + dword_t unk, lpdword_t handle_ptr) { // r3 = ptr to info? // +0 = -4 // +4 = name ptr @@ -47,8 +47,8 @@ dword_result_t ObOpenObjectByName(lpunknown_t obj_attributes_ptr, } DECLARE_XBOXKRNL_EXPORT1(ObOpenObjectByName, kNone, kImplemented); -dword_result_t ObOpenObjectByPointer(lpvoid_t object_ptr, - lpdword_t out_handle_ptr) { +dword_result_t ObOpenObjectByPointer_entry(lpvoid_t object_ptr, + lpdword_t out_handle_ptr) { auto object = XObject::GetNativeObject(kernel_state(), object_ptr); if (!object) { return X_STATUS_UNSUCCESSFUL; @@ -61,8 +61,8 @@ dword_result_t ObOpenObjectByPointer(lpvoid_t object_ptr, } DECLARE_XBOXKRNL_EXPORT1(ObOpenObjectByPointer, kNone, kImplemented); -dword_result_t ObLookupThreadByThreadId(dword_t thread_id, - lpdword_t out_object_ptr) { +dword_result_t ObLookupThreadByThreadId_entry(dword_t thread_id, + lpdword_t out_object_ptr) { auto thread = kernel_state()->GetThreadByID(thread_id); if (!thread) { return X_STATUS_NOT_FOUND; @@ -75,9 +75,9 @@ dword_result_t ObLookupThreadByThreadId(dword_t thread_id, } DECLARE_XBOXKRNL_EXPORT1(ObLookupThreadByThreadId, kNone, kImplemented); -dword_result_t ObReferenceObjectByHandle(dword_t handle, - dword_t object_type_ptr, - lpdword_t out_object_ptr) { +dword_result_t ObReferenceObjectByHandle_entry(dword_t handle, + dword_t object_type_ptr, + lpdword_t out_object_ptr) { // These values come from how Xenia handles uninitialized kernel data exports. // D###BEEF where ### is the ordinal. const static std::unordered_map object_types = { @@ -109,22 +109,24 @@ dword_result_t ObReferenceObjectByHandle(dword_t handle, } DECLARE_XBOXKRNL_EXPORT1(ObReferenceObjectByHandle, kNone, kImplemented); -dword_result_t ObReferenceObjectByName(lpstring_t name, dword_t attributes, - dword_t object_type_ptr, - lpvoid_t parse_context, - lpdword_t out_object_ptr) { +dword_result_t ObReferenceObjectByName_entry(lpstring_t name, + dword_t attributes, + dword_t object_type_ptr, + lpvoid_t parse_context, + lpdword_t out_object_ptr) { X_HANDLE handle = X_INVALID_HANDLE_VALUE; X_STATUS result = kernel_state()->object_table()->GetObjectByName(name.value(), &handle); if (XSUCCEEDED(result)) { - return ObReferenceObjectByHandle(handle, object_type_ptr, out_object_ptr); + return ObReferenceObjectByHandle_entry(handle, object_type_ptr, + out_object_ptr); } return result; } DECLARE_XBOXKRNL_EXPORT1(ObReferenceObjectByName, kNone, kImplemented); -dword_result_t ObDereferenceObject(dword_t native_ptr) { +dword_result_t ObDereferenceObject_entry(dword_t native_ptr) { // Check if a dummy value from ObReferenceObjectByHandle. if (native_ptr == 0xDEADF00D) { return 0; @@ -140,8 +142,8 @@ dword_result_t ObDereferenceObject(dword_t native_ptr) { } DECLARE_XBOXKRNL_EXPORT1(ObDereferenceObject, kNone, kImplemented); -dword_result_t ObCreateSymbolicLink(pointer_t path_ptr, - pointer_t target_ptr) { +dword_result_t ObCreateSymbolicLink_entry(pointer_t path_ptr, + pointer_t target_ptr) { auto path = util::TranslateAnsiString(kernel_memory(), path_ptr); auto target = util::TranslateAnsiString(kernel_memory(), target_ptr); path = xe::utf8::canonicalize_guest_path(path); @@ -159,7 +161,7 @@ dword_result_t ObCreateSymbolicLink(pointer_t path_ptr, } DECLARE_XBOXKRNL_EXPORT1(ObCreateSymbolicLink, kNone, kImplemented); -dword_result_t ObDeleteSymbolicLink(pointer_t path_ptr) { +dword_result_t ObDeleteSymbolicLink_entry(pointer_t path_ptr) { auto path = util::TranslateAnsiString(kernel_memory(), path_ptr); if (!kernel_state()->file_system()->UnregisterSymbolicLink(path)) { return X_STATUS_UNSUCCESSFUL; @@ -169,8 +171,8 @@ dword_result_t ObDeleteSymbolicLink(pointer_t path_ptr) { } DECLARE_XBOXKRNL_EXPORT1(ObDeleteSymbolicLink, kNone, kImplemented); -dword_result_t NtDuplicateObject(dword_t handle, lpdword_t new_handle_ptr, - dword_t options) { +dword_result_t NtDuplicateObject_entry(dword_t handle, lpdword_t new_handle_ptr, + dword_t options) { // NOTE: new_handle_ptr can be zero to just close a handle. // NOTE: this function seems to be used to get the current thread handle // (passed handle=-2). @@ -194,7 +196,7 @@ dword_result_t NtDuplicateObject(dword_t handle, lpdword_t new_handle_ptr, } DECLARE_XBOXKRNL_EXPORT1(NtDuplicateObject, kNone, kImplemented); -dword_result_t NtClose(dword_t handle) { +dword_result_t NtClose_entry(dword_t handle) { return kernel_state()->object_table()->ReleaseHandle(handle); } DECLARE_XBOXKRNL_EXPORT1(NtClose, kNone, kImplemented); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc index 18378b662..e23acb234 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -30,8 +30,8 @@ namespace kernel { namespace xboxkrnl { // https://msdn.microsoft.com/en-us/library/ff561778 -dword_result_t RtlCompareMemory(lpvoid_t source1, lpvoid_t source2, - dword_t length) { +dword_result_t RtlCompareMemory_entry(lpvoid_t source1, lpvoid_t source2, + dword_t length) { uint8_t* p1 = source1; uint8_t* p2 = source2; @@ -51,8 +51,8 @@ dword_result_t RtlCompareMemory(lpvoid_t source1, lpvoid_t source2, DECLARE_XBOXKRNL_EXPORT1(RtlCompareMemory, kMemory, kImplemented); // https://msdn.microsoft.com/en-us/library/ff552123 -dword_result_t RtlCompareMemoryUlong(lpvoid_t source, dword_t length, - dword_t pattern) { +dword_result_t RtlCompareMemoryUlong_entry(lpvoid_t source, dword_t length, + dword_t pattern) { // Return 0 if source/length not aligned if (source.guest_address() % 4 || length % 4) { return 0; @@ -72,7 +72,8 @@ dword_result_t RtlCompareMemoryUlong(lpvoid_t source, dword_t length, DECLARE_XBOXKRNL_EXPORT1(RtlCompareMemoryUlong, kMemory, kImplemented); // https://msdn.microsoft.com/en-us/library/ff552263 -void RtlFillMemoryUlong(lpvoid_t destination, dword_t length, dword_t pattern) { +void RtlFillMemoryUlong_entry(lpvoid_t destination, dword_t length, + dword_t pattern) { // NOTE: length must be % 4, so we can work on uint32s. uint32_t count = length >> 2; @@ -84,7 +85,7 @@ void RtlFillMemoryUlong(lpvoid_t destination, dword_t length, dword_t pattern) { } DECLARE_XBOXKRNL_EXPORT1(RtlFillMemoryUlong, kMemory, kImplemented); -dword_result_t RtlUpperChar(dword_t in) { +dword_result_t RtlUpperChar_entry(dword_t in) { char c = in & 0xFF; if (c >= 'a' && c <= 'z') { return c ^ 0x20; @@ -94,7 +95,7 @@ dword_result_t RtlUpperChar(dword_t in) { } DECLARE_XBOXKRNL_EXPORT1(RtlUpperChar, kNone, kImplemented); -dword_result_t RtlLowerChar(dword_t in) { +dword_result_t RtlLowerChar_entry(dword_t in) { char c = in & 0xFF; if (c >= 'A' && c <= 'Z') { return c ^ 0x20; @@ -104,8 +105,8 @@ dword_result_t RtlLowerChar(dword_t in) { } DECLARE_XBOXKRNL_EXPORT1(RtlLowerChar, kNone, kImplemented); -dword_result_t RtlCompareString(lpstring_t string_1, lpstring_t string_2, - dword_t case_insensitive) { +dword_result_t RtlCompareString_entry(lpstring_t string_1, lpstring_t string_2, + dword_t case_insensitive) { int ret = case_insensitive ? strcasecmp(string_1, string_2) : std::strcmp(string_1, string_2); @@ -113,9 +114,11 @@ dword_result_t RtlCompareString(lpstring_t string_1, lpstring_t string_2, } DECLARE_XBOXKRNL_EXPORT1(RtlCompareString, kNone, kImplemented); -dword_result_t RtlCompareStringN(lpstring_t string_1, dword_t string_1_len, - lpstring_t string_2, dword_t string_2_len, - dword_t case_insensitive) { +dword_result_t RtlCompareStringN_entry(lpstring_t string_1, + dword_t string_1_len, + lpstring_t string_2, + dword_t string_2_len, + dword_t case_insensitive) { uint32_t len1 = string_1_len; uint32_t len2 = string_2_len; @@ -135,8 +138,8 @@ dword_result_t RtlCompareStringN(lpstring_t string_1, dword_t string_1_len, DECLARE_XBOXKRNL_EXPORT1(RtlCompareStringN, kNone, kImplemented); // https://msdn.microsoft.com/en-us/library/ff561918 -void RtlInitAnsiString(pointer_t destination, - lpstring_t source) { +void RtlInitAnsiString_entry(pointer_t destination, + lpstring_t source) { if (source) { uint16_t length = (uint16_t)strlen(source); destination->length = length; @@ -150,7 +153,7 @@ void RtlInitAnsiString(pointer_t destination, DECLARE_XBOXKRNL_EXPORT1(RtlInitAnsiString, kNone, kImplemented); // https://msdn.microsoft.com/en-us/library/ff561899 -void RtlFreeAnsiString(pointer_t string) { +void RtlFreeAnsiString_entry(pointer_t string) { if (string->pointer) { kernel_memory()->SystemHeapFree(string->pointer); } @@ -160,8 +163,8 @@ void RtlFreeAnsiString(pointer_t string) { DECLARE_XBOXKRNL_EXPORT1(RtlFreeAnsiString, kNone, kImplemented); // https://msdn.microsoft.com/en-us/library/ff561934 -void RtlInitUnicodeString(pointer_t destination, - lpu16string_t source) { +void RtlInitUnicodeString_entry(pointer_t destination, + lpu16string_t source) { if (source) { destination->length = (uint16_t)source.value().size() * 2; destination->maximum_length = (uint16_t)(source.value().size() + 1) * 2; @@ -173,7 +176,7 @@ void RtlInitUnicodeString(pointer_t destination, DECLARE_XBOXKRNL_EXPORT1(RtlInitUnicodeString, kNone, kImplemented); // https://msdn.microsoft.com/en-us/library/ff561903 -void RtlFreeUnicodeString(pointer_t string) { +void RtlFreeUnicodeString_entry(pointer_t string) { if (string->pointer) { kernel_memory()->SystemHeapFree(string->pointer); } @@ -182,8 +185,8 @@ void RtlFreeUnicodeString(pointer_t string) { } DECLARE_XBOXKRNL_EXPORT1(RtlFreeUnicodeString, kNone, kImplemented); -void RtlCopyString(pointer_t destination, - pointer_t source) { +void RtlCopyString_entry(pointer_t destination, + pointer_t source) { if (!source) { destination->length = 0; return; @@ -199,8 +202,8 @@ void RtlCopyString(pointer_t destination, } DECLARE_XBOXKRNL_EXPORT1(RtlCopyString, kNone, kImplemented); -void RtlCopyUnicodeString(pointer_t destination, - pointer_t source) { +void RtlCopyUnicodeString_entry(pointer_t destination, + pointer_t source) { if (!source) { destination->length = 0; return; @@ -217,7 +220,7 @@ void RtlCopyUnicodeString(pointer_t destination, DECLARE_XBOXKRNL_EXPORT1(RtlCopyUnicodeString, kNone, kImplemented); // https://msdn.microsoft.com/en-us/library/ff562969 -dword_result_t RtlUnicodeStringToAnsiString( +dword_result_t RtlUnicodeStringToAnsiString_entry( pointer_t destination_ptr, pointer_t source_ptr, dword_t alloc_dest) { // NTSTATUS @@ -261,11 +264,11 @@ dword_result_t RtlUnicodeStringToAnsiString( DECLARE_XBOXKRNL_EXPORT1(RtlUnicodeStringToAnsiString, kNone, kImplemented); // https://msdn.microsoft.com/en-us/library/ff553113 -dword_result_t RtlMultiByteToUnicodeN(lpword_t destination_ptr, - dword_t destination_len, - lpdword_t written_ptr, - pointer_t source_ptr, - dword_t source_len) { +dword_result_t RtlMultiByteToUnicodeN_entry(lpword_t destination_ptr, + dword_t destination_len, + lpdword_t written_ptr, + pointer_t source_ptr, + dword_t source_len) { uint32_t copy_len = destination_len >> 1; copy_len = copy_len < source_len ? copy_len : source_len.value(); @@ -286,10 +289,11 @@ DECLARE_XBOXKRNL_EXPORT3(RtlMultiByteToUnicodeN, kNone, kImplemented, kHighFrequency, kSketchy); // https://msdn.microsoft.com/en-us/library/ff553261 -dword_result_t RtlUnicodeToMultiByteN(pointer_t destination_ptr, - dword_t destination_len, - lpdword_t written_ptr, - lpword_t source_ptr, dword_t source_len) { +dword_result_t RtlUnicodeToMultiByteN_entry(pointer_t destination_ptr, + dword_t destination_len, + lpdword_t written_ptr, + lpword_t source_ptr, + dword_t source_len) { uint32_t copy_len = source_len >> 1; copy_len = copy_len < destination_len ? copy_len : destination_len.value(); @@ -302,14 +306,13 @@ dword_result_t RtlUnicodeToMultiByteN(pointer_t destination_ptr, if (written_ptr.guest_address() != 0) { *written_ptr = copy_len; } - return 0; } DECLARE_XBOXKRNL_EXPORT3(RtlUnicodeToMultiByteN, kNone, kImplemented, kHighFrequency, kSketchy); // https://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Executable%20Images/RtlImageNtHeader.html -pointer_result_t RtlImageNtHeader(lpvoid_t module) { +pointer_result_t RtlImageNtHeader_entry(lpvoid_t module) { if (!module) { return 0; } @@ -332,8 +335,8 @@ pointer_result_t RtlImageNtHeader(lpvoid_t module) { } DECLARE_XBOXKRNL_EXPORT1(RtlImageNtHeader, kNone, kImplemented); -pointer_result_t RtlImageXexHeaderField(pointer_t xex_header, - dword_t field_dword) { +pointer_result_t RtlImageXexHeaderField_entry(pointer_t xex_header, + dword_t field_dword) { uint32_t field_value = 0; uint32_t field = field_dword; // VS acts weird going from dword_t -> enum @@ -382,7 +385,7 @@ void xeRtlInitializeCriticalSection(X_RTL_CRITICAL_SECTION* cs, cs->owning_thread = 0; } -void RtlInitializeCriticalSection(pointer_t cs) { +void RtlInitializeCriticalSection_entry(pointer_t cs) { xeRtlInitializeCriticalSection(cs, cs.guest_address()); } DECLARE_XBOXKRNL_EXPORT1(RtlInitializeCriticalSection, kNone, kImplemented); @@ -407,7 +410,7 @@ X_STATUS xeRtlInitializeCriticalSectionAndSpinCount(X_RTL_CRITICAL_SECTION* cs, return X_STATUS_SUCCESS; } -dword_result_t RtlInitializeCriticalSectionAndSpinCount( +dword_result_t RtlInitializeCriticalSectionAndSpinCount_entry( pointer_t cs, dword_t spin_count) { return xeRtlInitializeCriticalSectionAndSpinCount(cs, cs.guest_address(), spin_count); @@ -415,7 +418,7 @@ dword_result_t RtlInitializeCriticalSectionAndSpinCount( DECLARE_XBOXKRNL_EXPORT1(RtlInitializeCriticalSectionAndSpinCount, kNone, kImplemented); -void RtlEnterCriticalSection(pointer_t cs) { +void RtlEnterCriticalSection_entry(pointer_t cs) { uint32_t cur_thread = XThread::GetCurrentThread()->guest_object(); uint32_t spin_count = cs->header.absolute * 256; @@ -449,7 +452,7 @@ void RtlEnterCriticalSection(pointer_t cs) { DECLARE_XBOXKRNL_EXPORT2(RtlEnterCriticalSection, kNone, kImplemented, kHighFrequency); -dword_result_t RtlTryEnterCriticalSection( +dword_result_t RtlTryEnterCriticalSection_entry( pointer_t cs) { uint32_t thread = XThread::GetCurrentThread()->guest_object(); @@ -471,7 +474,7 @@ dword_result_t RtlTryEnterCriticalSection( DECLARE_XBOXKRNL_EXPORT2(RtlTryEnterCriticalSection, kNone, kImplemented, kHighFrequency); -void RtlLeaveCriticalSection(pointer_t cs) { +void RtlLeaveCriticalSection_entry(pointer_t cs) { assert_true(cs->owning_thread == XThread::GetCurrentThread()->guest_object()); // Drop recursion count - if it isn't zero we still have the lock. @@ -506,8 +509,8 @@ struct X_TIME_FIELDS { static_assert_size(X_TIME_FIELDS, 16); // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimetotimefields -void RtlTimeToTimeFields(lpqword_t time_ptr, - pointer_t time_fields_ptr) { +void RtlTimeToTimeFields_entry(lpqword_t time_ptr, + pointer_t time_fields_ptr) { auto tp = XClock::to_sys(XClock::from_file_time(time_ptr.value())); auto dp = date::floor(tp); auto year_month_day = date::year_month_day{dp}; @@ -526,8 +529,8 @@ void RtlTimeToTimeFields(lpqword_t time_ptr, DECLARE_XBOXKRNL_EXPORT1(RtlTimeToTimeFields, kNone, kImplemented); // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimefieldstotime -dword_result_t RtlTimeFieldsToTime(pointer_t time_fields_ptr, - lpqword_t time_ptr) { +dword_result_t RtlTimeFieldsToTime_entry( + pointer_t time_fields_ptr, lpqword_t time_ptr) { if (time_fields_ptr->year < 1601 || time_fields_ptr->month < 1 || time_fields_ptr->month > 12 || time_fields_ptr->day < 1 || time_fields_ptr->day > 31 || time_fields_ptr->hour > 23 || @@ -608,7 +611,8 @@ static uint32_t crc32_table[256] = { 0x2D02EF8Du, }; -dword_result_t RtlComputeCrc32(dword_t seed, lpvoid_t buffer, dword_t length) { +dword_result_t RtlComputeCrc32_entry(dword_t seed, lpvoid_t buffer, + dword_t length) { if (!length) { return seed.value(); } diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc index 2cfcb50ac..1bab60f73 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -824,7 +824,7 @@ class WideCountFormatData : public FormatData { int32_t count_; }; -SHIM_CALL DbgPrint_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL DbgPrint_entry(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t format_ptr = SHIM_GET_ARG_32(0); if (!format_ptr) { SHIM_SET_RETURN_32(X_STATUS_INVALID_PARAMETER); @@ -854,7 +854,7 @@ SHIM_CALL DbgPrint_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx -SHIM_CALL _snprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL _snprintf_entry(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t buffer_ptr = SHIM_GET_ARG_32(0); int32_t buffer_count = SHIM_GET_ARG_32(1); uint32_t format_ptr = SHIM_GET_ARG_32(2); @@ -894,7 +894,7 @@ SHIM_CALL _snprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/ybk95axf.aspx -SHIM_CALL sprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL sprintf_entry(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t buffer_ptr = SHIM_GET_ARG_32(0); uint32_t format_ptr = SHIM_GET_ARG_32(1); @@ -925,7 +925,7 @@ SHIM_CALL sprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx -SHIM_CALL _snwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL _snwprintf_entry(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t buffer_ptr = SHIM_GET_ARG_32(0); int32_t buffer_count = SHIM_GET_ARG_32(1); uint32_t format_ptr = SHIM_GET_ARG_32(2); @@ -966,7 +966,7 @@ SHIM_CALL _snwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/ybk95axf.aspx -SHIM_CALL swprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL swprintf_entry(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t buffer_ptr = SHIM_GET_ARG_32(0); uint32_t format_ptr = SHIM_GET_ARG_32(1); @@ -998,7 +998,7 @@ SHIM_CALL swprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/1kt27hek.aspx -SHIM_CALL _vsnprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL _vsnprintf_entry(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t buffer_ptr = SHIM_GET_ARG_32(0); int32_t buffer_count = SHIM_GET_ARG_32(1); uint32_t format_ptr = SHIM_GET_ARG_32(2); @@ -1041,7 +1041,8 @@ SHIM_CALL _vsnprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/1kt27hek.aspx -SHIM_CALL _vsnwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL _vsnwprintf_entry(PPCContext* ppc_context, + KernelState* kernel_state) { uint32_t buffer_ptr = SHIM_GET_ARG_32(0); int32_t buffer_count = SHIM_GET_ARG_32(1); uint32_t format_ptr = SHIM_GET_ARG_32(2); @@ -1086,7 +1087,7 @@ SHIM_CALL _vsnwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/28d5ce15.aspx -SHIM_CALL vsprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL vsprintf_entry(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t buffer_ptr = SHIM_GET_ARG_32(0); uint32_t format_ptr = SHIM_GET_ARG_32(1); uint32_t arg_ptr = SHIM_GET_ARG_32(2); @@ -1118,7 +1119,8 @@ SHIM_CALL vsprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/w05tbk72.aspx -SHIM_CALL _vscwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL _vscwprintf_entry(PPCContext* ppc_context, + KernelState* kernel_state) { uint32_t format_ptr = SHIM_GET_ARG_32(0); uint32_t arg_ptr = SHIM_GET_ARG_32(1); @@ -1145,7 +1147,7 @@ SHIM_CALL _vscwprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { } // https://msdn.microsoft.com/en-us/library/28d5ce15.aspx -SHIM_CALL vswprintf_shim(PPCContext* ppc_context, KernelState* kernel_state) { +SHIM_CALL vswprintf_entry(PPCContext* ppc_context, KernelState* kernel_state) { uint32_t buffer_ptr = SHIM_GET_ARG_32(0); uint32_t format_ptr = SHIM_GET_ARG_32(1); uint32_t arg_ptr = SHIM_GET_ARG_32(2); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index 0fa37bda8..c2c968218 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -95,11 +95,12 @@ object_ref LookupNamedObject(KernelState* kernel_state, return nullptr; } -dword_result_t ExCreateThread(lpdword_t handle_ptr, dword_t stack_size, - lpdword_t thread_id_ptr, - dword_t xapi_thread_startup, - lpvoid_t start_address, lpvoid_t start_context, - dword_t creation_flags) { +dword_result_t ExCreateThread_entry(lpdword_t handle_ptr, dword_t stack_size, + lpdword_t thread_id_ptr, + dword_t xapi_thread_startup, + lpvoid_t start_address, + lpvoid_t start_context, + dword_t creation_flags) { // http://jafile.com/uploads/scoop/main.cpp.txt // DWORD // LPHANDLE Handle, @@ -149,7 +150,7 @@ dword_result_t ExCreateThread(lpdword_t handle_ptr, dword_t stack_size, } DECLARE_XBOXKRNL_EXPORT1(ExCreateThread, kThreading, kImplemented); -dword_result_t ExTerminateThread(dword_t exit_code) { +dword_result_t ExTerminateThread_entry(dword_t exit_code) { XThread* thread = XThread::GetCurrentThread(); // NOTE: this kills us right now. We won't return from it. @@ -157,7 +158,8 @@ dword_result_t ExTerminateThread(dword_t exit_code) { } DECLARE_XBOXKRNL_EXPORT1(ExTerminateThread, kThreading, kImplemented); -dword_result_t NtResumeThread(dword_t handle, lpdword_t suspend_count_ptr) { +dword_result_t NtResumeThread_entry(dword_t handle, + lpdword_t suspend_count_ptr) { X_RESULT result = X_STATUS_INVALID_HANDLE; uint32_t suspend_count = 0; @@ -173,7 +175,7 @@ dword_result_t NtResumeThread(dword_t handle, lpdword_t suspend_count_ptr) { } DECLARE_XBOXKRNL_EXPORT1(NtResumeThread, kThreading, kImplemented); -dword_result_t KeResumeThread(lpvoid_t thread_ptr) { +dword_result_t KeResumeThread_entry(lpvoid_t thread_ptr) { X_STATUS result = X_STATUS_SUCCESS; auto thread = XObject::GetNativeObject(kernel_state(), thread_ptr); if (thread) { @@ -186,7 +188,8 @@ dword_result_t KeResumeThread(lpvoid_t thread_ptr) { } DECLARE_XBOXKRNL_EXPORT1(KeResumeThread, kThreading, kImplemented); -dword_result_t NtSuspendThread(dword_t handle, lpdword_t suspend_count_ptr) { +dword_result_t NtSuspendThread_entry(dword_t handle, + lpdword_t suspend_count_ptr) { X_RESULT result = X_STATUS_SUCCESS; uint32_t suspend_count = 0; @@ -205,9 +208,11 @@ dword_result_t NtSuspendThread(dword_t handle, lpdword_t suspend_count_ptr) { } DECLARE_XBOXKRNL_EXPORT1(NtSuspendThread, kThreading, kImplemented); -void KeSetCurrentStackPointers(lpvoid_t stack_ptr, pointer_t thread, - lpvoid_t stack_alloc_base, lpvoid_t stack_base, - lpvoid_t stack_limit) { +void KeSetCurrentStackPointers_entry(lpvoid_t stack_ptr, + pointer_t thread, + lpvoid_t stack_alloc_base, + lpvoid_t stack_base, + lpvoid_t stack_limit) { auto current_thread = XThread::GetCurrentThread(); auto context = current_thread->thread_state()->context(); auto pcr = kernel_memory()->TranslateVirtual( @@ -230,8 +235,8 @@ void KeSetCurrentStackPointers(lpvoid_t stack_ptr, pointer_t thread, DECLARE_XBOXKRNL_EXPORT2(KeSetCurrentStackPointers, kThreading, kImplemented, kHighFrequency); -dword_result_t KeSetAffinityThread(lpvoid_t thread_ptr, dword_t affinity, - lpdword_t previous_affinity_ptr) { +dword_result_t KeSetAffinityThread_entry(lpvoid_t thread_ptr, dword_t affinity, + lpdword_t previous_affinity_ptr) { // The Xbox 360, according to disassembly of KeSetAffinityThread, unlike // Windows NT, stores the previous affinity via the pointer provided as an // argument, not in the return value - the return value is used for the @@ -250,7 +255,7 @@ dword_result_t KeSetAffinityThread(lpvoid_t thread_ptr, dword_t affinity, } DECLARE_XBOXKRNL_EXPORT1(KeSetAffinityThread, kThreading, kImplemented); -dword_result_t KeQueryBasePriorityThread(lpvoid_t thread_ptr) { +dword_result_t KeQueryBasePriorityThread_entry(lpvoid_t thread_ptr) { int32_t priority = 0; auto thread = XObject::GetNativeObject(kernel_state(), thread_ptr); @@ -262,7 +267,8 @@ dword_result_t KeQueryBasePriorityThread(lpvoid_t thread_ptr) { } DECLARE_XBOXKRNL_EXPORT1(KeQueryBasePriorityThread, kThreading, kImplemented); -dword_result_t KeSetBasePriorityThread(lpvoid_t thread_ptr, dword_t increment) { +dword_result_t KeSetBasePriorityThread_entry(lpvoid_t thread_ptr, + dword_t increment) { int32_t prev_priority = 0; auto thread = XObject::GetNativeObject(kernel_state(), thread_ptr); @@ -275,7 +281,8 @@ dword_result_t KeSetBasePriorityThread(lpvoid_t thread_ptr, dword_t increment) { } DECLARE_XBOXKRNL_EXPORT1(KeSetBasePriorityThread, kThreading, kImplemented); -dword_result_t KeSetDisableBoostThread(lpvoid_t thread_ptr, dword_t disabled) { +dword_result_t KeSetDisableBoostThread_entry(lpvoid_t thread_ptr, + dword_t disabled) { auto thread = XObject::GetNativeObject(kernel_state(), thread_ptr); if (thread) { // Uhm? @@ -285,13 +292,13 @@ dword_result_t KeSetDisableBoostThread(lpvoid_t thread_ptr, dword_t disabled) { } DECLARE_XBOXKRNL_EXPORT1(KeSetDisableBoostThread, kThreading, kImplemented); -dword_result_t KeGetCurrentProcessType() { +dword_result_t KeGetCurrentProcessType_entry() { return kernel_state()->process_type(); } DECLARE_XBOXKRNL_EXPORT2(KeGetCurrentProcessType, kThreading, kImplemented, kHighFrequency); -void KeSetCurrentProcessType(dword_t type) { +void KeSetCurrentProcessType_entry(dword_t type) { // One of X_PROCTYPE_? assert_true(type <= 2); @@ -300,15 +307,16 @@ void KeSetCurrentProcessType(dword_t type) { } DECLARE_XBOXKRNL_EXPORT1(KeSetCurrentProcessType, kThreading, kImplemented); -dword_result_t KeQueryPerformanceFrequency() { +dword_result_t KeQueryPerformanceFrequency_entry() { uint64_t result = Clock::guest_tick_frequency(); return static_cast(result); } DECLARE_XBOXKRNL_EXPORT2(KeQueryPerformanceFrequency, kThreading, kImplemented, kHighFrequency); -dword_result_t KeDelayExecutionThread(dword_t processor_mode, dword_t alertable, - lpqword_t interval_ptr) { +dword_result_t KeDelayExecutionThread_entry(dword_t processor_mode, + dword_t alertable, + lpqword_t interval_ptr) { XThread* thread = XThread::GetCurrentThread(); X_STATUS result = thread->Delay(processor_mode, alertable, *interval_ptr); @@ -317,7 +325,7 @@ dword_result_t KeDelayExecutionThread(dword_t processor_mode, dword_t alertable, DECLARE_XBOXKRNL_EXPORT3(KeDelayExecutionThread, kThreading, kImplemented, kBlocking, kHighFrequency); -dword_result_t NtYieldExecution() { +dword_result_t NtYieldExecution_entry() { auto thread = XThread::GetCurrentThread(); thread->Delay(0, 0, 0); return 0; @@ -325,7 +333,7 @@ dword_result_t NtYieldExecution() { DECLARE_XBOXKRNL_EXPORT2(NtYieldExecution, kThreading, kImplemented, kHighFrequency); -void KeQuerySystemTime(lpqword_t time_ptr) { +void KeQuerySystemTime_entry(lpqword_t time_ptr) { uint64_t time = Clock::QueryGuestSystemTime(); if (time_ptr) { *time_ptr = time; @@ -334,7 +342,7 @@ void KeQuerySystemTime(lpqword_t time_ptr) { DECLARE_XBOXKRNL_EXPORT1(KeQuerySystemTime, kThreading, kImplemented); // https://msdn.microsoft.com/en-us/library/ms686801 -dword_result_t KeTlsAlloc() { +dword_result_t KeTlsAlloc_entry() { uint32_t slot = kernel_state()->AllocateTLS(); XThread::GetCurrentThread()->SetTLSValue(slot, 0); @@ -343,7 +351,7 @@ dword_result_t KeTlsAlloc() { DECLARE_XBOXKRNL_EXPORT1(KeTlsAlloc, kThreading, kImplemented); // https://msdn.microsoft.com/en-us/library/ms686804 -dword_result_t KeTlsFree(dword_t tls_index) { +dword_result_t KeTlsFree_entry(dword_t tls_index) { if (tls_index == X_TLS_OUT_OF_INDEXES) { return 0; } @@ -354,7 +362,7 @@ dword_result_t KeTlsFree(dword_t tls_index) { DECLARE_XBOXKRNL_EXPORT1(KeTlsFree, kThreading, kImplemented); // https://msdn.microsoft.com/en-us/library/ms686812 -dword_result_t KeTlsGetValue(dword_t tls_index) { +dword_result_t KeTlsGetValue_entry(dword_t tls_index) { // xboxkrnl doesn't actually have an error branch - it always succeeds, even // if it overflows the TLS. uint32_t value = 0; @@ -368,7 +376,7 @@ DECLARE_XBOXKRNL_EXPORT2(KeTlsGetValue, kThreading, kImplemented, kHighFrequency); // https://msdn.microsoft.com/en-us/library/ms686818 -dword_result_t KeTlsSetValue(dword_t tls_index, dword_t tls_value) { +dword_result_t KeTlsSetValue_entry(dword_t tls_index, dword_t tls_value) { // xboxkrnl doesn't actually have an error branch - it always succeeds, even // if it overflows the TLS. if (XThread::GetCurrentThread()->SetTLSValue(tls_index, tls_value)) { @@ -379,8 +387,8 @@ dword_result_t KeTlsSetValue(dword_t tls_index, dword_t tls_value) { } DECLARE_XBOXKRNL_EXPORT1(KeTlsSetValue, kThreading, kImplemented); -void KeInitializeEvent(pointer_t event_ptr, dword_t event_type, - dword_t initial_state) { +void KeInitializeEvent_entry(pointer_t event_ptr, dword_t event_type, + dword_t initial_state) { event_ptr.Zero(); event_ptr->header.type = event_type; event_ptr->header.signal_state = (uint32_t)initial_state; @@ -403,14 +411,14 @@ uint32_t xeKeSetEvent(X_KEVENT* event_ptr, uint32_t increment, uint32_t wait) { return ev->Set(increment, !!wait); } -dword_result_t KeSetEvent(pointer_t event_ptr, dword_t increment, - dword_t wait) { +dword_result_t KeSetEvent_entry(pointer_t event_ptr, + dword_t increment, dword_t wait) { return xeKeSetEvent(event_ptr, increment, wait); } DECLARE_XBOXKRNL_EXPORT2(KeSetEvent, kThreading, kImplemented, kHighFrequency); -dword_result_t KePulseEvent(pointer_t event_ptr, dword_t increment, - dword_t wait) { +dword_result_t KePulseEvent_entry(pointer_t event_ptr, + dword_t increment, dword_t wait) { auto ev = XObject::GetNativeObject(kernel_state(), event_ptr); if (!ev) { assert_always(); @@ -422,7 +430,7 @@ dword_result_t KePulseEvent(pointer_t event_ptr, dword_t increment, DECLARE_XBOXKRNL_EXPORT2(KePulseEvent, kThreading, kImplemented, kHighFrequency); -dword_result_t KeResetEvent(pointer_t event_ptr) { +dword_result_t KeResetEvent_entry(pointer_t event_ptr) { auto ev = XObject::GetNativeObject(kernel_state(), event_ptr); if (!ev) { assert_always(); @@ -433,9 +441,9 @@ dword_result_t KeResetEvent(pointer_t event_ptr) { } DECLARE_XBOXKRNL_EXPORT1(KeResetEvent, kThreading, kImplemented); -dword_result_t NtCreateEvent(lpdword_t handle_ptr, - pointer_t obj_attributes_ptr, - dword_t event_type, dword_t initial_state) { +dword_result_t NtCreateEvent_entry( + lpdword_t handle_ptr, pointer_t obj_attributes_ptr, + dword_t event_type, dword_t initial_state) { // Check for an existing timer with the same name. auto existing_object = LookupNamedObject(kernel_state(), obj_attributes_ptr); @@ -482,12 +490,13 @@ uint32_t xeNtSetEvent(uint32_t handle, xe::be* previous_state_ptr) { return result; } -dword_result_t NtSetEvent(dword_t handle, lpdword_t previous_state_ptr) { +dword_result_t NtSetEvent_entry(dword_t handle, lpdword_t previous_state_ptr) { return xeNtSetEvent(handle, previous_state_ptr); } DECLARE_XBOXKRNL_EXPORT2(NtSetEvent, kThreading, kImplemented, kHighFrequency); -dword_result_t NtPulseEvent(dword_t handle, lpdword_t previous_state_ptr) { +dword_result_t NtPulseEvent_entry(dword_t handle, + lpdword_t previous_state_ptr) { X_STATUS result = X_STATUS_SUCCESS; auto ev = kernel_state()->object_table()->LookupObject(handle); @@ -518,13 +527,15 @@ uint32_t xeNtClearEvent(uint32_t handle) { return result; } -dword_result_t NtClearEvent(dword_t handle) { return xeNtClearEvent(handle); } +dword_result_t NtClearEvent_entry(dword_t handle) { + return xeNtClearEvent(handle); +} DECLARE_XBOXKRNL_EXPORT2(NtClearEvent, kThreading, kImplemented, kHighFrequency); // https://msdn.microsoft.com/en-us/library/windows/hardware/ff552150(v=vs.85).aspx -void KeInitializeSemaphore(pointer_t semaphore_ptr, dword_t count, - dword_t limit) { +void KeInitializeSemaphore_entry(pointer_t semaphore_ptr, + dword_t count, dword_t limit) { semaphore_ptr->header.type = 5; // SemaphoreObject semaphore_ptr->header.signal_state = (uint32_t)count; semaphore_ptr->limit = (uint32_t)limit; @@ -553,16 +564,16 @@ uint32_t xeKeReleaseSemaphore(X_KSEMAPHORE* semaphore_ptr, uint32_t increment, return sem->ReleaseSemaphore(adjustment); } -dword_result_t KeReleaseSemaphore(pointer_t semaphore_ptr, - dword_t increment, dword_t adjustment, - dword_t wait) { +dword_result_t KeReleaseSemaphore_entry(pointer_t semaphore_ptr, + dword_t increment, dword_t adjustment, + dword_t wait) { return xeKeReleaseSemaphore(semaphore_ptr, increment, adjustment, wait); } DECLARE_XBOXKRNL_EXPORT1(KeReleaseSemaphore, kThreading, kImplemented); -dword_result_t NtCreateSemaphore(lpdword_t handle_ptr, - lpvoid_t obj_attributes_ptr, dword_t count, - dword_t limit) { +dword_result_t NtCreateSemaphore_entry(lpdword_t handle_ptr, + lpvoid_t obj_attributes_ptr, + dword_t count, dword_t limit) { // Check for an existing timer with the same name. auto existing_object = LookupNamedObject(kernel_state(), obj_attributes_ptr); @@ -594,8 +605,9 @@ dword_result_t NtCreateSemaphore(lpdword_t handle_ptr, } DECLARE_XBOXKRNL_EXPORT1(NtCreateSemaphore, kThreading, kImplemented); -dword_result_t NtReleaseSemaphore(dword_t sem_handle, dword_t release_count, - lpdword_t previous_count_ptr) { +dword_result_t NtReleaseSemaphore_entry(dword_t sem_handle, + dword_t release_count, + lpdword_t previous_count_ptr) { X_STATUS result = X_STATUS_SUCCESS; int32_t previous_count = 0; @@ -612,11 +624,12 @@ dword_result_t NtReleaseSemaphore(dword_t sem_handle, dword_t release_count, return result; } -DECLARE_XBOXKRNL_EXPORT1(NtReleaseSemaphore, kThreading, kImplemented); +DECLARE_XBOXKRNL_EXPORT2(NtReleaseSemaphore, kThreading, kImplemented, + kHighFrequency); -dword_result_t NtCreateMutant(lpdword_t handle_out, - pointer_t obj_attributes, - dword_t initial_owner) { +dword_result_t NtCreateMutant_entry( + lpdword_t handle_out, pointer_t obj_attributes, + dword_t initial_owner) { // Check for an existing timer with the same name. auto existing_object = LookupNamedObject( kernel_state(), obj_attributes.guest_address()); @@ -648,7 +661,7 @@ dword_result_t NtCreateMutant(lpdword_t handle_out, } DECLARE_XBOXKRNL_EXPORT1(NtCreateMutant, kThreading, kImplemented); -dword_result_t NtReleaseMutant(dword_t mutant_handle, dword_t unknown) { +dword_result_t NtReleaseMutant_entry(dword_t mutant_handle, dword_t unknown) { // This doesn't seem to be supported. // int32_t previous_count_ptr = SHIM_GET_ARG_32(2); @@ -674,8 +687,9 @@ dword_result_t NtReleaseMutant(dword_t mutant_handle, dword_t unknown) { } DECLARE_XBOXKRNL_EXPORT1(NtReleaseMutant, kThreading, kImplemented); -dword_result_t NtCreateTimer(lpdword_t handle_ptr, lpvoid_t obj_attributes_ptr, - dword_t timer_type) { +dword_result_t NtCreateTimer_entry(lpdword_t handle_ptr, + lpvoid_t obj_attributes_ptr, + dword_t timer_type) { // timer_type = NotificationTimer (0) or SynchronizationTimer (1) // Check for an existing timer with the same name. @@ -709,11 +723,11 @@ dword_result_t NtCreateTimer(lpdword_t handle_ptr, lpvoid_t obj_attributes_ptr, } DECLARE_XBOXKRNL_EXPORT1(NtCreateTimer, kThreading, kImplemented); -dword_result_t NtSetTimerEx(dword_t timer_handle, lpqword_t due_time_ptr, - lpvoid_t routine_ptr /*PTIMERAPCROUTINE*/, - dword_t unk_one, lpvoid_t routine_arg, - dword_t resume, dword_t period_ms, - dword_t unk_zero) { +dword_result_t NtSetTimerEx_entry(dword_t timer_handle, lpqword_t due_time_ptr, + lpvoid_t routine_ptr /*PTIMERAPCROUTINE*/, + dword_t unk_one, lpvoid_t routine_arg, + dword_t resume, dword_t period_ms, + dword_t unk_zero) { assert_true(unk_one == 1); assert_true(unk_zero == 0); @@ -735,8 +749,8 @@ dword_result_t NtSetTimerEx(dword_t timer_handle, lpqword_t due_time_ptr, } DECLARE_XBOXKRNL_EXPORT1(NtSetTimerEx, kThreading, kImplemented); -dword_result_t NtCancelTimer(dword_t timer_handle, - lpdword_t current_state_ptr) { +dword_result_t NtCancelTimer_entry(dword_t timer_handle, + lpdword_t current_state_ptr) { X_STATUS result = X_STATUS_SUCCESS; auto timer = @@ -771,9 +785,11 @@ uint32_t xeKeWaitForSingleObject(void* object_ptr, uint32_t wait_reason, return result; } -dword_result_t KeWaitForSingleObject(lpvoid_t object_ptr, dword_t wait_reason, - dword_t processor_mode, dword_t alertable, - lpqword_t timeout_ptr) { +dword_result_t KeWaitForSingleObject_entry(lpvoid_t object_ptr, + dword_t wait_reason, + dword_t processor_mode, + dword_t alertable, + lpqword_t timeout_ptr) { uint64_t timeout = timeout_ptr ? static_cast(*timeout_ptr) : 0u; return xeKeWaitForSingleObject(object_ptr, wait_reason, processor_mode, alertable, timeout_ptr ? &timeout : nullptr); @@ -781,9 +797,10 @@ dword_result_t KeWaitForSingleObject(lpvoid_t object_ptr, dword_t wait_reason, DECLARE_XBOXKRNL_EXPORT3(KeWaitForSingleObject, kThreading, kImplemented, kBlocking, kHighFrequency); -dword_result_t NtWaitForSingleObjectEx(dword_t object_handle, dword_t wait_mode, - dword_t alertable, - lpqword_t timeout_ptr) { +dword_result_t NtWaitForSingleObjectEx_entry(dword_t object_handle, + dword_t wait_mode, + dword_t alertable, + lpqword_t timeout_ptr) { X_STATUS result = X_STATUS_SUCCESS; auto object = @@ -801,12 +818,10 @@ dword_result_t NtWaitForSingleObjectEx(dword_t object_handle, dword_t wait_mode, DECLARE_XBOXKRNL_EXPORT3(NtWaitForSingleObjectEx, kThreading, kImplemented, kBlocking, kHighFrequency); -dword_result_t KeWaitForMultipleObjects(dword_t count, lpdword_t objects_ptr, - dword_t wait_type, dword_t wait_reason, - dword_t processor_mode, - dword_t alertable, - lpqword_t timeout_ptr, - lpvoid_t wait_block_array_ptr) { +dword_result_t KeWaitForMultipleObjects_entry( + dword_t count, lpdword_t objects_ptr, dword_t wait_type, + dword_t wait_reason, dword_t processor_mode, dword_t alertable, + lpqword_t timeout_ptr, lpvoid_t wait_block_array_ptr) { assert_true(wait_type <= 1); std::vector> objects; @@ -852,10 +867,9 @@ uint32_t xeNtWaitForMultipleObjectsEx(uint32_t count, xe::be* handles, wait_type, 6, wait_mode, alertable, timeout_ptr); } -dword_result_t NtWaitForMultipleObjectsEx(dword_t count, lpdword_t handles, - dword_t wait_type, dword_t wait_mode, - dword_t alertable, - lpqword_t timeout_ptr) { +dword_result_t NtWaitForMultipleObjectsEx_entry( + dword_t count, lpdword_t handles, dword_t wait_type, dword_t wait_mode, + dword_t alertable, lpqword_t timeout_ptr) { uint64_t timeout = timeout_ptr ? static_cast(*timeout_ptr) : 0u; return xeNtWaitForMultipleObjectsEx(count, handles, wait_type, wait_mode, alertable, @@ -864,10 +878,11 @@ dword_result_t NtWaitForMultipleObjectsEx(dword_t count, lpdword_t handles, DECLARE_XBOXKRNL_EXPORT3(NtWaitForMultipleObjectsEx, kThreading, kImplemented, kBlocking, kHighFrequency); -dword_result_t NtSignalAndWaitForSingleObjectEx(dword_t signal_handle, - dword_t wait_handle, - dword_t alertable, dword_t r6, - lpqword_t timeout_ptr) { +dword_result_t NtSignalAndWaitForSingleObjectEx_entry(dword_t signal_handle, + dword_t wait_handle, + dword_t alertable, + dword_t r6, + lpqword_t timeout_ptr) { X_STATUS result = X_STATUS_SUCCESS; auto signal_object = @@ -907,7 +922,7 @@ uint32_t xeKeKfAcquireSpinLock(uint32_t* lock) { return old_irql; } -dword_result_t KfAcquireSpinLock(lpdword_t lock_ptr) { +dword_result_t KfAcquireSpinLock_entry(lpdword_t lock_ptr) { auto lock = reinterpret_cast(lock_ptr.host_address()); return xeKeKfAcquireSpinLock(lock); } @@ -923,14 +938,14 @@ void xeKeKfReleaseSpinLock(uint32_t* lock, dword_t old_irql) { xe::atomic_dec(lock); } -void KfReleaseSpinLock(lpdword_t lock_ptr, dword_t old_irql) { +void KfReleaseSpinLock_entry(lpdword_t lock_ptr, dword_t old_irql) { auto lock = reinterpret_cast(lock_ptr.host_address()); xeKeKfReleaseSpinLock(lock, old_irql); } DECLARE_XBOXKRNL_EXPORT2(KfReleaseSpinLock, kThreading, kImplemented, kHighFrequency); -void KeAcquireSpinLockAtRaisedIrql(lpdword_t lock_ptr) { +void KeAcquireSpinLockAtRaisedIrql_entry(lpdword_t lock_ptr) { // Lock. auto lock = reinterpret_cast(lock_ptr.host_address()); while (!xe::atomic_cas(0, 1, lock)) { @@ -941,7 +956,7 @@ void KeAcquireSpinLockAtRaisedIrql(lpdword_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT3(KeAcquireSpinLockAtRaisedIrql, kThreading, kImplemented, kBlocking, kHighFrequency); -dword_result_t KeTryToAcquireSpinLockAtRaisedIrql(lpdword_t lock_ptr) { +dword_result_t KeTryToAcquireSpinLockAtRaisedIrql_entry(lpdword_t lock_ptr) { // Lock. auto lock = reinterpret_cast(lock_ptr.host_address()); if (!xe::atomic_cas(0, 1, lock)) { @@ -952,7 +967,7 @@ dword_result_t KeTryToAcquireSpinLockAtRaisedIrql(lpdword_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT4(KeTryToAcquireSpinLockAtRaisedIrql, kThreading, kImplemented, kBlocking, kHighFrequency, kSketchy); -void KeReleaseSpinLockFromRaisedIrql(lpdword_t lock_ptr) { +void KeReleaseSpinLockFromRaisedIrql_entry(lpdword_t lock_ptr) { // Unlock. auto lock = reinterpret_cast(lock_ptr.host_address()); xe::atomic_dec(lock); @@ -960,26 +975,26 @@ void KeReleaseSpinLockFromRaisedIrql(lpdword_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT2(KeReleaseSpinLockFromRaisedIrql, kThreading, kImplemented, kHighFrequency); -void KeEnterCriticalRegion() { +void KeEnterCriticalRegion_entry() { XThread::GetCurrentThread()->EnterCriticalRegion(); } DECLARE_XBOXKRNL_EXPORT2(KeEnterCriticalRegion, kThreading, kImplemented, kHighFrequency); -void KeLeaveCriticalRegion() { +void KeLeaveCriticalRegion_entry() { XThread::GetCurrentThread()->LeaveCriticalRegion(); } DECLARE_XBOXKRNL_EXPORT2(KeLeaveCriticalRegion, kThreading, kImplemented, kHighFrequency); -dword_result_t KeRaiseIrqlToDpcLevel() { +dword_result_t KeRaiseIrqlToDpcLevel_entry() { auto old_value = kernel_state()->processor()->RaiseIrql(cpu::Irql::DPC); return (uint32_t)old_value; } DECLARE_XBOXKRNL_EXPORT2(KeRaiseIrqlToDpcLevel, kThreading, kImplemented, kHighFrequency); -void KfLowerIrql(dword_t old_value) { +void KfLowerIrql_entry(dword_t old_value) { kernel_state()->processor()->LowerIrql( static_cast((uint32_t)old_value)); @@ -987,9 +1002,9 @@ void KfLowerIrql(dword_t old_value) { } DECLARE_XBOXKRNL_EXPORT2(KfLowerIrql, kThreading, kImplemented, kHighFrequency); -void NtQueueApcThread(dword_t thread_handle, lpvoid_t apc_routine, - lpvoid_t apc_routine_context, lpvoid_t arg1, - lpvoid_t arg2) { +void NtQueueApcThread_entry(dword_t thread_handle, lpvoid_t apc_routine, + lpvoid_t apc_routine_context, lpvoid_t arg1, + lpvoid_t arg2) { auto thread = kernel_state()->object_table()->LookupObject(thread_handle); @@ -1007,10 +1022,10 @@ void NtQueueApcThread(dword_t thread_handle, lpvoid_t apc_routine, } DECLARE_XBOXKRNL_EXPORT1(NtQueueApcThread, kThreading, kImplemented); -void KeInitializeApc(pointer_t apc, lpvoid_t thread_ptr, - lpvoid_t kernel_routine, lpvoid_t rundown_routine, - lpvoid_t normal_routine, dword_t processor_mode, - lpvoid_t normal_context) { +void KeInitializeApc_entry(pointer_t apc, lpvoid_t thread_ptr, + lpvoid_t kernel_routine, lpvoid_t rundown_routine, + lpvoid_t normal_routine, dword_t processor_mode, + lpvoid_t normal_context) { apc->Initialize(); apc->processor_mode = processor_mode; apc->thread_ptr = thread_ptr.guest_address(); @@ -1022,8 +1037,9 @@ void KeInitializeApc(pointer_t apc, lpvoid_t thread_ptr, } DECLARE_XBOXKRNL_EXPORT1(KeInitializeApc, kThreading, kImplemented); -dword_result_t KeInsertQueueApc(pointer_t apc, lpvoid_t arg1, - lpvoid_t arg2, dword_t priority_increment) { +dword_result_t KeInsertQueueApc_entry(pointer_t apc, lpvoid_t arg1, + lpvoid_t arg2, + dword_t priority_increment) { auto thread = XObject::GetNativeObject( kernel_state(), kernel_state()->memory()->TranslateVirtual(apc->thread_ptr)); @@ -1057,7 +1073,7 @@ dword_result_t KeInsertQueueApc(pointer_t apc, lpvoid_t arg1, } DECLARE_XBOXKRNL_EXPORT1(KeInsertQueueApc, kThreading, kImplemented); -dword_result_t KeRemoveQueueApc(pointer_t apc) { +dword_result_t KeRemoveQueueApc_entry(pointer_t apc) { bool result = false; auto thread = XObject::GetNativeObject( @@ -1087,8 +1103,8 @@ dword_result_t KeRemoveQueueApc(pointer_t apc) { } DECLARE_XBOXKRNL_EXPORT1(KeRemoveQueueApc, kThreading, kImplemented); -dword_result_t KiApcNormalRoutineNop(dword_t unk0 /* output? */, - dword_t unk1 /* 0x13 */) { +dword_result_t KiApcNormalRoutineNop_entry(dword_t unk0 /* output? */, + dword_t unk1 /* 0x13 */) { return 0; } DECLARE_XBOXKRNL_EXPORT1(KiApcNormalRoutineNop, kThreading, kStub); @@ -1103,7 +1119,8 @@ typedef struct { xe::be arg2; } XDPC; -void KeInitializeDpc(pointer_t dpc, lpvoid_t routine, lpvoid_t context) { +void KeInitializeDpc_entry(pointer_t dpc, lpvoid_t routine, + lpvoid_t context) { // KDPC (maybe) 0x18 bytes? uint32_t type = 19; // DpcObject uint32_t importance = 0; @@ -1118,8 +1135,8 @@ void KeInitializeDpc(pointer_t dpc, lpvoid_t routine, lpvoid_t context) { } DECLARE_XBOXKRNL_EXPORT2(KeInitializeDpc, kThreading, kImplemented, kSketchy); -dword_result_t KeInsertQueueDpc(pointer_t dpc, dword_t arg1, - dword_t arg2) { +dword_result_t KeInsertQueueDpc_entry(pointer_t dpc, dword_t arg1, + dword_t arg2) { assert_always("DPC does not dispatch yet; going to hang!"); uint32_t list_entry_ptr = dpc.guest_address() + 4; @@ -1143,7 +1160,7 @@ dword_result_t KeInsertQueueDpc(pointer_t dpc, dword_t arg1, } DECLARE_XBOXKRNL_EXPORT2(KeInsertQueueDpc, kThreading, kStub, kSketchy); -dword_result_t KeRemoveQueueDpc(pointer_t dpc) { +dword_result_t KeRemoveQueueDpc_entry(pointer_t dpc) { bool result = false; uint32_t list_entry_ptr = dpc.guest_address() + 4; @@ -1171,18 +1188,18 @@ struct X_ERWLOCK { }; static_assert_size(X_ERWLOCK, 0x38); -void ExInitializeReadWriteLock(pointer_t lock_ptr) { +void ExInitializeReadWriteLock_entry(pointer_t lock_ptr) { lock_ptr->lock_count = -1; lock_ptr->writers_waiting_count = 0; lock_ptr->readers_waiting_count = 0; lock_ptr->readers_entry_count = 0; - KeInitializeEvent(&lock_ptr->writer_event, 1, 0); - KeInitializeSemaphore(&lock_ptr->reader_semaphore, 0, 0x7FFFFFFF); + KeInitializeEvent_entry(&lock_ptr->writer_event, 1, 0); + KeInitializeSemaphore_entry(&lock_ptr->reader_semaphore, 0, 0x7FFFFFFF); lock_ptr->spin_lock = 0; } DECLARE_XBOXKRNL_EXPORT1(ExInitializeReadWriteLock, kThreading, kImplemented); -void ExAcquireReadWriteLockExclusive(pointer_t lock_ptr) { +void ExAcquireReadWriteLockExclusive_entry(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); int32_t lock_count = ++lock_ptr->lock_count; @@ -1199,7 +1216,7 @@ void ExAcquireReadWriteLockExclusive(pointer_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT2(ExAcquireReadWriteLockExclusive, kThreading, kImplemented, kBlocking); -dword_result_t ExTryToAcquireReadWriteLockExclusive( +dword_result_t ExTryToAcquireReadWriteLockExclusive_entry( pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); @@ -1217,7 +1234,7 @@ dword_result_t ExTryToAcquireReadWriteLockExclusive( DECLARE_XBOXKRNL_EXPORT1(ExTryToAcquireReadWriteLockExclusive, kThreading, kImplemented); -void ExAcquireReadWriteLockShared(pointer_t lock_ptr) { +void ExAcquireReadWriteLockShared_entry(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); int32_t lock_count = ++lock_ptr->lock_count; @@ -1236,7 +1253,7 @@ void ExAcquireReadWriteLockShared(pointer_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT2(ExAcquireReadWriteLockShared, kThreading, kImplemented, kBlocking); -dword_result_t ExTryToAcquireReadWriteLockShared( +dword_result_t ExTryToAcquireReadWriteLockShared_entry( pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); @@ -1256,7 +1273,7 @@ dword_result_t ExTryToAcquireReadWriteLockShared( DECLARE_XBOXKRNL_EXPORT1(ExTryToAcquireReadWriteLockShared, kThreading, kImplemented); -void ExReleaseReadWriteLock(pointer_t lock_ptr) { +void ExReleaseReadWriteLock_entry(pointer_t lock_ptr) { auto old_irql = xeKeKfAcquireSpinLock(&lock_ptr->spin_lock); int32_t lock_count = --lock_ptr->lock_count; @@ -1292,7 +1309,7 @@ void ExReleaseReadWriteLock(pointer_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT1(ExReleaseReadWriteLock, kThreading, kImplemented); // NOTE: This function is very commonly inlined, and probably won't be called! -pointer_result_t InterlockedPushEntrySList( +pointer_result_t InterlockedPushEntrySList_entry( pointer_t plist_ptr, pointer_t entry) { assert_not_null(plist_ptr); assert_not_null(entry); @@ -1317,7 +1334,8 @@ pointer_result_t InterlockedPushEntrySList( DECLARE_XBOXKRNL_EXPORT2(InterlockedPushEntrySList, kThreading, kImplemented, kHighFrequency); -pointer_result_t InterlockedPopEntrySList(pointer_t plist_ptr) { +pointer_result_t InterlockedPopEntrySList_entry( + pointer_t plist_ptr) { assert_not_null(plist_ptr); uint32_t popped = 0; @@ -1344,7 +1362,8 @@ pointer_result_t InterlockedPopEntrySList(pointer_t plist_ptr) { DECLARE_XBOXKRNL_EXPORT2(InterlockedPopEntrySList, kThreading, kImplemented, kHighFrequency); -pointer_result_t InterlockedFlushSList(pointer_t plist_ptr) { +pointer_result_t InterlockedFlushSList_entry( + pointer_t plist_ptr) { assert_not_null(plist_ptr); alignas(8) X_SLIST_HEADER old_hdr = *plist_ptr; diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc index 40c3f1987..671c20105 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,9 +17,9 @@ namespace xe { namespace kernel { namespace xboxkrnl { -dword_result_t XUsbcamCreate(dword_t buffer, - dword_t buffer_size, // 0x4B000 640x480? - lpunknown_t unk3_ptr) { +dword_result_t XUsbcamCreate_entry(dword_t buffer, + dword_t buffer_size, // 0x4B000 640x480? + lpunknown_t unk3_ptr) { // This function should return success. // It looks like it only allocates space for usbcam support. // returning error code might cause games to initialize incorrectly. @@ -31,7 +31,7 @@ dword_result_t XUsbcamCreate(dword_t buffer, } DECLARE_XBOXKRNL_EXPORT1(XUsbcamCreate, kNone, kStub); -dword_result_t XUsbcamGetState() { +dword_result_t XUsbcamGetState_entry() { // 0 = not connected. return 0; } diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc index e05415570..8ebacd35b 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -48,7 +48,7 @@ namespace xboxkrnl { // https://www.microsoft.com/en-za/download/details.aspx?id=5313 -- "Stripped // Down Direct3D: Xbox 360 Command Buffer and Resource Management" -void VdGetCurrentDisplayGamma(lpdword_t type_ptr, lpfloat_t power_ptr) { +void VdGetCurrentDisplayGamma_entry(lpdword_t type_ptr, lpfloat_t power_ptr) { // 1 - sRGB. // 2 - TV (BT.709). // 3 - use the power written to *power_ptr. @@ -104,9 +104,8 @@ struct X_DISPLAY_INFO { }; static_assert_size(X_DISPLAY_INFO, 0x58); -void VdQueryVideoMode(pointer_t video_mode); - -void VdGetCurrentDisplayInformation(pointer_t display_info) { +void VdGetCurrentDisplayInformation_entry( + pointer_t display_info) { X_VIDEO_MODE mode; VdQueryVideoMode(&mode); @@ -132,9 +131,9 @@ void VdGetCurrentDisplayInformation(pointer_t display_info) { } DECLARE_XBOXKRNL_EXPORT1(VdGetCurrentDisplayInformation, kVideo, kStub); -void VdQueryVideoMode(pointer_t video_mode) { +void VdQueryVideoMode(X_VIDEO_MODE* video_mode) { // TODO(benvanik): get info from actual display. - video_mode.Zero(); + std::memset(video_mode, 0, sizeof(X_VIDEO_MODE)); video_mode->display_width = 1280; video_mode->display_height = 720; video_mode->is_interlaced = 0; @@ -145,9 +144,13 @@ void VdQueryVideoMode(pointer_t video_mode) { video_mode->unknown_0x8a = 0x4A; video_mode->unknown_0x01 = 0x01; } + +void VdQueryVideoMode_entry(pointer_t video_mode) { + VdQueryVideoMode(video_mode); +} DECLARE_XBOXKRNL_EXPORT1(VdQueryVideoMode, kVideo, kStub); -dword_result_t VdQueryVideoFlags() { +dword_result_t VdQueryVideoFlags_entry() { X_VIDEO_MODE mode; VdQueryVideoMode(&mode); @@ -160,7 +163,7 @@ dword_result_t VdQueryVideoFlags() { } DECLARE_XBOXKRNL_EXPORT1(VdQueryVideoFlags, kVideo, kStub); -dword_result_t VdSetDisplayMode(dword_t flags) { +dword_result_t VdSetDisplayMode_entry(dword_t flags) { // Often 0x40000000. // 0?ccf000 00000000 00000000 000000r0 @@ -179,17 +182,17 @@ dword_result_t VdSetDisplayMode(dword_t flags) { } DECLARE_XBOXKRNL_EXPORT1(VdSetDisplayMode, kVideo, kStub); -dword_result_t VdSetDisplayModeOverride(unknown_t unk0, unknown_t unk1, - double_t refresh_rate, unknown_t unk3, - unknown_t unk4) { +dword_result_t VdSetDisplayModeOverride_entry(unknown_t unk0, unknown_t unk1, + double_t refresh_rate, + unknown_t unk3, unknown_t unk4) { // refresh_rate = 0, 50, 59.9, etc. return 0; } DECLARE_XBOXKRNL_EXPORT1(VdSetDisplayModeOverride, kVideo, kStub); -dword_result_t VdInitializeEngines(unknown_t unk0, function_t callback, - lpvoid_t arg, lpdword_t pfp_ptr, - lpdword_t me_ptr) { +dword_result_t VdInitializeEngines_entry(unknown_t unk0, function_t callback, + lpvoid_t arg, lpdword_t pfp_ptr, + lpdword_t me_ptr) { // r3 = 0x4F810000 // r4 = function ptr (cleanup callback?) // r5 = function arg @@ -199,27 +202,28 @@ dword_result_t VdInitializeEngines(unknown_t unk0, function_t callback, } DECLARE_XBOXKRNL_EXPORT1(VdInitializeEngines, kVideo, kStub); -void VdShutdownEngines() { +void VdShutdownEngines_entry() { // Ignored for now. // Games seem to call an Initialize/Shutdown pair to query info, then // re-initialize. } DECLARE_XBOXKRNL_EXPORT1(VdShutdownEngines, kVideo, kStub); -dword_result_t VdGetGraphicsAsicID() { +dword_result_t VdGetGraphicsAsicID_entry() { // Games compare for < 0x10 and do VdInitializeEDRAM, else other // (retrain/etc). return 0x11; } DECLARE_XBOXKRNL_EXPORT1(VdGetGraphicsAsicID, kVideo, kStub); -dword_result_t VdEnableDisableClockGating(dword_t enabled) { +dword_result_t VdEnableDisableClockGating_entry(dword_t enabled) { // Ignored, as it really doesn't matter. return 0; } DECLARE_XBOXKRNL_EXPORT1(VdEnableDisableClockGating, kVideo, kStub); -void VdSetGraphicsInterruptCallback(function_t callback, lpvoid_t user_data) { +void VdSetGraphicsInterruptCallback_entry(function_t callback, + lpvoid_t user_data) { // callback takes 2 params // r3 = bool 0/1 - 0 is normal interrupt, 1 is some acquire/lock mumble // r4 = user_data (r4 of VdSetGraphicsInterruptCallback) @@ -228,7 +232,7 @@ void VdSetGraphicsInterruptCallback(function_t callback, lpvoid_t user_data) { } DECLARE_XBOXKRNL_EXPORT1(VdSetGraphicsInterruptCallback, kVideo, kImplemented); -void VdInitializeRingBuffer(lpvoid_t ptr, int_t size_log2) { +void VdInitializeRingBuffer_entry(lpvoid_t ptr, int_t size_log2) { // r3 = result of MmGetPhysicalAddress // r4 = log2(size) // Buffer pointers are from MmAllocatePhysicalMemory with WRITE_COMBINE. @@ -237,21 +241,22 @@ void VdInitializeRingBuffer(lpvoid_t ptr, int_t size_log2) { } DECLARE_XBOXKRNL_EXPORT1(VdInitializeRingBuffer, kVideo, kImplemented); -void VdEnableRingBufferRPtrWriteBack(lpvoid_t ptr, int_t block_size_log2) { +void VdEnableRingBufferRPtrWriteBack_entry(lpvoid_t ptr, + int_t block_size_log2) { // r4 = log2(block size), 6, usually --- <=19 auto graphics_system = kernel_state()->emulator()->graphics_system(); graphics_system->EnableReadPointerWriteBack(ptr, block_size_log2); } DECLARE_XBOXKRNL_EXPORT1(VdEnableRingBufferRPtrWriteBack, kVideo, kImplemented); -void VdGetSystemCommandBuffer(lpunknown_t p0_ptr, lpunknown_t p1_ptr) { +void VdGetSystemCommandBuffer_entry(lpunknown_t p0_ptr, lpunknown_t p1_ptr) { p0_ptr.Zero(0x94); xe::store_and_swap(p0_ptr, 0xBEEF0000); xe::store_and_swap(p1_ptr, 0xBEEF0001); } DECLARE_XBOXKRNL_EXPORT1(VdGetSystemCommandBuffer, kVideo, kStub); -void VdSetSystemCommandBufferGpuIdentifierAddress(lpunknown_t unk) { +void VdSetSystemCommandBufferGpuIdentifierAddress_entry(lpunknown_t unk) { // r3 = 0x2B10(d3d?) + 8 } DECLARE_XBOXKRNL_EXPORT1(VdSetSystemCommandBufferGpuIdentifierAddress, kVideo, @@ -262,7 +267,7 @@ DECLARE_XBOXKRNL_EXPORT1(VdSetSystemCommandBufferGpuIdentifierAddress, kVideo, // r4 = 19 // no op? -dword_result_t VdInitializeScalerCommandBuffer( +dword_result_t VdInitializeScalerCommandBuffer_entry( dword_t scaler_source_xy, // ((uint16_t)y << 16) | (uint16_t)x dword_t scaler_source_wh, // ((uint16_t)h << 16) | (uint16_t)w dword_t scaled_output_xy, // ((uint16_t)y << 16) | (uint16_t)x @@ -302,7 +307,7 @@ void AppendParam(StringBuffer* string_buffer, pointer_t param) { uint16_t(param->fb_width), uint16_t(param->fb_height)); } -dword_result_t VdCallGraphicsNotificationRoutines( +dword_result_t VdCallGraphicsNotificationRoutines_entry( unknown_t unk0, pointer_t args_ptr) { assert_true(unk0 == 1); @@ -313,13 +318,13 @@ dword_result_t VdCallGraphicsNotificationRoutines( DECLARE_XBOXKRNL_EXPORT2(VdCallGraphicsNotificationRoutines, kVideo, kImplemented, kSketchy); -dword_result_t VdIsHSIOTrainingSucceeded() { +dword_result_t VdIsHSIOTrainingSucceeded_entry() { // BOOL return value return 1; } DECLARE_XBOXKRNL_EXPORT1(VdIsHSIOTrainingSucceeded, kVideo, kStub); -dword_result_t VdPersistDisplay(unknown_t unk0, lpdword_t unk1_ptr) { +dword_result_t VdPersistDisplay_entry(unknown_t unk0, lpdword_t unk1_ptr) { // unk1_ptr needs to be populated with a pointer passed to // MmFreePhysicalMemory(1, *unk1_ptr). if (unk1_ptr) { @@ -334,23 +339,25 @@ dword_result_t VdPersistDisplay(unknown_t unk0, lpdword_t unk1_ptr) { } DECLARE_XBOXKRNL_EXPORT2(VdPersistDisplay, kVideo, kImplemented, kSketchy); -dword_result_t VdRetrainEDRAMWorker(unknown_t unk0) { return 0; } +dword_result_t VdRetrainEDRAMWorker_entry(unknown_t unk0) { return 0; } DECLARE_XBOXKRNL_EXPORT1(VdRetrainEDRAMWorker, kVideo, kStub); -dword_result_t VdRetrainEDRAM(unknown_t unk0, unknown_t unk1, unknown_t unk2, - unknown_t unk3, unknown_t unk4, unknown_t unk5) { +dword_result_t VdRetrainEDRAM_entry(unknown_t unk0, unknown_t unk1, + unknown_t unk2, unknown_t unk3, + unknown_t unk4, unknown_t unk5) { return 0; } DECLARE_XBOXKRNL_EXPORT1(VdRetrainEDRAM, kVideo, kStub); -void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer - lpvoid_t fetch_ptr, // frontbuffer Direct3D 9 texture header fetch - lpunknown_t unk2, // system writeback ptr - lpunknown_t unk3, // buffer from VdGetSystemCommandBuffer - lpunknown_t unk4, // from VdGetSystemCommandBuffer (0xBEEF0001) - lpdword_t frontbuffer_ptr, // ptr to frontbuffer address - lpdword_t texture_format_ptr, lpdword_t color_space_ptr, - lpdword_t width, lpdword_t height) { +void VdSwap_entry( + lpvoid_t buffer_ptr, // ptr into primary ringbuffer + lpvoid_t fetch_ptr, // frontbuffer Direct3D 9 texture header fetch + lpunknown_t unk2, // system writeback ptr + lpunknown_t unk3, // buffer from VdGetSystemCommandBuffer + lpunknown_t unk4, // from VdGetSystemCommandBuffer (0xBEEF0001) + lpdword_t frontbuffer_ptr, // ptr to frontbuffer address + lpdword_t texture_format_ptr, lpdword_t color_space_ptr, lpdword_t width, + lpdword_t height) { // All of these parameters are REQUIRED. assert(buffer_ptr); assert(fetch_ptr); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_video.h b/src/xenia/kernel/xboxkrnl/xboxkrnl_video.h index 2d26c6bd3..4cb3861f4 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_video.h +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_video.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,7 +17,7 @@ namespace xe { namespace kernel { namespace xboxkrnl { -void VdQueryVideoMode(pointer_t video_mode); +void VdQueryVideoMode(X_VIDEO_MODE* video_mode); } // namespace xboxkrnl } // namespace kernel diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc index f6b0b09bd..afde2b82b 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2019 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -122,9 +122,10 @@ X_STATUS xeExGetXConfigSetting(uint16_t category, uint16_t setting, return X_STATUS_SUCCESS; } -dword_result_t ExGetXConfigSetting(word_t category, word_t setting, - lpdword_t buffer_ptr, word_t buffer_size, - lpword_t required_size_ptr) { +dword_result_t ExGetXConfigSetting_entry(word_t category, word_t setting, + lpvoid_t buffer_ptr, + word_t buffer_size, + lpword_t required_size_ptr) { uint16_t required_size = 0; X_STATUS result = xeExGetXConfigSetting(category, setting, buffer_ptr, buffer_size, &required_size); From 12ec7289893b3121b9bf054c2f3c6059b91af69e Mon Sep 17 00:00:00 2001 From: gibbed Date: Sun, 9 Jan 2022 13:29:47 -0600 Subject: [PATCH 32/88] [Kernel] Use tables for export groups. --- src/xenia/kernel/util/shim_utils.h | 14 +++++++++ src/xenia/kernel/xam/xam_avatar.cc | 2 ++ src/xenia/kernel/xam/xam_content.cc | 5 ++- src/xenia/kernel/xam/xam_content_aggregate.cc | 5 ++- src/xenia/kernel/xam/xam_content_device.cc | 5 ++- src/xenia/kernel/xam/xam_enum.cc | 5 ++- src/xenia/kernel/xam/xam_info.cc | 5 ++- src/xenia/kernel/xam/xam_input.cc | 5 ++- src/xenia/kernel/xam/xam_locale.cc | 3 +- src/xenia/kernel/xam/xam_module.cc | 1 + src/xenia/kernel/xam/xam_msg.cc | 5 ++- src/xenia/kernel/xam/xam_net.cc | 5 ++- src/xenia/kernel/xam/xam_notify.cc | 7 ++--- src/xenia/kernel/xam/xam_nui.cc | 5 ++- src/xenia/kernel/xam/xam_party.cc | 4 +-- src/xenia/kernel/xam/xam_task.cc | 5 ++- src/xenia/kernel/xam/xam_ui.cc | 5 ++- src/xenia/kernel/xam/xam_user.cc | 3 +- src/xenia/kernel/xam/xam_video.cc | 5 ++- src/xenia/kernel/xam/xam_voice.cc | 5 ++- src/xenia/kernel/xbdm/xbdm_misc.cc | 5 ++- src/xenia/kernel/xbdm/xbdm_module.cc | 7 +++-- .../kernel/xbdm/xbdm_module_export_groups.inc | 13 ++++++++ src/xenia/kernel/xbdm/xbdm_private.h | 9 ++++-- src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc | 7 ++--- .../kernel/xboxkrnl/xboxkrnl_audio_xma.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc | 7 ++--- src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc | 23 +++----------- .../xboxkrnl_module_export_groups.inc | 31 +++++++++++++++++++ src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_private.h | 28 +++-------------- src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc | 5 ++- .../kernel/xboxkrnl/xboxkrnl_threading.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc | 5 ++- src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc | 5 ++- 44 files changed, 151 insertions(+), 153 deletions(-) create mode 100644 src/xenia/kernel/xbdm/xbdm_module_export_groups.inc create mode 100644 src/xenia/kernel/xboxkrnl/xboxkrnl_module_export_groups.inc diff --git a/src/xenia/kernel/util/shim_utils.h b/src/xenia/kernel/util/shim_utils.h index e9e287fbc..3e05ad0aa 100644 --- a/src/xenia/kernel/util/shim_utils.h +++ b/src/xenia/kernel/util/shim_utils.h @@ -564,6 +564,11 @@ using xe::cpu::ExportTag; xe::cpu::ExportCategory::category) \ << xe::cpu::ExportTag::CategoryShift))); +#define DECLARE_EMPTY_REGISTER_EXPORTS(module_name, group_name) \ + void xe::kernel::module_name::Register##group_name##Exports( \ + xe::cpu::ExportResolver* export_resolver, \ + xe::kernel::KernelState* kernel_state) {} + #define DECLARE_XAM_EXPORT_(name, category, tags) \ DECLARE_EXPORT(xam, name, category, tags) #define DECLARE_XAM_EXPORT1(name, category, tag) \ @@ -572,11 +577,17 @@ using xe::cpu::ExportTag; DECLARE_EXPORT(xam, name, category, \ xe::cpu::ExportTag::tag1 | xe::cpu::ExportTag::tag2) +#define DECLARE_XAM_EMPTY_REGISTER_EXPORTS(group_name) \ + DECLARE_EMPTY_REGISTER_EXPORTS(xam, group_name) + #define DECLARE_XBDM_EXPORT_(name, category, tags) \ DECLARE_EXPORT(xbdm, name, category, tags) #define DECLARE_XBDM_EXPORT1(name, category, tag) \ DECLARE_EXPORT(xbdm, name, category, xe::cpu::ExportTag::tag) +#define DECLARE_XBDM_EMPTY_REGISTER_EXPORTS(group_name) \ + DECLARE_EMPTY_REGISTER_EXPORTS(xbdm, group_name) + #define DECLARE_XBOXKRNL_EXPORT_(name, category, tags) \ DECLARE_EXPORT(xboxkrnl, name, category, tags) #define DECLARE_XBOXKRNL_EXPORT1(name, category, tag) \ @@ -593,6 +604,9 @@ using xe::cpu::ExportTag; xe::cpu::ExportTag::tag1 | xe::cpu::ExportTag::tag2 | \ xe::cpu::ExportTag::tag3 | xe::cpu::ExportTag::tag4) +#define DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(group_name) \ + DECLARE_EMPTY_REGISTER_EXPORTS(xboxkrnl, group_name) + } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/xam/xam_avatar.cc b/src/xenia/kernel/xam/xam_avatar.cc index c105aaefe..0beabc37b 100644 --- a/src/xenia/kernel/xam/xam_avatar.cc +++ b/src/xenia/kernel/xam/xam_avatar.cc @@ -41,3 +41,5 @@ void RegisterAvatarExports(xe::cpu::ExportResolver* export_resolver, } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Avatar); diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index 5f4c12111..11a9cf52b 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -420,9 +420,8 @@ dword_result_t XamContentDeleteInternal_entry(lpvoid_t content_data_ptr, } DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented); -void RegisterContentExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Content); diff --git a/src/xenia/kernel/xam/xam_content_aggregate.cc b/src/xenia/kernel/xam/xam_content_aggregate.cc index 970d80652..eb41c29f7 100644 --- a/src/xenia/kernel/xam/xam_content_aggregate.cc +++ b/src/xenia/kernel/xam/xam_content_aggregate.cc @@ -139,9 +139,8 @@ dword_result_t XamContentAggregateCreateEnumerator_entry(qword_t xuid, } DECLARE_XAM_EXPORT1(XamContentAggregateCreateEnumerator, kContent, kStub); -void RegisterContentAggregateExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(ContentAggregate); diff --git a/src/xenia/kernel/xam/xam_content_device.cc b/src/xenia/kernel/xam/xam_content_device.cc index 04bf93326..7abe02123 100644 --- a/src/xenia/kernel/xam/xam_content_device.cc +++ b/src/xenia/kernel/xam/xam_content_device.cc @@ -167,9 +167,8 @@ dword_result_t XamContentCreateDeviceEnumerator_entry(dword_t content_type, } DECLARE_XAM_EXPORT1(XamContentCreateDeviceEnumerator, kNone, kImplemented); -void RegisterContentDeviceExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(ContentDevice); diff --git a/src/xenia/kernel/xam/xam_enum.cc b/src/xenia/kernel/xam/xam_enum.cc index eacec3630..9f5c08bb4 100644 --- a/src/xenia/kernel/xam/xam_enum.cc +++ b/src/xenia/kernel/xam/xam_enum.cc @@ -110,9 +110,8 @@ dword_result_t XamGetPrivateEnumStructureFromHandle_entry( } DECLARE_XAM_EXPORT1(XamGetPrivateEnumStructureFromHandle, kNone, kStub); -void RegisterEnumExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Enum); diff --git a/src/xenia/kernel/xam/xam_info.cc b/src/xenia/kernel/xam/xam_info.cc index 76d749841..0bfe7c97b 100644 --- a/src/xenia/kernel/xam/xam_info.cc +++ b/src/xenia/kernel/xam/xam_info.cc @@ -342,9 +342,8 @@ dword_result_t XamQueryLiveHiveW_entry(lpu16string_t name, lpvoid_t out_buf, } DECLARE_XAM_EXPORT1(XamQueryLiveHiveW, kNone, kStub); -void RegisterInfoExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Info); diff --git a/src/xenia/kernel/xam/xam_input.cc b/src/xenia/kernel/xam/xam_input.cc index 413b797a0..2d177e98b 100644 --- a/src/xenia/kernel/xam/xam_input.cc +++ b/src/xenia/kernel/xam/xam_input.cc @@ -194,9 +194,8 @@ X_HRESULT_result_t XamUserGetDeviceContext_entry(dword_t user_index, } DECLARE_XAM_EXPORT1(XamUserGetDeviceContext, kInput, kStub); -void RegisterInputExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Input); diff --git a/src/xenia/kernel/xam/xam_locale.cc b/src/xenia/kernel/xam/xam_locale.cc index 573b41086..e45c771ad 100644 --- a/src/xenia/kernel/xam/xam_locale.cc +++ b/src/xenia/kernel/xam/xam_locale.cc @@ -436,5 +436,4 @@ DECLARE_XAM_EXPORT1(XamGetLocaleEx, kLocale, kImplemented); } // namespace kernel } // namespace xe -void xe::kernel::xam::RegisterLocaleExports( - xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) {} +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Locale); diff --git a/src/xenia/kernel/xam/xam_module.cc b/src/xenia/kernel/xam/xam_module.cc index 7689d3bf8..a94b1d6ef 100644 --- a/src/xenia/kernel/xam/xam_module.cc +++ b/src/xenia/kernel/xam/xam_module.cc @@ -27,6 +27,7 @@ XamModule::XamModule(Emulator* emulator, KernelState* kernel_state) : KernelModule(kernel_state, "xe:\\xam.xex"), loader_data_() { RegisterExportTable(export_resolver_); + // Register all exported functions. #define XE_MODULE_EXPORT_GROUP(m, n) \ Register##n##Exports(export_resolver_, kernel_state_); #include "xam_module_export_groups.inc" diff --git a/src/xenia/kernel/xam/xam_msg.cc b/src/xenia/kernel/xam/xam_msg.cc index 3251b0d05..3be2f7ed1 100644 --- a/src/xenia/kernel/xam/xam_msg.cc +++ b/src/xenia/kernel/xam/xam_msg.cc @@ -135,9 +135,8 @@ dword_result_t XamGetOverlappedResult_entry( } DECLARE_XAM_EXPORT2(XamGetOverlappedResult, kNone, kImplemented, kSketchy); -void RegisterMsgExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Msg); diff --git a/src/xenia/kernel/xam/xam_net.cc b/src/xenia/kernel/xam/xam_net.cc index 67af4622c..a331f139d 100644 --- a/src/xenia/kernel/xam/xam_net.cc +++ b/src/xenia/kernel/xam/xam_net.cc @@ -1031,9 +1031,8 @@ void NetDll_WSASetLastError_entry(dword_t error_code) { } DECLARE_XAM_EXPORT1(NetDll_WSASetLastError, kNetworking, kImplemented); -void RegisterNetExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Net); diff --git a/src/xenia/kernel/xam/xam_notify.cc b/src/xenia/kernel/xam/xam_notify.cc index dc28466e9..71bc59807 100644 --- a/src/xenia/kernel/xam/xam_notify.cc +++ b/src/xenia/kernel/xam/xam_notify.cc @@ -102,9 +102,8 @@ void XNotifyPositionUI_entry(dword_t position) { } DECLARE_XAM_EXPORT1(XNotifyPositionUI, kNone, kStub); -void RegisterNotifyExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel -} // namespace xe \ No newline at end of file +} // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Notify); diff --git a/src/xenia/kernel/xam/xam_nui.cc b/src/xenia/kernel/xam/xam_nui.cc index 725517111..48ab0d946 100644 --- a/src/xenia/kernel/xam/xam_nui.cc +++ b/src/xenia/kernel/xam/xam_nui.cc @@ -66,9 +66,8 @@ dword_result_t XamShowNuiTroubleshooterUI_entry(unknown_t unk1, unknown_t unk2, } DECLARE_XAM_EXPORT1(XamShowNuiTroubleshooterUI, kNone, kStub); -void RegisterNUIExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(NUI); diff --git a/src/xenia/kernel/xam/xam_party.cc b/src/xenia/kernel/xam/xam_party.cc index 5e74dca02..d278be996 100644 --- a/src/xenia/kernel/xam/xam_party.cc +++ b/src/xenia/kernel/xam/xam_party.cc @@ -40,8 +40,8 @@ dword_result_t XamPartyGetBandwidth_entry(dword_t r3, dword_t r4) { } DECLARE_XAM_EXPORT1(XamPartyGetBandwidth, kNone, kStub); -void RegisterPartyExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Party); diff --git a/src/xenia/kernel/xam/xam_task.cc b/src/xenia/kernel/xam/xam_task.cc index 82ae23025..7fe6eb4d3 100644 --- a/src/xenia/kernel/xam/xam_task.cc +++ b/src/xenia/kernel/xam/xam_task.cc @@ -72,9 +72,8 @@ DECLARE_XAM_EXPORT2(XamTaskSchedule, kNone, kImplemented, kSketchy); dword_result_t XamTaskShouldExit_entry(dword_t r3) { return 0; } DECLARE_XAM_EXPORT2(XamTaskShouldExit, kNone, kStub, kSketchy); -void RegisterTaskExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Task); diff --git a/src/xenia/kernel/xam/xam_ui.cc b/src/xenia/kernel/xam/xam_ui.cc index 4fc28fd7d..2762facf3 100644 --- a/src/xenia/kernel/xam/xam_ui.cc +++ b/src/xenia/kernel/xam/xam_ui.cc @@ -503,9 +503,8 @@ dword_result_t XamShowCommunitySessionsUI_entry(unknown_t r3, unknown_t r4) { } DECLARE_XAM_EXPORT1(XamShowCommunitySessionsUI, kNone, kStub); -void RegisterUIExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(UI); diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 6da2d1635..625b977cd 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -788,5 +788,4 @@ DECLARE_XAM_EXPORT1(XamSessionRefObjByHandle, kUserProfiles, kStub); } // namespace kernel } // namespace xe -void xe::kernel::xam::RegisterUserExports( - xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) {} +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(User); diff --git a/src/xenia/kernel/xam/xam_video.cc b/src/xenia/kernel/xam/xam_video.cc index 06a1d0029..37c9d6948 100644 --- a/src/xenia/kernel/xam/xam_video.cc +++ b/src/xenia/kernel/xam/xam_video.cc @@ -27,9 +27,8 @@ DECLARE_XAM_EXPORT1(XGetVideoMode, kVideo, ExportTag::kSketchy); dword_result_t XGetVideoCapabilities_entry() { return 0; } DECLARE_XAM_EXPORT1(XGetVideoCapabilities, kVideo, kStub); -void RegisterVideoExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Video); diff --git a/src/xenia/kernel/xam/xam_voice.cc b/src/xenia/kernel/xam/xam_voice.cc index 3d3379424..dbe87db3d 100644 --- a/src/xenia/kernel/xam/xam_voice.cc +++ b/src/xenia/kernel/xam/xam_voice.cc @@ -38,9 +38,8 @@ DECLARE_XAM_EXPORT1(XamVoiceClose, kNone, kStub); dword_result_t XamVoiceHeadsetPresent_entry(lpunknown_t voice_ptr) { return 0; } DECLARE_XAM_EXPORT1(XamVoiceHeadsetPresent, kNone, kStub); -void RegisterVoiceExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe + +DECLARE_XAM_EMPTY_REGISTER_EXPORTS(Voice); diff --git a/src/xenia/kernel/xbdm/xbdm_misc.cc b/src/xenia/kernel/xbdm/xbdm_misc.cc index 8db457c70..7f2b9f960 100644 --- a/src/xenia/kernel/xbdm/xbdm_misc.cc +++ b/src/xenia/kernel/xbdm/xbdm_misc.cc @@ -86,9 +86,8 @@ dword_result_t DmFindPdbSignature_entry(lpdword_t unk0_ptr, } DECLARE_XBDM_EXPORT1(DmFindPdbSignature, kDebug, kStub); -void RegisterMiscExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xbdm } // namespace kernel } // namespace xe + +DECLARE_XBDM_EMPTY_REGISTER_EXPORTS(Misc); diff --git a/src/xenia/kernel/xbdm/xbdm_module.cc b/src/xenia/kernel/xbdm/xbdm_module.cc index 5e22aae43..c744e4b68 100644 --- a/src/xenia/kernel/xbdm/xbdm_module.cc +++ b/src/xenia/kernel/xbdm/xbdm_module.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -24,7 +24,10 @@ XbdmModule::XbdmModule(Emulator* emulator, KernelState* kernel_state) RegisterExportTable(export_resolver_); // Register all exported functions. - RegisterMiscExports(export_resolver_, kernel_state_); +#define XE_MODULE_EXPORT_GROUP(m, n) \ + Register##n##Exports(export_resolver_, kernel_state_); +#include "xbdm_module_export_groups.inc" +#undef XE_MODULE_EXPORT_GROUP } std::vector xbdm_exports(4096); diff --git a/src/xenia/kernel/xbdm/xbdm_module_export_groups.inc b/src/xenia/kernel/xbdm/xbdm_module_export_groups.inc new file mode 100644 index 000000000..f47842d3d --- /dev/null +++ b/src/xenia/kernel/xbdm/xbdm_module_export_groups.inc @@ -0,0 +1,13 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +// This is a partial file designed to be included by other files when +// constructing various tables. + +XE_MODULE_EXPORT_GROUP(xbdm, Misc) diff --git a/src/xenia/kernel/xbdm/xbdm_private.h b/src/xenia/kernel/xbdm/xbdm_private.h index 3f8619204..9c2e70515 100644 --- a/src/xenia/kernel/xbdm/xbdm_private.h +++ b/src/xenia/kernel/xbdm/xbdm_private.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -21,8 +21,11 @@ namespace xbdm { xe::cpu::Export* RegisterExport_xbdm(xe::cpu::Export* export_entry); // Registration functions, one per file. -void RegisterMiscExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state); +#define XE_MODULE_EXPORT_GROUP(m, n) \ + void Register##n##Exports(xe::cpu::ExportResolver* export_resolver, \ + KernelState* kernel_state); +#include "xbdm_module_export_groups.inc" +#undef XE_MODULE_EXPORT_GROUP } // namespace xbdm } // namespace kernel diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc index cbf7e3d64..3e9c39cbd 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc @@ -96,11 +96,8 @@ dword_result_t XAudioSubmitRenderDriverFrame_entry(lpunknown_t driver_ptr, DECLARE_XBOXKRNL_EXPORT2(XAudioSubmitRenderDriverFrame, kAudio, kImplemented, kHighFrequency); -void RegisterAudioExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) { - // Additional XMA* methods are in xboxkrnl_audio_xma.cc. -} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Audio); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc index f0b264778..66e988ea0 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_audio_xma.cc @@ -392,9 +392,8 @@ dword_result_t XMABlockWhileInUse_entry(lpvoid_t context_ptr) { DECLARE_XBOXKRNL_EXPORT2(XMABlockWhileInUse, kAudio, kImplemented, kHighFrequency); -void RegisterAudioXmaExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(AudioXma); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc index 64961fb3a..6d98bc01d 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_crypt.cc @@ -686,9 +686,8 @@ dword_result_t XeKeysHmacShaUsingKey_entry(lpvoid_t obscured_key, } DECLARE_XBOXKRNL_EXPORT1(XeKeysHmacShaUsingKey, kNone, kImplemented); -void RegisterCryptExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Crypt); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc index 0eeb8f8f7..4470cd36e 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc @@ -154,9 +154,8 @@ DECLARE_XBOXKRNL_EXPORT2(KeBugCheckEx, kDebug, kStub, kImportant); void KeBugCheck_entry(dword_t code) { KeBugCheckEx_entry(code, 0, 0, 0, 0); } DECLARE_XBOXKRNL_EXPORT2(KeBugCheck, kDebug, kImplemented, kImportant); -void RegisterDebugExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Debug); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc index f1f9938d1..c3c9ddb11 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc @@ -1017,9 +1017,8 @@ dword_result_t RtlNtStatusToDosError_entry(dword_t source_status) { DECLARE_XBOXKRNL_EXPORT3(RtlNtStatusToDosError, kNone, kImportant, kHighFrequency, kLogResult); -void RegisterErrorExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Error); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc index 8cf3a1cbb..b173fd304 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc @@ -31,9 +31,8 @@ void HalReturnToFirmware_entry(dword_t routine) { } DECLARE_XBOXKRNL_EXPORT2(HalReturnToFirmware, kNone, kStub, kImportant); -void RegisterHalExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Hal); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc index 0234ee062..15e60161e 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_hid.cc @@ -32,9 +32,8 @@ dword_result_t HidReadKeys_entry(dword_t unk1, unknown_t unk2, unknown_t unk3) { } DECLARE_XBOXKRNL_EXPORT1(HidReadKeys, kInput, kStub); -void RegisterHidExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Hid); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index 010dcf22b..2e2022bc6 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -735,9 +735,8 @@ dword_result_t IoCreateDevice_entry(dword_t device_struct, dword_t r4, } DECLARE_XBOXKRNL_EXPORT1(IoCreateDevice, kFileSystem, kStub); -void RegisterIoExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Io); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc index 2b9c9832b..1ace11c97 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc @@ -357,9 +357,8 @@ dword_result_t NtQueryVolumeInformationFile_entry( DECLARE_XBOXKRNL_EXPORT1(NtQueryVolumeInformationFile, kFileSystem, kImplemented); -void RegisterIoInfoExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(IoInfo); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc index 97c2875d7..7d72b3215 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc @@ -649,9 +649,8 @@ dword_result_t MmDeleteKernelStack_entry(lpvoid_t stack_base, } DECLARE_XBOXKRNL_EXPORT1(MmDeleteKernelStack, kMemory, kImplemented); -void RegisterMemoryExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel -} // namespace xe \ No newline at end of file +} // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Memory); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc index 9f80c9d6d..7a100665a 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_misc.cc @@ -23,9 +23,8 @@ void KeEnableFpuExceptions_entry(dword_t enabled) { } DECLARE_XBOXKRNL_EXPORT1(KeEnableFpuExceptions, kNone, kStub); -void RegisterMiscExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Misc); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc index f6b9846e0..f70f30bd0 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc @@ -83,25 +83,10 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) RegisterExportTable(export_resolver_); // Register all exported functions. - RegisterAudioExports(export_resolver_, kernel_state_); - RegisterAudioXmaExports(export_resolver_, kernel_state_); - RegisterCryptExports(export_resolver_, kernel_state_); - RegisterDebugExports(export_resolver_, kernel_state_); - RegisterErrorExports(export_resolver_, kernel_state_); - RegisterHalExports(export_resolver_, kernel_state_); - RegisterHidExports(export_resolver_, kernel_state_); - RegisterIoExports(export_resolver_, kernel_state_); - RegisterIoInfoExports(export_resolver_, kernel_state_); - RegisterMemoryExports(export_resolver_, kernel_state_); - RegisterMiscExports(export_resolver_, kernel_state_); - RegisterModuleExports(export_resolver_, kernel_state_); - RegisterObExports(export_resolver_, kernel_state_); - RegisterRtlExports(export_resolver_, kernel_state_); - RegisterStringExports(export_resolver_, kernel_state_); - RegisterThreadingExports(export_resolver_, kernel_state_); - RegisterUsbcamExports(export_resolver_, kernel_state_); - RegisterVideoExports(export_resolver_, kernel_state_); - RegisterXConfigExports(export_resolver_, kernel_state_); +#define XE_MODULE_EXPORT_GROUP(m, n) \ + Register##n##Exports(export_resolver_, kernel_state_); +#include "xboxkrnl_module_export_groups.inc" +#undef XE_MODULE_EXPORT_GROUP // KeDebugMonitorData (?*) // Set to a valid value when a remote debugger is attached. diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_module_export_groups.inc b/src/xenia/kernel/xboxkrnl/xboxkrnl_module_export_groups.inc new file mode 100644 index 000000000..2506c9004 --- /dev/null +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_module_export_groups.inc @@ -0,0 +1,31 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +// This is a partial file designed to be included by other files when +// constructing various tables. + +XE_MODULE_EXPORT_GROUP(xboxkrnl, Audio) +XE_MODULE_EXPORT_GROUP(xboxkrnl, AudioXma) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Crypt) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Debug) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Error) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Hal) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Hid) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Io) +XE_MODULE_EXPORT_GROUP(xboxkrnl, IoInfo) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Memory) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Misc) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Module) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Ob) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Rtl) +XE_MODULE_EXPORT_GROUP(xboxkrnl, String) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Threading) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Usbcam) +XE_MODULE_EXPORT_GROUP(xboxkrnl, Video) +XE_MODULE_EXPORT_GROUP(xboxkrnl, XConfig) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc index 02380283d..b17e4b2a2 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc @@ -199,9 +199,8 @@ void ExRegisterTitleTerminateNotification_entry( DECLARE_XBOXKRNL_EXPORT1(ExRegisterTitleTerminateNotification, kModules, kImplemented); -void RegisterModuleExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Module); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc index 1937d85af..1eec0e430 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_ob.cc @@ -201,9 +201,8 @@ dword_result_t NtClose_entry(dword_t handle) { } DECLARE_XBOXKRNL_EXPORT1(NtClose, kNone, kImplemented); -void RegisterObExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Ob); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_private.h b/src/xenia/kernel/xboxkrnl/xboxkrnl_private.h index d1f081518..379577176 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_private.h +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_private.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -21,29 +21,11 @@ namespace xboxkrnl { xe::cpu::Export* RegisterExport_xboxkrnl(xe::cpu::Export* export_entry); // Registration functions, one per file. -#define DECLARE_REGISTER_EXPORTS(n) \ +#define XE_MODULE_EXPORT_GROUP(m, n) \ void Register##n##Exports(xe::cpu::ExportResolver* export_resolver, \ - KernelState* kernel_state) -DECLARE_REGISTER_EXPORTS(Audio); -DECLARE_REGISTER_EXPORTS(AudioXma); -DECLARE_REGISTER_EXPORTS(Crypt); -DECLARE_REGISTER_EXPORTS(Debug); -DECLARE_REGISTER_EXPORTS(Error); -DECLARE_REGISTER_EXPORTS(Hal); -DECLARE_REGISTER_EXPORTS(Hid); -DECLARE_REGISTER_EXPORTS(Io); -DECLARE_REGISTER_EXPORTS(IoInfo); -DECLARE_REGISTER_EXPORTS(Memory); -DECLARE_REGISTER_EXPORTS(Misc); -DECLARE_REGISTER_EXPORTS(Module); -DECLARE_REGISTER_EXPORTS(Ob); -DECLARE_REGISTER_EXPORTS(Rtl); -DECLARE_REGISTER_EXPORTS(String); -DECLARE_REGISTER_EXPORTS(Threading); -DECLARE_REGISTER_EXPORTS(Usbcam); -DECLARE_REGISTER_EXPORTS(Video); -DECLARE_REGISTER_EXPORTS(XConfig); -#undef DECLARE_REGISTER_EXPORTS + KernelState* kernel_state); +#include "xboxkrnl_module_export_groups.inc" +#undef XE_MODULE_EXPORT_GROUP } // namespace xboxkrnl } // namespace kernel diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc index e23acb234..adb07c81b 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc @@ -624,9 +624,8 @@ dword_result_t RtlComputeCrc32_entry(dword_t seed, lpvoid_t buffer, } DECLARE_XBOXKRNL_EXPORT1(RtlComputeCrc32, kNone, kImplemented); -void RegisterRtlExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Rtl); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index c2c968218..f913643eb 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -1383,9 +1383,8 @@ pointer_result_t InterlockedFlushSList_entry( } DECLARE_XBOXKRNL_EXPORT1(InterlockedFlushSList, kThreading, kImplemented); -void RegisterThreadingExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Threading); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc index 671c20105..2fc29e5ed 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_usbcam.cc @@ -37,9 +37,8 @@ dword_result_t XUsbcamGetState_entry() { } DECLARE_XBOXKRNL_EXPORT1(XUsbcamGetState, kNone, kStub); -void RegisterUsbcamExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Usbcam); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc index afde2b82b..8c8b009c7 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_xconfig.cc @@ -138,9 +138,8 @@ dword_result_t ExGetXConfigSetting_entry(word_t category, word_t setting, } DECLARE_XBOXKRNL_EXPORT1(ExGetXConfigSetting, kModules, kImplemented); -void RegisterXConfigExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xboxkrnl } // namespace kernel } // namespace xe + +DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(XConfig); From 975eadf17e8a4af93530d595ce06b14c8be7209b Mon Sep 17 00:00:00 2001 From: gibbed Date: Sun, 9 Jan 2022 13:33:30 -0600 Subject: [PATCH 33/88] [Kernel] Assert export function return/arg types. --- src/xenia/kernel/util/shim_utils.h | 26 ++++++++++++++++++-------- src/xenia/kernel/xam/xam_avatar.cc | 3 --- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/xenia/kernel/util/shim_utils.h b/src/xenia/kernel/util/shim_utils.h index 3e05ad0aa..3233ec67e 100644 --- a/src/xenia/kernel/util/shim_utils.h +++ b/src/xenia/kernel/util/shim_utils.h @@ -324,15 +324,20 @@ class TypedPointerParam : public ParamBase { T* host_ptr_; }; -template class Result { public: - Result(T value) : value_(value) {} + virtual void Store(PPCContext* ppc_context) = 0; +}; + +template +class ResultBase : public Result { + public: + ResultBase(T value) : value_(value) {} void Store(PPCContext* ppc_context) { ppc_context->r[3] = uint64_t(int32_t(value_)); } - Result() = delete; - Result& operator=(const Result&) = delete; + ResultBase() = delete; + ResultBase& operator=(const ResultBase&) = delete; operator T() const { return value_; } private: @@ -361,10 +366,10 @@ using lpunknown_t = const shim::PointerParam&; template using pointer_t = const shim::TypedPointerParam&; -using int_result_t = shim::Result; -using dword_result_t = shim::Result; -using pointer_result_t = shim::Result; -using X_HRESULT_result_t = shim::Result; +using int_result_t = shim::ResultBase; +using dword_result_t = shim::ResultBase; +using pointer_result_t = shim::ResultBase; +using X_HRESULT_result_t = shim::ResultBase; // Exported from kernel_state.cc. KernelState* kernel_state(); @@ -511,6 +516,11 @@ auto KernelTrampoline(F&& f, Tuple&& t, std::index_sequence) { template xe::cpu::Export* RegisterExport(R (*fn)(Ps&...), const char* name, xe::cpu::ExportTag::type tags) { + static_assert( + std::is_void::value || std::is_base_of::value, + "R must be void or derive from shim::Result"); + static_assert((std::is_base_of_v && ...), + "Ps must derive from shim::Param"); static const auto export_entry = new cpu::Export( ORDINAL, xe::cpu::Export::Type::kFunction, name, tags | xe::cpu::ExportTag::kImplemented | xe::cpu::ExportTag::kLog); diff --git a/src/xenia/kernel/xam/xam_avatar.cc b/src/xenia/kernel/xam/xam_avatar.cc index 0beabc37b..c3c8917ff 100644 --- a/src/xenia/kernel/xam/xam_avatar.cc +++ b/src/xenia/kernel/xam/xam_avatar.cc @@ -35,9 +35,6 @@ void XamAvatarShutdown_entry() { } DECLARE_XAM_EXPORT1(XamAvatarShutdown, kAvatars, kStub); -void RegisterAvatarExports(xe::cpu::ExportResolver* export_resolver, - KernelState* kernel_state) {} - } // namespace xam } // namespace kernel } // namespace xe From 37aa3d129cdf43e5b843cfba74f719bb6f2ab319 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 10 Jan 2022 13:37:19 -0800 Subject: [PATCH 34/88] [x64] Explicitly handle AND_NOT `dest == src1` This addresses a JIT-issue in the case that the `src1` and `dest` register are both the same. This issue only happens in the "generic" x86 path but not in the BMI1-accelerated path. Thanks Rick for the extensive debugging help. When `src1` and `dest` were the same, then the `addc` instruction at `82099A08` in title `584108FF` might emit the following assembly: ``` .text:82099A08 andc r11, r10, r11 | | Jitted | V 00000000A0011B15 mov rbx,r10 00000000A0011B18 not rbx 00000000A0011B1B and rbx,rbx ``` This was due to the src1 operand and the destination register being the same, which used to call the "else" case in the x64 emitter when it needs to be handled explicitly due to register aliasing/allocation. Addresses issue #1945 --- src/xenia/cpu/backend/x64/x64_sequences.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index 638e63364..62f6e9a3f 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -2688,6 +2688,11 @@ void EmitAndNotXX(X64Emitter& e, const ARGS& i) { if (i.dest == i.src2) { e.not_(i.dest); e.and_(i.dest, i.src1); + } else if (i.dest == i.src1) { + auto temp = GetTempReg(e); + e.mov(temp, i.src2); + e.not_(temp); + e.and_(i.dest, temp); } else { e.mov(i.dest, i.src2); e.not_(i.dest); From 233ed107fe3bb4532e85b8e558a51c67c4becc64 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 10 Jan 2022 16:18:55 -0800 Subject: [PATCH 35/88] [CPU] Remove `use_haswell_instructions` in favor of `x64_extension_mask` Rather than having a single bool to conditionally detect haswell-level instruction features. The granularity is increased with a new `x64_extension_mask` where individual features within the x64 backend can be turned on or off in a bit-mask manner. Since we have an ARM backend on the horizon, I've added this to the new `x64` configuration-group rather than `CPU`. This new pattern will hopefully allow for testing to be more targetted to certain processor features and allows the user to determine if they want certain features to be enabled or disabled(such as avoiding BMI2 on certain AMD processors due to pdep/pext being incredibly slow). The default configuration is to detect and utilize all available features. --- src/xenia/cpu/backend/x64/x64_backend.cc | 23 +++++++++++++++---- src/xenia/cpu/backend/x64/x64_backend.h | 2 +- src/xenia/cpu/backend/x64/x64_emitter.cc | 13 +++++++++-- src/xenia/cpu/backend/x64/x64_emitter.h | 22 +++++++++--------- src/xenia/cpu/ppc/testing/ppc_testing_main.cc | 3 +-- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_backend.cc b/src/xenia/cpu/backend/x64/x64_backend.cc index 130d84456..fe1326920 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.cc +++ b/src/xenia/cpu/backend/x64/x64_backend.cc @@ -26,10 +26,23 @@ #include "xenia/cpu/processor.h" #include "xenia/cpu/stack_walker.h" -DEFINE_bool( - use_haswell_instructions, true, - "Uses the AVX2/FMA/etc instructions on Haswell processors when available.", - "CPU"); +DEFINE_int32(x64_extension_mask, -1, + "Allow the detection and utilization of specific instruction set " + "features.\n" + " 0 = x86_64 + AVX1\n" + " 1 = AVX2\n" + " 2 = FMA\n" + " 4 = LZCNT\n" + " 8 = BMI1\n" + " 16 = BMI2\n" + " 32 = F16C\n" + " 64 = Movbe\n" + " 128 = AVX512F\n" + " 256 = AVX512VL\n" + " 512 = AVX512BW\n" + " 1024 = AVX512DQ\n" + " -1 = Detect and utilize all possible processor features\n", + "x64"); namespace xe { namespace cpu { @@ -84,7 +97,7 @@ bool X64Backend::Initialize(Processor* processor) { } // Need movbe to do advanced LOAD/STORE tricks. - if (cvars::use_haswell_instructions) { + if (cvars::x64_extension_mask & kX64EmitMovbe) { machine_info_.supports_extended_load_store = cpu.has(Xbyak::util::Cpu::tMOVBE); } else { diff --git a/src/xenia/cpu/backend/x64/x64_backend.h b/src/xenia/cpu/backend/x64/x64_backend.h index d3036e875..4cb69e040 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.h +++ b/src/xenia/cpu/backend/x64/x64_backend.h @@ -15,7 +15,7 @@ #include "xenia/base/cvar.h" #include "xenia/cpu/backend/backend.h" -DECLARE_bool(use_haswell_instructions); +DECLARE_int32(x64_extension_mask); namespace xe { class Exception; diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index ae9ba1eed..d555ff7eb 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -74,23 +74,32 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) backend_(backend), code_cache_(backend->code_cache()), allocator_(allocator) { - if (cvars::use_haswell_instructions) { + if (cvars::x64_extension_mask & kX64EmitAVX2) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tAVX2) ? kX64EmitAVX2 : 0; + if (cvars::x64_extension_mask & kX64EmitFMA) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tFMA) ? kX64EmitFMA : 0; + if (cvars::x64_extension_mask & kX64EmitLZCNT) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tLZCNT) ? kX64EmitLZCNT : 0; + if (cvars::x64_extension_mask & kX64EmitBMI1) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tBMI1) ? kX64EmitBMI1 : 0; + if (cvars::x64_extension_mask & kX64EmitBMI2) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tBMI2) ? kX64EmitBMI2 : 0; + if (cvars::x64_extension_mask & kX64EmitF16C) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tF16C) ? kX64EmitF16C : 0; + if (cvars::x64_extension_mask & kX64EmitMovbe) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tMOVBE) ? kX64EmitMovbe : 0; + if (cvars::x64_extension_mask & kX64EmitAVX512F) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tAVX512F) ? kX64EmitAVX512F : 0; + if (cvars::x64_extension_mask & kX64EmitAVX512VL) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tAVX512VL) ? kX64EmitAVX512VL : 0; + if (cvars::x64_extension_mask & kX64EmitAVX512BW) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tAVX512BW) ? kX64EmitAVX512BW : 0; + if (cvars::x64_extension_mask & kX64EmitAVX512DQ) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tAVX512DQ) ? kX64EmitAVX512DQ : 0; - } if (!cpu_.has(Xbyak::util::Cpu::tAVX)) { xe::FatalError( diff --git a/src/xenia/cpu/backend/x64/x64_emitter.h b/src/xenia/cpu/backend/x64/x64_emitter.h index b84162d34..be8cd0b1a 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.h +++ b/src/xenia/cpu/backend/x64/x64_emitter.h @@ -125,19 +125,19 @@ class XbyakAllocator : public Xbyak::Allocator { }; enum X64EmitterFeatureFlags { - kX64EmitAVX2 = 1 << 1, - kX64EmitFMA = 1 << 2, - kX64EmitLZCNT = 1 << 3, - kX64EmitBMI1 = 1 << 4, - kX64EmitBMI2 = 1 << 5, - kX64EmitF16C = 1 << 6, - kX64EmitMovbe = 1 << 7, + kX64EmitAVX2 = 1 << 0, + kX64EmitFMA = 1 << 1, + kX64EmitLZCNT = 1 << 2, + kX64EmitBMI1 = 1 << 3, + kX64EmitBMI2 = 1 << 4, + kX64EmitF16C = 1 << 5, + kX64EmitMovbe = 1 << 6, - kX64EmitAVX512F = 1 << 8, - kX64EmitAVX512VL = 1 << 9, + kX64EmitAVX512F = 1 << 7, + kX64EmitAVX512VL = 1 << 8, - kX64EmitAVX512BW = 1 << 10, - kX64EmitAVX512DQ = 1 << 11, + kX64EmitAVX512BW = 1 << 9, + kX64EmitAVX512DQ = 1 << 10, kX64EmitAVX512Ortho = kX64EmitAVX512F | kX64EmitAVX512VL, kX64EmitAVX512Ortho64 = kX64EmitAVX512Ortho | kX64EmitAVX512DQ diff --git a/src/xenia/cpu/ppc/testing/ppc_testing_main.cc b/src/xenia/cpu/ppc/testing/ppc_testing_main.cc index dbd184327..a39c41bd1 100644 --- a/src/xenia/cpu/ppc/testing/ppc_testing_main.cc +++ b/src/xenia/cpu/ppc/testing/ppc_testing_main.cc @@ -422,8 +422,7 @@ bool RunTests(const std::string_view test_name) { int failed_count = 0; int passed_count = 0; - XELOGI("Haswell instruction usage {}.", - cvars::use_haswell_instructions ? "enabled" : "disabled"); + XELOGI("Instruction feature mask {}.", cvars::x64_extension_mask); auto test_path_root = cvars::test_path; std::vector test_files; From f4d60f3fc461f11c8b6f69e53aea8bd8eb83f4ae Mon Sep 17 00:00:00 2001 From: gibbed Date: Tue, 11 Jan 2022 04:15:49 -0600 Subject: [PATCH 36/88] [XAM] Fix xeXMsgStartIORequestEx result check. --- src/xenia/kernel/xam/xam_msg.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xenia/kernel/xam/xam_msg.cc b/src/xenia/kernel/xam/xam_msg.cc index 3be2f7ed1..3a1b83c3f 100644 --- a/src/xenia/kernel/xam/xam_msg.cc +++ b/src/xenia/kernel/xam/xam_msg.cc @@ -63,7 +63,7 @@ X_HRESULT xeXMsgStartIORequestEx(uint32_t app, uint32_t message, kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result); result = X_ERROR_IO_PENDING; } - if (result == X_ERROR_SUCCESS || X_ERROR_IO_PENDING) { + if (result == X_ERROR_SUCCESS || result == X_ERROR_IO_PENDING) { XThread::SetLastError(0); } return result; From 5384e0e174d7d500c8cacc0ee9fd22eca3d40baf Mon Sep 17 00:00:00 2001 From: gibbed Date: Sun, 9 Jan 2022 20:48:19 -0600 Subject: [PATCH 37/88] [Base] Fix MICROPROFILE_PRINTF. --- src/xenia/base/profiling.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/xenia/base/profiling.cc b/src/xenia/base/profiling.cc index bf24d4f7d..038c26058 100644 --- a/src/xenia/base/profiling.cc +++ b/src/xenia/base/profiling.cc @@ -13,6 +13,8 @@ // XELOGI. #include "xenia/base/logging.h" +#include "third_party/fmt/include/fmt/printf.h" + // NOTE: microprofile must be setup first, before profiling.h is included. #define MICROPROFILE_ENABLED 1 #define MICROPROFILEUI_ENABLED 1 @@ -21,7 +23,11 @@ #define MICROPROFILE_PER_THREAD_BUFFER_SIZE (1024 * 1024 * 10) #define MICROPROFILE_USE_THREAD_NAME_CALLBACK 1 #define MICROPROFILE_WEBSERVER_MAXFRAMES 3 -#define MICROPROFILE_PRINTF XELOGI +#define MICROPROFILE_PRINTF(...) \ + do { \ + auto xenia_profiler_formatted = fmt::sprintf(__VA_ARGS__); \ + XELOGI("{}", xenia_profiler_formatted); \ + } while (false); #define MICROPROFILE_WEBSERVER 0 #define MICROPROFILE_DEBUG 0 #define MICROPROFILE_MAX_THREADS 128 From 5e314291284d6f846bef033878495bc70402c170 Mon Sep 17 00:00:00 2001 From: Enrico Pozzobon Date: Mon, 20 Sep 2021 11:23:27 +0200 Subject: [PATCH 38/88] [WinKey] Rebindable keyboard controls. --- src/xenia/hid/winkey/winkey_binding_table.inc | 38 ++ src/xenia/hid/winkey/winkey_input_driver.cc | 350 ++++++++---------- src/xenia/hid/winkey/winkey_input_driver.h | 14 +- 3 files changed, 206 insertions(+), 196 deletions(-) create mode 100644 src/xenia/hid/winkey/winkey_binding_table.inc diff --git a/src/xenia/hid/winkey/winkey_binding_table.inc b/src/xenia/hid/winkey/winkey_binding_table.inc new file mode 100644 index 000000000..fdf0261f3 --- /dev/null +++ b/src/xenia/hid/winkey/winkey_binding_table.inc @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +// This is a partial file designed to be included by other files when +// constructing various tables. + +// clang-format off +XE_HID_WINKEY_BINDING(DpadLeft, "DPAD_LEFT" , keybind_dpad_left , "^A" ) +XE_HID_WINKEY_BINDING(DpadRight, "DPAD_RIGHT" , keybind_dpad_right , "^D" ) +XE_HID_WINKEY_BINDING(DpadDown, "DPAD_DOWN" , keybind_dpad_down , "^S" ) +XE_HID_WINKEY_BINDING(DpadUp, "DPAD_UP" , keybind_dpad_up , "^W" ) +XE_HID_WINKEY_BINDING(LThumbLeft, "LEFT_THUMB_LEFT" , keybind_left_thumb_left , "_A" ) +XE_HID_WINKEY_BINDING(LThumbRight, "LEFT_THUMB_RIGHT" , keybind_left_thumb_right , "_D" ) +XE_HID_WINKEY_BINDING(LThumbDown, "LEFT_THUMB_DOWN" , keybind_left_thumb_down , "_S" ) +XE_HID_WINKEY_BINDING(LThumbUp, "LEFT_THUMB_UP" , keybind_left_thumb_up , "_W" ) +XE_HID_WINKEY_BINDING(LThumbPress, "LEFT_THUMB_PRESSED" , keybind_left_thumb , "F" ) +XE_HID_WINKEY_BINDING(RThumbUp, "RIGHT_THUMB_UP" , keybind_right_thumb_up , "0x26") +XE_HID_WINKEY_BINDING(RThumbLeft, "RIGHT_THUMB_DOWN" , keybind_right_thumb_down , "0x28") +XE_HID_WINKEY_BINDING(RThumbRight, "RIGHT_THUMB_RIGHT" , keybind_right_thumb_right, "0x27") +XE_HID_WINKEY_BINDING(RThumbLeft, "RIGHT_THUMB_LEFT" , keybind_right_thumb_left , "0x25") +XE_HID_WINKEY_BINDING(RThumbPress, "RIGHT_THUMB_PRESSED", keybind_right_thumb , "K" ) +XE_HID_WINKEY_BINDING(X, "X" , keybind_x , "L" ) +XE_HID_WINKEY_BINDING(B, "B" , keybind_b , "0xDE") +XE_HID_WINKEY_BINDING(A, "A" , keybind_a , "0xBA") +XE_HID_WINKEY_BINDING(Y, "Y" , keybind_y , "P" ) +XE_HID_WINKEY_BINDING(LTrigger, "LEFT_TRIGGER" , keybind_left_trigger , "Q I" ) +XE_HID_WINKEY_BINDING(RTrigger, "RIGHT_TRIGGER" , keybind_right_trigger , "E O" ) +XE_HID_WINKEY_BINDING(Back, "BACK" , keybind_back , "Z" ) +XE_HID_WINKEY_BINDING(Start, "START" , keybind_start , "X" ) +XE_HID_WINKEY_BINDING(LShoulder, "LEFT_SHOULDER" , keybind_left_shoulder , "1" ) +XE_HID_WINKEY_BINDING(RShoulder, "RIGHT_SHOULDER" , keybind_right_shoulder , "3" ) +// clang-format on diff --git a/src/xenia/hid/winkey/winkey_input_driver.cc b/src/xenia/hid/winkey/winkey_input_driver.cc index ba0b12cdf..c038e69db 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.cc +++ b/src/xenia/hid/winkey/winkey_input_driver.cc @@ -2,23 +2,84 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/hid/winkey/winkey_input_driver.h" +#include "xenia/base/logging.h" #include "xenia/base/platform_win.h" #include "xenia/hid/hid_flags.h" #include "xenia/hid/input_system.h" #include "xenia/ui/virtual_key.h" #include "xenia/ui/window.h" +#define XE_HID_WINKEY_BINDING(button, description, cvar_name, \ + cvar_default_value) \ + DEFINE_string(cvar_name, cvar_default_value, \ + "List of keys to bind to " description \ + ", separated by spaces", \ + "HID.WinKey") +#include "winkey_binding_table.inc" +#undef XE_HID_WINKEY_BINDING + namespace xe { namespace hid { namespace winkey { +bool __inline IsKeyToggled(uint8_t key) { + return (GetKeyState(key) & 0x1) == 0x1; +} + +bool __inline IsKeyDown(uint8_t key) { + return (GetAsyncKeyState(key) & 0x8000) == 0x8000; +} + +bool __inline IsKeyDown(ui::VirtualKey virtual_key) { + return IsKeyDown(static_cast(virtual_key)); +} + +void WinKeyInputDriver::ParseKeyBinding(ui::VirtualKey output_key, + const std::string_view description, + const std::string_view source_tokens) { + for (const std::string_view source_token : + utf8::split(source_tokens, " ", true)) { + KeyBinding key_binding; + key_binding.output_key = output_key; + + std::string_view token = source_token; + + if (utf8::starts_with(token, "_")) { + key_binding.lowercase = true; + token = token.substr(1); + } else if (utf8::starts_with(token, "^")) { + key_binding.uppercase = true; + token = token.substr(1); + } + + if (utf8::starts_with(token, "0x")) { + token = token.substr(2); + key_binding.input_key = static_cast( + string_util::from_string(token, true)); + } else if (token.size() == 1 && (token[0] >= 'A' && token[0] <= 'Z') || + (token[0] >= '0' && token[0] <= '9')) { + key_binding.input_key = static_cast(token[0]); + } + + if (key_binding.input_key == ui::VirtualKey::kNone) { + XELOGW("winkey: failed to parse binding \"{}\" for controller input {}.", + source_token, description); + continue; + } + + key_bindings_.push_back(key_binding); + XELOGI("winkey: \"{}\" binds key 0x{:X} to controller input {}.", + source_token, key_binding.input_key, description); + } +} + WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window) : InputDriver(window), packet_number_(1) { // Register a key listener. @@ -50,6 +111,13 @@ WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window) key.repeat_count = evt->repeat_count(); key_events_.push(key); }); + +#define XE_HID_WINKEY_BINDING(button, description, cvar_name, \ + cvar_default_value) \ + ParseKeyBinding(xe::ui::VirtualKey::kXInputPad##button, description, \ + cvars::cvar_name); +#include "winkey_binding_table.inc" +#undef XE_HID_WINKEY_BINDING } WinKeyInputDriver::~WinKeyInputDriver() = default; @@ -78,9 +146,6 @@ X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags, return X_ERROR_SUCCESS; } -#define IS_KEY_TOGGLED(key) ((GetKeyState(key) & 0x1) == 0x1) -#define IS_KEY_DOWN(key) ((GetAsyncKeyState(key) & 0x8000) == 0x8000) - X_RESULT WinKeyInputDriver::GetState(uint32_t user_index, X_INPUT_STATE* out_state) { if (user_index != 0) { @@ -98,114 +163,86 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index, int16_t thumb_ry = 0; if (window()->has_focus() && is_active()) { - if (IS_KEY_TOGGLED(VK_CAPITAL) || IS_KEY_DOWN(VK_SHIFT)) { - // dpad toggled - if (IS_KEY_DOWN('A')) { - // A - buttons |= 0x0004; // XINPUT_GAMEPAD_DPAD_LEFT + bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT); + for (const KeyBinding& b : key_bindings_) { + if (((b.lowercase == b.uppercase) || (b.lowercase && !capital) || + (b.uppercase && capital)) && + IsKeyDown(b.input_key)) { + switch (b.output_key) { + case ui::VirtualKey::kXInputPadA: + buttons |= 0x1000; // XINPUT_GAMEPAD_A + break; + case ui::VirtualKey::kXInputPadY: + buttons |= 0x8000; // XINPUT_GAMEPAD_Y + break; + case ui::VirtualKey::kXInputPadB: + buttons |= 0x2000; // XINPUT_GAMEPAD_B + break; + case ui::VirtualKey::kXInputPadX: + buttons |= 0x4000; // XINPUT_GAMEPAD_X + break; + case ui::VirtualKey::kXInputPadDpadLeft: + buttons |= 0x0004; // XINPUT_GAMEPAD_DPAD_LEFT + break; + case ui::VirtualKey::kXInputPadDpadRight: + buttons |= 0x0008; // XINPUT_GAMEPAD_DPAD_RIGHT + break; + case ui::VirtualKey::kXInputPadDpadDown: + buttons |= 0x0002; // XINPUT_GAMEPAD_DPAD_DOWN + break; + case ui::VirtualKey::kXInputPadDpadUp: + buttons |= 0x0001; // XINPUT_GAMEPAD_DPAD_UP + break; + case ui::VirtualKey::kXInputPadRThumbPress: + buttons |= 0x0080; // XINPUT_GAMEPAD_RIGHT_THUMB + break; + case ui::VirtualKey::kXInputPadLThumbPress: + buttons |= 0x0040; // XINPUT_GAMEPAD_LEFT_THUMB + break; + case ui::VirtualKey::kXInputPadBack: + buttons |= 0x0020; // XINPUT_GAMEPAD_BACK + break; + case ui::VirtualKey::kXInputPadStart: + buttons |= 0x0010; // XINPUT_GAMEPAD_START + break; + case ui::VirtualKey::kXInputPadLShoulder: + buttons |= 0x0100; // XINPUT_GAMEPAD_LEFT_SHOULDER + break; + case ui::VirtualKey::kXInputPadRShoulder: + buttons |= 0x0200; // XINPUT_GAMEPAD_RIGHT_SHOULDER + break; + case ui::VirtualKey::kXInputPadLTrigger: + left_trigger = 0xFF; + break; + case ui::VirtualKey::kXInputPadRTrigger: + right_trigger = 0xFF; + break; + case ui::VirtualKey::kXInputPadLThumbLeft: + thumb_lx += SHRT_MIN; + break; + case ui::VirtualKey::kXInputPadLThumbRight: + thumb_lx += SHRT_MAX; + break; + case ui::VirtualKey::kXInputPadLThumbDown: + thumb_ly += SHRT_MIN; + break; + case ui::VirtualKey::kXInputPadLThumbUp: + thumb_ly += SHRT_MAX; + break; + case ui::VirtualKey::kXInputPadRThumbUp: + thumb_ry += SHRT_MAX; + break; + case ui::VirtualKey::kXInputPadRThumbDown: + thumb_ry += SHRT_MIN; + break; + case ui::VirtualKey::kXInputPadRThumbRight: + thumb_rx += SHRT_MAX; + break; + case ui::VirtualKey::kXInputPadRThumbLeft: + thumb_rx += SHRT_MIN; + break; + } } - if (IS_KEY_DOWN('D')) { - // D - buttons |= 0x0008; // XINPUT_GAMEPAD_DPAD_RIGHT - } - if (IS_KEY_DOWN('S')) { - // S - buttons |= 0x0002; // XINPUT_GAMEPAD_DPAD_DOWN - } - if (IS_KEY_DOWN('W')) { - // W - buttons |= 0x0001; // XINPUT_GAMEPAD_DPAD_UP - } - } else { - // left stick - if (IS_KEY_DOWN('A')) { - // A - thumb_lx += SHRT_MIN; - } - if (IS_KEY_DOWN('D')) { - // D - thumb_lx += SHRT_MAX; - } - if (IS_KEY_DOWN('S')) { - // S - thumb_ly += SHRT_MIN; - } - if (IS_KEY_DOWN('W')) { - // W - thumb_ly += SHRT_MAX; - } - } - - if (IS_KEY_DOWN('F')) { - // F - buttons |= 0x0040; // XINPUT_GAMEPAD_LEFT_THUMB - } - - // Right stick - if (IS_KEY_DOWN(VK_UP)) { - // Up - thumb_ry += SHRT_MAX; - } - if (IS_KEY_DOWN(VK_DOWN)) { - // Down - thumb_ry += SHRT_MIN; - } - if (IS_KEY_DOWN(VK_RIGHT)) { - // Right - thumb_rx += SHRT_MAX; - } - if (IS_KEY_DOWN(VK_LEFT)) { - // Left - thumb_rx += SHRT_MIN; - } - - if (IS_KEY_DOWN('L')) { - // L - buttons |= 0x4000; // XINPUT_GAMEPAD_X - } - if (IS_KEY_DOWN(VK_OEM_7)) { - // ' - buttons |= 0x2000; // XINPUT_GAMEPAD_B - } - if (IS_KEY_DOWN(VK_OEM_1)) { - // ; - buttons |= 0x1000; // XINPUT_GAMEPAD_A - } - if (IS_KEY_DOWN('P')) { - // P - buttons |= 0x8000; // XINPUT_GAMEPAD_Y - } - - if (IS_KEY_DOWN('K')) { - // K - buttons |= 0x0080; // XINPUT_GAMEPAD_RIGHT_THUMB - } - - if (IS_KEY_DOWN('Q') || IS_KEY_DOWN('I')) { - // Q / I - left_trigger = 0xFF; - } - - if (IS_KEY_DOWN('E') || IS_KEY_DOWN('O')) { - // E / O - right_trigger = 0xFF; - } - - if (IS_KEY_DOWN('Z')) { - // Z - buttons |= 0x0020; // XINPUT_GAMEPAD_BACK - } - if (IS_KEY_DOWN('X')) { - // X - buttons |= 0x0010; // XINPUT_GAMEPAD_START - } - if (IS_KEY_DOWN('1')) { - // 1 - buttons |= 0x0100; // XINPUT_GAMEPAD_LEFT_SHOULDER - } - if (IS_KEY_DOWN('3')) { - // 3 - buttons |= 0x0200; // XINPUT_GAMEPAD_RIGHT_SHOULDER } } @@ -259,90 +296,13 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, key_events_.pop(); } - switch (evt.virtual_key) { - case ui::VirtualKey::kOem1: // ; - xinput_virtual_key = ui::VirtualKey::kXInputPadA; - break; - case ui::VirtualKey::kOem7: // ' - xinput_virtual_key = ui::VirtualKey::kXInputPadB; - break; - case ui::VirtualKey::kL: - xinput_virtual_key = ui::VirtualKey::kXInputPadX; - break; - case ui::VirtualKey::kP: - xinput_virtual_key = ui::VirtualKey::kXInputPadY; - break; - case ui::VirtualKey::k3: - xinput_virtual_key = ui::VirtualKey::kXInputPadRShoulder; - break; - case ui::VirtualKey::k1: - xinput_virtual_key = ui::VirtualKey::kXInputPadLShoulder; - break; - case ui::VirtualKey::kQ: - case ui::VirtualKey::kI: - xinput_virtual_key = ui::VirtualKey::kXInputPadLTrigger; - break; - case ui::VirtualKey::kE: - case ui::VirtualKey::kO: - xinput_virtual_key = ui::VirtualKey::kXInputPadRTrigger; - break; - case ui::VirtualKey::kX: - xinput_virtual_key = ui::VirtualKey::kXInputPadStart; - break; - case ui::VirtualKey::kZ: - xinput_virtual_key = ui::VirtualKey::kXInputPadBack; - break; - case ui::VirtualKey::kUp: - xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbUp; - break; - case ui::VirtualKey::kDown: - xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbDown; - break; - case ui::VirtualKey::kRight: - xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbRight; - break; - case ui::VirtualKey::kLeft: - xinput_virtual_key = ui::VirtualKey::kXInputPadRThumbLeft; - break; - default: - // TODO(DrChat): Some other way to toggle this... - if (IS_KEY_TOGGLED(VK_CAPITAL) || IS_KEY_DOWN(VK_SHIFT)) { - // D-pad toggled. - switch (evt.virtual_key) { - case ui::VirtualKey::kW: - xinput_virtual_key = ui::VirtualKey::kXInputPadDpadUp; - break; - case ui::VirtualKey::kS: - xinput_virtual_key = ui::VirtualKey::kXInputPadDpadDown; - break; - case ui::VirtualKey::kA: - xinput_virtual_key = ui::VirtualKey::kXInputPadDpadLeft; - break; - case ui::VirtualKey::kD: - xinput_virtual_key = ui::VirtualKey::kXInputPadDpadRight; - break; - default: - break; - } - } else { - // Left thumbstick. - switch (evt.virtual_key) { - case ui::VirtualKey::kW: - xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbUp; - break; - case ui::VirtualKey::kS: - xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbDown; - break; - case ui::VirtualKey::kA: - xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbLeft; - break; - case ui::VirtualKey::kD: - xinput_virtual_key = ui::VirtualKey::kXInputPadLThumbRight; - break; - default: - break; - } - } + bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT); + for (const KeyBinding& b : key_bindings_) { + if (b.input_key == evt.virtual_key && + ((b.lowercase == b.uppercase) || (b.lowercase && !capital) || + (b.uppercase && capital))) { + xinput_virtual_key = b.output_key; + } } if (xinput_virtual_key != ui::VirtualKey::kNone) { diff --git a/src/xenia/hid/winkey/winkey_input_driver.h b/src/xenia/hid/winkey/winkey_input_driver.h index f015b7416..8b45c1457 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.h +++ b/src/xenia/hid/winkey/winkey_input_driver.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -42,10 +42,22 @@ class WinKeyInputDriver : public InputDriver { bool prev_state = false; // down(true) or up(false) }; + struct KeyBinding { + ui::VirtualKey input_key = ui::VirtualKey::kNone; + ui::VirtualKey output_key = ui::VirtualKey::kNone; + bool uppercase = false; + bool lowercase = false; + }; + xe::global_critical_region global_critical_region_; std::queue key_events_; + std::vector key_bindings_; uint32_t packet_number_; + + void ParseKeyBinding(ui::VirtualKey virtual_key, + const std::string_view description, + const std::string_view binding); }; } // namespace winkey From 31c9f026c5c4eb1adee6055a605d2f3c687f1199 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Wed, 12 Jan 2022 15:10:20 +0100 Subject: [PATCH 39/88] [UI] Force use of Xwayland when running on Wayland --- src/xenia/ui/windowed_app_main_posix.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xenia/ui/windowed_app_main_posix.cc b/src/xenia/ui/windowed_app_main_posix.cc index 60b93a04e..dc99e243d 100644 --- a/src/xenia/ui/windowed_app_main_posix.cc +++ b/src/xenia/ui/windowed_app_main_posix.cc @@ -18,6 +18,10 @@ #include "xenia/ui/windowed_app_context_gtk.h" extern "C" int main(int argc_pre_gtk, char** argv_pre_gtk) { + // Before touching anything GTK+, make sure that when running on Wayland, + // we'll still get an X11 (Xwayland) window + setenv("GDK_BACKEND", "x11", 1); + // Initialize GTK+, which will handle and remove its own arguments from argv. // Both GTK+ and Xenia use --option=value argument format (see man // gtk-options), however, it's meaningless to try to parse the same argument From 5d1b53cd6f5386038769c932bb249ff1c6dbaf10 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Tue, 28 Dec 2021 20:05:46 -0800 Subject: [PATCH 40/88] [x64] Add `VECTOR_SHA_I8_SAME_CONSTANT` unit test This is to target the new GNFI-based optimization for the Int8 case. --- src/xenia/cpu/testing/vector_sha_test.cc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/xenia/cpu/testing/vector_sha_test.cc b/src/xenia/cpu/testing/vector_sha_test.cc index 5b4adabc6..187e8b4f2 100644 --- a/src/xenia/cpu/testing/vector_sha_test.cc +++ b/src/xenia/cpu/testing/vector_sha_test.cc @@ -58,6 +58,28 @@ TEST_CASE("VECTOR_SHA_I8_CONSTANT", "[instr]") { }); } +// This targets the "all_same" optimization of the Int8 specialization of +// VECTOR_SHA_V128 +TEST_CASE("VECTOR_SHA_I8_SAME_CONSTANT", "[instr]") { + TestFunction test([](HIRBuilder& b) { + StoreVR( + b, 3, + b.VectorSha(LoadVR(b, 4), b.LoadConstantVec128(vec128b(5)), INT8_TYPE)); + b.Return(); + }); + test.Run( + [](PPCContext* ctx) { + ctx->v[4] = vec128b(0x7E, 0x7E, 0x7E, 0x7F, 0x80, 0xFF, 0x01, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + }, + [](PPCContext* ctx) { + auto result = ctx->v[3]; + REQUIRE(result == vec128b(0x03, 0x03, 0x03, 0x03, 0xfc, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00)); + }); +} + TEST_CASE("VECTOR_SHA_I16", "[instr]") { TestFunction test([](HIRBuilder& b) { StoreVR(b, 3, b.VectorSha(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE)); From fba23e3e757ffe09e3a8f4bd0946b51c74126fb8 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Tue, 28 Dec 2021 20:08:48 -0800 Subject: [PATCH 41/88] [x64] Add `kX64EmitGFNI` emitter feature-flag This determines support for the `gf2p8affineqb` instruction. Even though `GFNI` is typically found with AVX512-enabled chips, it _is_ possible for there to be a chip with `GFNI` but does not support `AVX` or `AVX2` of any sort. An example of this is Tremont(Lakefield) chips as well as Jasper Lake. https://github.com/InstLatx64/InstLatx64/blob/13df339fe7150b114929f71b19a6b2fe72fc751e/GenuineIntel/GenuineIntel00806A1_Lakefield_LC_InstLatX64.txt#L1297-L1299 https://github.com/InstLatx64/InstLatx64/blob/13df339fe7150b114929f71b19a6b2fe72fc751e/GenuineIntel/GenuineIntel00906C0_JasperLake_InstLatX64.txt#L1252-L1254 --- src/xenia/cpu/backend/x64/x64_backend.cc | 9 +++++---- src/xenia/cpu/backend/x64/x64_emitter.cc | 2 ++ src/xenia/cpu/backend/x64/x64_emitter.h | 9 +++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_backend.cc b/src/xenia/cpu/backend/x64/x64_backend.cc index fe1326920..1da4ba9f3 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.cc +++ b/src/xenia/cpu/backend/x64/x64_backend.cc @@ -37,10 +37,11 @@ DEFINE_int32(x64_extension_mask, -1, " 16 = BMI2\n" " 32 = F16C\n" " 64 = Movbe\n" - " 128 = AVX512F\n" - " 256 = AVX512VL\n" - " 512 = AVX512BW\n" - " 1024 = AVX512DQ\n" + " 128 = GFNI\n" + " 256 = AVX512F\n" + " 512 = AVX512VL\n" + " 1024 = AVX512BW\n" + " 2048 = AVX512DQ\n" " -1 = Detect and utilize all possible processor features\n", "x64"); diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index d555ff7eb..f517122d3 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -88,6 +88,8 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tF16C) ? kX64EmitF16C : 0; if (cvars::x64_extension_mask & kX64EmitMovbe) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tMOVBE) ? kX64EmitMovbe : 0; + if (cvars::x64_extension_mask & kX64EmitGFNI) + feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tGFNI) ? kX64EmitGFNI : 0; if (cvars::x64_extension_mask & kX64EmitAVX512F) feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tAVX512F) ? kX64EmitAVX512F : 0; diff --git a/src/xenia/cpu/backend/x64/x64_emitter.h b/src/xenia/cpu/backend/x64/x64_emitter.h index be8cd0b1a..840e355fc 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.h +++ b/src/xenia/cpu/backend/x64/x64_emitter.h @@ -132,12 +132,13 @@ enum X64EmitterFeatureFlags { kX64EmitBMI2 = 1 << 4, kX64EmitF16C = 1 << 5, kX64EmitMovbe = 1 << 6, + kX64EmitGFNI = 1 << 7, - kX64EmitAVX512F = 1 << 7, - kX64EmitAVX512VL = 1 << 8, + kX64EmitAVX512F = 1 << 8, + kX64EmitAVX512VL = 1 << 9, - kX64EmitAVX512BW = 1 << 9, - kX64EmitAVX512DQ = 1 << 10, + kX64EmitAVX512BW = 1 << 10, + kX64EmitAVX512DQ = 1 << 11, kX64EmitAVX512Ortho = kX64EmitAVX512F | kX64EmitAVX512VL, kX64EmitAVX512Ortho64 = kX64EmitAVX512Ortho | kX64EmitAVX512DQ From a9a365aa32a59898f8e3cc18d0537119542800d4 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Tue, 28 Dec 2021 20:17:41 -0800 Subject: [PATCH 42/88] [x64] Add `GFNI`-based optimization for `VECTOR_SHA_V128(Int8)` In the `Int8` case of `VECTOR_SHA_V128`, when all the values are the same, then a single-instruction `gf2p8affineqb` can be emitted that does an int8-based arithmetic-shift, utilizing GF(8) arithmetic. More info here: https://wunkolo.github.io/post/2020/11/gf2p8affineqb-int8-shifting/ As of now(Dec 2021): Tremont(Lakefield), Jasper Lake, Ice lake, Tigerlake, and Rocket Lake support GNFI. --- src/xenia/cpu/backend/x64/x64_seq_vector.cc | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/xenia/cpu/backend/x64/x64_seq_vector.cc b/src/xenia/cpu/backend/x64/x64_seq_vector.cc index b5a8e49a2..c428018ba 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_vector.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_vector.cc @@ -1084,6 +1084,28 @@ struct VECTOR_SHA_V128 static void EmitInt8(X64Emitter& e, const EmitArgType& i) { // TODO(benvanik): native version (with shift magic). if (i.src2.is_constant) { + if (e.IsFeatureEnabled(kX64EmitGFNI)) { + const auto& shamt = i.src2.constant(); + bool all_same = true; + for (size_t n = 0; n < 8 - n; ++n) { + if (shamt.u16[n] != shamt.u16[n + 1]) { + all_same = false; + break; + } + } + if (all_same) { + // Every count is the same, so we can use gf2p8affineqb. + const uint8_t shift_amount = shamt.u8[0]; + const uint64_t shift_matrix = + shift_amount < 8 + ? (0x0102040810204080ULL << (shift_amount * 8)) | + (0x8080808080808080ULL >> (64 - shift_amount * 8)) + : 0x8080808080808080ULL; + e.vgf2p8affineqb(i.dest, i.src1, + e.StashConstantXmm(0, vec128q(shift_matrix)), 0); + return; + } + } e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant())); } else { e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2)); From 617b17e25b85f4164524895e5b13c81be6157f3d Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Fri, 14 Jan 2022 16:06:40 -0600 Subject: [PATCH 43/88] [WinKey] Fix RThumbDown being mapped to RThumbLeft --- src/xenia/hid/winkey/winkey_binding_table.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xenia/hid/winkey/winkey_binding_table.inc b/src/xenia/hid/winkey/winkey_binding_table.inc index fdf0261f3..c11b277a0 100644 --- a/src/xenia/hid/winkey/winkey_binding_table.inc +++ b/src/xenia/hid/winkey/winkey_binding_table.inc @@ -21,7 +21,7 @@ XE_HID_WINKEY_BINDING(LThumbDown, "LEFT_THUMB_DOWN" , keybind_left_thumb_dow XE_HID_WINKEY_BINDING(LThumbUp, "LEFT_THUMB_UP" , keybind_left_thumb_up , "_W" ) XE_HID_WINKEY_BINDING(LThumbPress, "LEFT_THUMB_PRESSED" , keybind_left_thumb , "F" ) XE_HID_WINKEY_BINDING(RThumbUp, "RIGHT_THUMB_UP" , keybind_right_thumb_up , "0x26") -XE_HID_WINKEY_BINDING(RThumbLeft, "RIGHT_THUMB_DOWN" , keybind_right_thumb_down , "0x28") +XE_HID_WINKEY_BINDING(RThumbDown, "RIGHT_THUMB_DOWN" , keybind_right_thumb_down , "0x28") XE_HID_WINKEY_BINDING(RThumbRight, "RIGHT_THUMB_RIGHT" , keybind_right_thumb_right, "0x27") XE_HID_WINKEY_BINDING(RThumbLeft, "RIGHT_THUMB_LEFT" , keybind_right_thumb_left , "0x25") XE_HID_WINKEY_BINDING(RThumbPress, "RIGHT_THUMB_PRESSED", keybind_right_thumb , "K" ) From bfaad055a2ad2ed12de982374984fd55f902c9fc Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Sat, 22 Jan 2022 14:17:29 +0100 Subject: [PATCH 44/88] [Base] Add easier to debug `copy_and_swap` tests --- src/xenia/base/testing/memory_test.cc | 48 ++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/xenia/base/testing/memory_test.cc b/src/xenia/base/testing/memory_test.cc index f78d9c3bb..1374d6c14 100644 --- a/src/xenia/base/testing/memory_test.cc +++ b/src/xenia/base/testing/memory_test.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -14,6 +14,8 @@ #include "xenia/base/clock.h" +#include + namespace xe { namespace base { namespace test { @@ -113,6 +115,24 @@ TEST_CASE("copy_and_swap_16_unaligned", "[copy_and_swap]") { REQUIRE(c[2] == 0xAB89); REQUIRE(c[3] == 0xEFCD); + { + constexpr size_t count = 100; + std::array src{}; + std::array dst{}; + for (size_t i = 0; i < src.size(); ++i) { + src[i] = static_cast(i) + 1; // no zero in array + } + copy_and_swap_16_unaligned(dst.data(), src.data(), count); + for (size_t i = 0; i < src.size(); i += 2) { + // Check src is untouched + REQUIRE(static_cast(src[i + 0]) == i + 1); + REQUIRE(static_cast(src[i + 1]) == i + 2); + // Check swapped bytes + REQUIRE(static_cast(dst[i]) == static_cast(src[i + 1])); + REQUIRE(static_cast(dst[i + 1]) == static_cast(src[i])); + } + } + uint64_t e; copy_and_swap_16_unaligned(&e, d, 4); REQUIRE(e == 0xEFCDAB8967452301); @@ -221,6 +241,32 @@ TEST_CASE("copy_and_swap_32_unaligned", "[copy_and_swap]") { REQUIRE(c[2] == 0xEDEE87E8); REQUIRE(c[3] == 0x994151D8); + { + constexpr size_t count = 17; + std::array src{}; + std::array dst{}; + for (size_t i = 0; i < src.size(); ++i) { + src[i] = static_cast(i) + 1; // no zero in array + } + copy_and_swap_32_unaligned(dst.data(), src.data(), count); + for (size_t i = 0; i < src.size(); i += 4) { + // Check src is untouched + REQUIRE(static_cast(src[i + 0]) == i + 1); + REQUIRE(static_cast(src[i + 1]) == i + 2); + REQUIRE(static_cast(src[i + 2]) == i + 3); + REQUIRE(static_cast(src[i + 3]) == i + 4); + // Check swapped bytes + REQUIRE(static_cast(dst[i + 0]) == + static_cast(src[i + 3])); + REQUIRE(static_cast(dst[i + 1]) == + static_cast(src[i + 2])); + REQUIRE(static_cast(dst[i + 2]) == + static_cast(src[i + 1])); + REQUIRE(static_cast(dst[i + 3]) == + static_cast(src[i + 0])); + } + } + uint64_t e; copy_and_swap_32_unaligned(&e, d, 2); REQUIRE(e == 0xEFCDAB8967452301); From 4a288dc6bde891c11a187e704e5144cb09d5f18a Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Fri, 21 Jan 2022 22:28:47 +0100 Subject: [PATCH 45/88] [Base, aarch64] Add `copy_and_swap` NEON impls --- src/xenia/base/memory.cc | 128 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/src/xenia/base/memory.cc b/src/xenia/base/memory.cc index 8acbf43bd..c45bf4636 100644 --- a/src/xenia/base/memory.cc +++ b/src/xenia/base/memory.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -11,6 +11,10 @@ #include "xenia/base/cvar.h" #include "xenia/base/platform.h" +#if XE_ARCH_ARM64 +#include +#endif + #include DEFINE_bool( @@ -215,7 +219,128 @@ void copy_and_swap_16_in_32_unaligned(void* dest_ptr, const void* src_ptr, dest[i] = (src[i] >> 16) | (src[i] << 16); } } + +#elif XE_ARCH_ARM64 + +// Although NEON offers vector rev instructions (like vrev32q_u8), they are +// slower in benchmarks. Also, using uint8x16xN_t wasn't any faster in the +// benchmarks, hence we use just use one SIMD register to minimize residual +// processing. + +void copy_and_swap_16_aligned(void* dst_ptr, const void* src_ptr, + size_t count) { + copy_and_swap_16_unaligned(dst_ptr, src_ptr, count); +} + +void copy_and_swap_16_unaligned(void* dst_ptr, const void* src_ptr, + size_t count) { + auto dst = reinterpret_cast(dst_ptr); + auto src = reinterpret_cast(src_ptr); + + const uint8x16_t tbl_idx = + vcombine_u8(vcreate_u8(UINT64_C(0x0607040502030001)), + vcreate_u8(UINT64_C(0x0E0F0C0D0A0B0809))); + + while (count >= 8) { + uint8x16_t data = vld1q_u8(src); + data = vqtbl1q_u8(data, tbl_idx); + vst1q_u8(dst, data); + + count -= 8; + // These pointer increments will be combined with the load/stores (ldr/str) + // into single instructions (at least by clang) + dst += 16; + src += 16; + } + + while (count > 0) { + store_and_swap(dst, load(src)); + + count--; + dst += 2; + src += 2; + } +} + +void copy_and_swap_32_aligned(void* dst, const void* src, size_t count) { + copy_and_swap_32_unaligned(dst, src, count); +} + +void copy_and_swap_32_unaligned(void* dst_ptr, const void* src_ptr, + size_t count) { + auto dst = reinterpret_cast(dst_ptr); + auto src = reinterpret_cast(src_ptr); + + const uint8x16_t tbl_idx = + vcombine_u8(vcreate_u8(UINT64_C(0x405060700010203)), + vcreate_u8(UINT64_C(0x0C0D0E0F08090A0B))); + + while (count >= 4) { + uint8x16_t data = vld1q_u8(src); + data = vqtbl1q_u8(data, tbl_idx); + vst1q_u8(dst, data); + + count -= 4; + dst += 16; + src += 16; + } + + while (count > 0) { + store_and_swap(dst, load(src)); + + count--; + dst += 4; + src += 4; + } +} + +void copy_and_swap_64_aligned(void* dst, const void* src, size_t count) { + copy_and_swap_64_unaligned(dst, src, count); +} + +void copy_and_swap_64_unaligned(void* dst_ptr, const void* src_ptr, + size_t count) { + auto dst = reinterpret_cast(dst_ptr); + auto src = reinterpret_cast(src_ptr); + + const uint8x16_t tbl_idx = + vcombine_u8(vcreate_u8(UINT64_C(0x0001020304050607)), + vcreate_u8(UINT64_C(0x08090A0B0C0D0E0F))); + + while (count >= 2) { + uint8x16_t data = vld1q_u8(src); + data = vqtbl1q_u8(data, tbl_idx); + vst1q_u8(dst, data); + + count -= 2; + dst += 16; + src += 16; + } + + while (count > 0) { + store_and_swap(dst, load(src)); + + count--; + dst += 8; + src += 8; + } +} + +void copy_and_swap_16_in_32_aligned(void* dst, const void* src, size_t count) { + return copy_and_swap_16_in_32_unaligned(dst, src, count); +} + +void copy_and_swap_16_in_32_unaligned(void* dst_ptr, const void* src_ptr, + size_t count) { + auto dst = reinterpret_cast(dst_ptr); + auto src = reinterpret_cast(src_ptr); + for (size_t i = 0; i < count; ++i) { + dst[i] = (src[i] >> 16) | (src[i] << 16); + } +} + #else + // Generic routines. void copy_and_swap_16_aligned(void* dest, const void* src, size_t count) { return copy_and_swap_16_unaligned(dest, src, count); @@ -268,6 +393,7 @@ void copy_and_swap_16_in_32_unaligned(void* dest_ptr, const void* src_ptr, dest[i] = (src[i] >> 16) | (src[i] << 16); } } + #endif } // namespace xe From 0316d1a05498e86aa3d6b4e1602252b1f4e0ef18 Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Sat, 22 Jan 2022 14:06:18 +0100 Subject: [PATCH 46/88] [Base] Tests for `copy_and_swap_16_in_32_aligned` --- src/xenia/base/testing/memory_test.cc | 51 ++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/xenia/base/testing/memory_test.cc b/src/xenia/base/testing/memory_test.cc index 1374d6c14..0594b53d9 100644 --- a/src/xenia/base/testing/memory_test.cc +++ b/src/xenia/base/testing/memory_test.cc @@ -454,13 +454,56 @@ TEST_CASE("copy_and_swap_64_unaligned", "[copy_and_swap]") { } TEST_CASE("copy_and_swap_16_in_32_aligned", "[copy_and_swap]") { - // TODO(bwrsandman): test once properly understood. - REQUIRE(true == true); + constexpr size_t count = 17; + std::array src{}; + std::array dst{}; + + // Check alignment (if this fails, adjust allocation) + REQUIRE((reinterpret_cast(src.data()) & 0xF) == 0); + REQUIRE((reinterpret_cast(dst.data()) & 0xF) == 0); + + for (size_t i = 0; i < src.size(); ++i) { + src[i] = static_cast(i) + 1; // no zero in array + } + + copy_and_swap_16_in_32_aligned(dst.data(), src.data(), count); + + for (size_t i = 0; i < src.size(); i += 4) { + // Check src is untouched + REQUIRE(static_cast(src[i + 0]) == i + 1); + REQUIRE(static_cast(src[i + 1]) == i + 2); + REQUIRE(static_cast(src[i + 2]) == i + 3); + REQUIRE(static_cast(src[i + 3]) == i + 4); + // Check swapped bytes + REQUIRE(static_cast(dst[i + 0]) == static_cast(src[i + 2])); + REQUIRE(static_cast(dst[i + 1]) == static_cast(src[i + 3])); + REQUIRE(static_cast(dst[i + 2]) == static_cast(src[i + 0])); + REQUIRE(static_cast(dst[i + 3]) == static_cast(src[i + 1])); + } } TEST_CASE("copy_and_swap_16_in_32_unaligned", "[copy_and_swap]") { - // TODO(bwrsandman): test once properly understood. - REQUIRE(true == true); + constexpr size_t count = 17; + std::array src{}; + std::array dst{}; + for (size_t i = 0; i < src.size(); ++i) { + src[i] = static_cast(i) + 1; // no zero in array + } + + copy_and_swap_16_in_32_unaligned(dst.data(), src.data(), count); + + for (size_t i = 0; i < src.size(); i += 4) { + // Check src is untouched + REQUIRE(static_cast(src[i + 0]) == i + 1); + REQUIRE(static_cast(src[i + 1]) == i + 2); + REQUIRE(static_cast(src[i + 2]) == i + 3); + REQUIRE(static_cast(src[i + 3]) == i + 4); + // Check swapped bytes + REQUIRE(static_cast(dst[i + 0]) == static_cast(src[i + 2])); + REQUIRE(static_cast(dst[i + 1]) == static_cast(src[i + 3])); + REQUIRE(static_cast(dst[i + 2]) == static_cast(src[i + 0])); + REQUIRE(static_cast(dst[i + 3]) == static_cast(src[i + 1])); + } } TEST_CASE("create_and_close_file_mapping", "Virtual Memory Mapping") { From e4ae1d8b2f32425069127c8d51d98828c7046d8a Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Sat, 22 Jan 2022 14:07:35 +0100 Subject: [PATCH 47/88] [Base] Fix `copy_and_swap_16_in_32_aligned` --- src/xenia/base/memory.cc | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/xenia/base/memory.cc b/src/xenia/base/memory.cc index c45bf4636..5c1763282 100644 --- a/src/xenia/base/memory.cc +++ b/src/xenia/base/memory.cc @@ -188,8 +188,8 @@ void copy_and_swap_64_unaligned(void* dest_ptr, const void* src_ptr, void copy_and_swap_16_in_32_aligned(void* dest_ptr, const void* src_ptr, size_t count) { - auto dest = reinterpret_cast(dest_ptr); - auto src = reinterpret_cast(src_ptr); + auto dest = reinterpret_cast(dest_ptr); + auto src = reinterpret_cast(src_ptr); size_t i; for (i = 0; i + 4 <= count; i += 4) { __m128i input = _mm_load_si128(reinterpret_cast(&src[i])); @@ -205,8 +205,8 @@ void copy_and_swap_16_in_32_aligned(void* dest_ptr, const void* src_ptr, void copy_and_swap_16_in_32_unaligned(void* dest_ptr, const void* src_ptr, size_t count) { - auto dest = reinterpret_cast(dest_ptr); - auto src = reinterpret_cast(src_ptr); + auto dest = reinterpret_cast(dest_ptr); + auto src = reinterpret_cast(src_ptr); size_t i; for (i = 0; i + 4 <= count; i += 4) { __m128i input = _mm_loadu_si128(reinterpret_cast(&src[i])); @@ -332,10 +332,15 @@ void copy_and_swap_16_in_32_aligned(void* dst, const void* src, size_t count) { void copy_and_swap_16_in_32_unaligned(void* dst_ptr, const void* src_ptr, size_t count) { - auto dst = reinterpret_cast(dst_ptr); - auto src = reinterpret_cast(src_ptr); - for (size_t i = 0; i < count; ++i) { - dst[i] = (src[i] >> 16) | (src[i] << 16); + auto dst = reinterpret_cast(dst_ptr); + auto src = reinterpret_cast(src_ptr); + while (count > 0) { + uint16_t word0 = *src++; + uint16_t word1 = *src++; + *dst++ = word1; + *dst++ = word0; + + count--; } } @@ -385,12 +390,17 @@ void copy_and_swap_16_in_32_aligned(void* dest, const void* src, size_t count) { return copy_and_swap_16_in_32_unaligned(dest, src, count); } -void copy_and_swap_16_in_32_unaligned(void* dest_ptr, const void* src_ptr, +void copy_and_swap_16_in_32_unaligned(void* dst_ptr, const void* src_ptr, size_t count) { - auto dest = reinterpret_cast(dest_ptr); - auto src = reinterpret_cast(src_ptr); - for (size_t i = 0; i < count; ++i) { - dest[i] = (src[i] >> 16) | (src[i] << 16); + auto dst = reinterpret_cast(dst_ptr); + auto src = reinterpret_cast(src_ptr); + while (count > 0) { + uint16_t word0 = *src++; + uint16_t word1 = *src++; + *dst++ = word1; + *dst++ = word0; + + count--; } } From f7c14a089dec4ad5da9c0f11664889523eded015 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Fri, 14 Jan 2022 10:33:23 -0800 Subject: [PATCH 48/88] [x64] Add host-extension detection preprocessor Rather than having a huge list of if-statements that all do the same thing, this preprocessor allows a more concise pattern to detecting if the emit-flag is enabled as well as the correlated Xbyak flag that it needs to check for to before allowing the feature-flag to be emitted. Also moved the AVX-check to the beginning to early-out rather than do a bunch of wasted work only to find out last that the host doesn't even support AVX. --- src/xenia/cpu/backend/x64/x64_emitter.cc | 48 ++++++++++-------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index f517122d3..eda6be614 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -74,41 +74,31 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator) backend_(backend), code_cache_(backend->code_cache()), allocator_(allocator) { - if (cvars::x64_extension_mask & kX64EmitAVX2) - feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tAVX2) ? kX64EmitAVX2 : 0; - if (cvars::x64_extension_mask & kX64EmitFMA) - feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tFMA) ? kX64EmitFMA : 0; - if (cvars::x64_extension_mask & kX64EmitLZCNT) - feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tLZCNT) ? kX64EmitLZCNT : 0; - if (cvars::x64_extension_mask & kX64EmitBMI1) - feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tBMI1) ? kX64EmitBMI1 : 0; - if (cvars::x64_extension_mask & kX64EmitBMI2) - feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tBMI2) ? kX64EmitBMI2 : 0; - if (cvars::x64_extension_mask & kX64EmitF16C) - feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tF16C) ? kX64EmitF16C : 0; - if (cvars::x64_extension_mask & kX64EmitMovbe) - feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tMOVBE) ? kX64EmitMovbe : 0; - if (cvars::x64_extension_mask & kX64EmitGFNI) - feature_flags_ |= cpu_.has(Xbyak::util::Cpu::tGFNI) ? kX64EmitGFNI : 0; - if (cvars::x64_extension_mask & kX64EmitAVX512F) - feature_flags_ |= - cpu_.has(Xbyak::util::Cpu::tAVX512F) ? kX64EmitAVX512F : 0; - if (cvars::x64_extension_mask & kX64EmitAVX512VL) - feature_flags_ |= - cpu_.has(Xbyak::util::Cpu::tAVX512VL) ? kX64EmitAVX512VL : 0; - if (cvars::x64_extension_mask & kX64EmitAVX512BW) - feature_flags_ |= - cpu_.has(Xbyak::util::Cpu::tAVX512BW) ? kX64EmitAVX512BW : 0; - if (cvars::x64_extension_mask & kX64EmitAVX512DQ) - feature_flags_ |= - cpu_.has(Xbyak::util::Cpu::tAVX512DQ) ? kX64EmitAVX512DQ : 0; - if (!cpu_.has(Xbyak::util::Cpu::tAVX)) { xe::FatalError( "Your CPU does not support AVX, which is required by Xenia. See the " "FAQ for system requirements at https://xenia.jp"); return; } + +#define TEST_EMIT_FEATURE(emit, ext) \ + if ((cvars::x64_extension_mask & emit) == emit) { \ + feature_flags_ |= (cpu_.has(ext) ? emit : 0); \ + } + + TEST_EMIT_FEATURE(kX64EmitAVX2, Xbyak::util::Cpu::tAVX2); + TEST_EMIT_FEATURE(kX64EmitFMA, Xbyak::util::Cpu::tFMA); + TEST_EMIT_FEATURE(kX64EmitLZCNT, Xbyak::util::Cpu::tLZCNT); + TEST_EMIT_FEATURE(kX64EmitBMI1, Xbyak::util::Cpu::tBMI1); + TEST_EMIT_FEATURE(kX64EmitF16C, Xbyak::util::Cpu::tF16C); + TEST_EMIT_FEATURE(kX64EmitMovbe, Xbyak::util::Cpu::tMOVBE); + TEST_EMIT_FEATURE(kX64EmitGFNI, Xbyak::util::Cpu::tGFNI); + TEST_EMIT_FEATURE(kX64EmitAVX512F, Xbyak::util::Cpu::tAVX512F); + TEST_EMIT_FEATURE(kX64EmitAVX512VL, Xbyak::util::Cpu::tAVX512VL); + TEST_EMIT_FEATURE(kX64EmitAVX512BW, Xbyak::util::Cpu::tAVX512BW); + TEST_EMIT_FEATURE(kX64EmitAVX512DQ, Xbyak::util::Cpu::tAVX512DQ); + +#undef TEST_EMIT_FEATURE } X64Emitter::~X64Emitter() = default; From 564a6d6238c2187fde4da21af2f79d30303e59ae Mon Sep 17 00:00:00 2001 From: Margen67 Date: Sun, 19 Dec 2021 18:59:12 -0800 Subject: [PATCH 49/88] [App] Disable stuff that crashes the emulator --- src/xenia/app/emulator_window.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index 62d288f19..a91574604 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -118,6 +118,7 @@ bool EmulatorWindow::Initialize() { case ui::VirtualKey::kF5: { GpuClearCaches(); } break; +#ifdef DEBUG case ui::VirtualKey::kF7: { // Save to file // TODO: Choose path based on user input, or from options @@ -130,6 +131,7 @@ bool EmulatorWindow::Initialize() { // TODO: Spawn a new thread to do this. emulator()->RestoreFromFile("test.sav"); } break; +#endif // #ifdef DEBUG case ui::VirtualKey::kF11: { ToggleFullscreen(); } break; @@ -186,9 +188,11 @@ bool EmulatorWindow::Initialize() { file_menu->AddChild( MenuItem::Create(MenuItem::Type::kString, "&Open...", "Ctrl+O", std::bind(&EmulatorWindow::FileOpen, this))); +#ifdef DEBUG file_menu->AddChild( MenuItem::Create(MenuItem::Type::kString, "Close", std::bind(&EmulatorWindow::FileClose, this))); +#endif // #ifdef DEBUG file_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); file_menu->AddChild(MenuItem::Create( MenuItem::Type::kString, "Show content directory...", From fcd4f69e0f06e1ad4783056d75d1f5d427183ca4 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Mon, 24 Jan 2022 01:40:57 -0600 Subject: [PATCH 50/88] [AppVeyor] Deploy artifacts to GitHub Releases --- .appveyor.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 429e0e6ce..d2c7f4b37 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -58,7 +58,6 @@ artifacts: deploy: - provider: Environment - name: xenia-master release: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version) artifact: '*.zip' draft: false @@ -68,3 +67,17 @@ deploy: configuration: release appveyor_repo_tag: true is_not_pr: true + - provider: GitHub + name: xenia-master + repository: xenia-project/release-builds-windows + auth_token: + secure: /8he47z1WnPN7LcCTe5T5KMxxX0SmqFj9QMpeWEa3aZ64kMsfupOT/jKakqTM8af + release: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version) + description: 'Release build for xenia/$(APPVEYOR_REPO_COMMIT)' + artifact: '*.zip' + draft: false + prerelease: false + on: + branch: master + configuration: release + is_not_pr: true From 258581ee071ba888b005415a9cf64eee5deb0ca0 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Mon, 24 Jan 2022 02:15:23 -0600 Subject: [PATCH 51/88] [AppVeyor] Deploy artifacts to GitHub Releases [AppVeyor] Deploy artifacts to GitHub Releases, take two. --- .appveyor.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index d2c7f4b37..cd6503e99 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -56,8 +56,15 @@ artifacts: - path: '*.zip' - path: xenia-cpu-ppc-test.log +before_deploy: + - ps: | + If (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { + $env:IS_NOT_PR = "true" + } + deploy: - provider: Environment + name: xenia-master release: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version) artifact: '*.zip' draft: false @@ -74,7 +81,6 @@ deploy: secure: /8he47z1WnPN7LcCTe5T5KMxxX0SmqFj9QMpeWEa3aZ64kMsfupOT/jKakqTM8af release: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version) description: 'Release build for xenia/$(APPVEYOR_REPO_COMMIT)' - artifact: '*.zip' draft: false prerelease: false on: From a7f3b110761216fc9749b8a9e843c7205bb96ca3 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Mon, 24 Jan 2022 02:54:05 -0600 Subject: [PATCH 52/88] [AppVeyor] Deploy artifacts to GitHub Releases. [AppVeyor] Deploy artifacts to GitHub Releases, take three. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index cd6503e99..6c5e5492f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -59,7 +59,7 @@ artifacts: before_deploy: - ps: | If (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { - $env:IS_NOT_PR = "true" + $env:is_not_pr = "true" } deploy: From bf20aa5f8e8ce8f7c587fe112bfc576ac0f21b90 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Mon, 24 Jan 2022 03:52:38 -0600 Subject: [PATCH 53/88] [AppVeyor] Deploy artifacts to GitHub Releases. [AppVeyor] Deploy artifacts to GitHub Releases, take four. I guess before_deploy is broken? --- .appveyor.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 6c5e5492f..57fd2c842 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -25,6 +25,12 @@ pull_requests: os: Visual Studio 2019 +init: + - ps: | + If (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { + $env:is_not_pr = "true" + } + install: - | vcpkg integrate remove @@ -56,12 +62,6 @@ artifacts: - path: '*.zip' - path: xenia-cpu-ppc-test.log -before_deploy: - - ps: | - If (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { - $env:is_not_pr = "true" - } - deploy: - provider: Environment name: xenia-master From 02bdea7b8b5afa4e2f38078e2eca140da2f5d842 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Mon, 24 Jan 2022 05:09:07 -0600 Subject: [PATCH 54/88] [AppVeyor] Better deploy tag/description. --- .appveyor.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 57fd2c842..d42506119 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -79,8 +79,13 @@ deploy: repository: xenia-project/release-builds-windows auth_token: secure: /8he47z1WnPN7LcCTe5T5KMxxX0SmqFj9QMpeWEa3aZ64kMsfupOT/jKakqTM8af + tag: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version) release: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version) - description: 'Release build for xenia/$(APPVEYOR_REPO_COMMIT)' + description: | + Windows release build for https://github.com/xenia-project/xenia/commit/$(APPVEYOR_REPO_COMMIT) + + $(APPVEYOR_REPO_COMMIT_MESSAGE) + $(APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED) draft: false prerelease: false on: From cb83479c5314ad008d6b238c57938dccb22218a0 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Mon, 24 Jan 2022 06:05:45 -0600 Subject: [PATCH 55/88] [AppVeyor] Fix deploy tag/description. --- .appveyor.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index d42506119..603687691 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -30,6 +30,12 @@ init: If (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { $env:is_not_pr = "true" } + If (-Not $env:APPVEYOR_REPO_COMMIT_MESSAGE) { + $env:APPVEYOR_REPO_COMMIT_MESSAGE = "" + } + If (-Not $env:APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED) { + $env:APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED = "" + } install: - | @@ -79,10 +85,10 @@ deploy: repository: xenia-project/release-builds-windows auth_token: secure: /8he47z1WnPN7LcCTe5T5KMxxX0SmqFj9QMpeWEa3aZ64kMsfupOT/jKakqTM8af - tag: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version) - release: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version) + tag: v$(appveyor_build_version) + release: v$(appveyor_build_version) description: | - Windows release build for https://github.com/xenia-project/xenia/commit/$(APPVEYOR_REPO_COMMIT) + Windows release build for https://github.com/xenia-project/xenia/commit/$(APPVEYOR_REPO_COMMIT). $(APPVEYOR_REPO_COMMIT_MESSAGE) $(APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED) From b3bb6687dbdff00adc04cd66e973bc7c2a364d1b Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Mon, 24 Jan 2022 06:54:11 -0600 Subject: [PATCH 56/88] [AppVeyor] Fix extended commit message. --- .appveyor.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 603687691..960ce805e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -30,11 +30,8 @@ init: If (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { $env:is_not_pr = "true" } - If (-Not $env:APPVEYOR_REPO_COMMIT_MESSAGE) { - $env:APPVEYOR_REPO_COMMIT_MESSAGE = "" - } If (-Not $env:APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED) { - $env:APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED = "" + $env:APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED = " " } install: @@ -91,6 +88,7 @@ deploy: Windows release build for https://github.com/xenia-project/xenia/commit/$(APPVEYOR_REPO_COMMIT). $(APPVEYOR_REPO_COMMIT_MESSAGE) + $(APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED) draft: false prerelease: false From e49916ea0a19e80982d3c21a7a31b28826ab5834 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Sun, 23 Jan 2022 12:55:11 -0600 Subject: [PATCH 57/88] [XAM] Improvements to profile r/w setting exports [XAM] Improvements to XamUserReadProfileSettingsEx/ XamUserWriteProfileSettings. - Unify X_USER_READ_PROFILE_SETTING and X_USER_WRITE_PROFILE_SETTING into X_USER_PROFILE_SETTING. - Clean up Setting serialization to use X_USER_PROFILE_SETTING_DATA instead of manual buffer copying. - Fix XamUserReadProfileSettingsEx case where user_index is non-zero and xuids are being used. - Skip unset settings in XamUserWriteProfileSettings_entry. --- src/xenia/kernel/xam/user_profile.h | 183 ++++++++++++++----------- src/xenia/kernel/xam/xam_user.cc | 199 ++++++++++++---------------- 2 files changed, 188 insertions(+), 194 deletions(-) diff --git a/src/xenia/kernel/xam/user_profile.h b/src/xenia/kernel/xam/user_profile.h index 82bb7b7a4..ad24b003d 100644 --- a/src/xenia/kernel/xam/user_profile.h +++ b/src/xenia/kernel/xam/user_profile.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -15,14 +15,69 @@ #include #include +#include "xenia/base/byte_stream.h" #include "xenia/xbox.h" namespace xe { namespace kernel { namespace xam { +struct X_USER_PROFILE_SETTING_DATA { + // UserProfile::Setting::Type. Appears to be 8-in-32 field, and the upper 24 + // are not always zeroed by the game. + uint8_t type; + uint8_t unk_1[3]; + xe::be unk_4; + // TODO(sabretooth): not sure if this is a union, but it seems likely. + // Haven't run into cases other than "binary data" yet. + union { + xe::be s32; + xe::be s64; + xe::be u32; + xe::be f64; + struct { + xe::be size; + xe::be ptr; + } unicode; + xe::be f32; + struct { + xe::be size; + xe::be ptr; + } binary; + xe::be filetime; + }; +}; +static_assert_size(X_USER_PROFILE_SETTING_DATA, 16); + +struct X_USER_PROFILE_SETTING { + xe::be from; + xe::be unk04; + union { + xe::be user_index; + xe::be xuid; + }; + xe::be setting_id; + xe::be unk14; + union { + uint8_t data_bytes[sizeof(X_USER_PROFILE_SETTING_DATA)]; + X_USER_PROFILE_SETTING_DATA data; + }; +}; +static_assert_size(X_USER_PROFILE_SETTING, 40); + class UserProfile { public: + class SettingByteStream : public ByteStream { + public: + SettingByteStream(uint32_t ptr, uint8_t* data, size_t data_length, + size_t offset = 0) + : ByteStream(data, data_length, offset), ptr_(ptr) {} + + uint32_t ptr() const { return static_cast(ptr_ + offset()); } + + private: + uint32_t ptr_; + }; struct Setting { enum class Type { CONTENT = 0, @@ -33,7 +88,7 @@ class UserProfile { FLOAT = 5, BINARY = 6, DATETIME = 7, - INVALID = 0xFF, + UNSET = 0xFF, }; union Key { uint32_t value; @@ -55,97 +110,77 @@ class UserProfile { size(size), is_set(is_set), loaded_title_id(0) {} - virtual size_t extra_size() const { return 0; } - virtual size_t Append(uint8_t* user_data, uint8_t* buffer, - uint32_t buffer_ptr, size_t buffer_offset) { - xe::store_and_swap(user_data + kTypeOffset, - static_cast(type)); - return buffer_offset; + virtual void Append(X_USER_PROFILE_SETTING_DATA* data, + SettingByteStream* stream) { + data->type = static_cast(type); } virtual std::vector Serialize() const { return std::vector(); } virtual void Deserialize(std::vector) {} bool is_title_specific() const { return (setting_id & 0x3F00) == 0x3F00; } - - protected: - const size_t kTypeOffset = 0; - const size_t kValueOffset = 8; - const size_t kPointerOffset = 12; }; struct Int32Setting : public Setting { Int32Setting(uint32_t setting_id, int32_t value) : Setting(setting_id, Type::INT32, 4, true), value(value) {} int32_t value; - size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr, - size_t buffer_offset) override { - buffer_offset = - Setting::Append(user_data, buffer, buffer_ptr, buffer_offset); - xe::store_and_swap(user_data + kValueOffset, value); - return buffer_offset; + void Append(X_USER_PROFILE_SETTING_DATA* data, + SettingByteStream* stream) override { + Setting::Append(data, stream); + data->s32 = value; } }; struct Int64Setting : public Setting { Int64Setting(uint32_t setting_id, int64_t value) : Setting(setting_id, Type::INT64, 8, true), value(value) {} int64_t value; - size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr, - size_t buffer_offset) override { - buffer_offset = - Setting::Append(user_data, buffer, buffer_ptr, buffer_offset); - xe::store_and_swap(user_data + kValueOffset, value); - return buffer_offset; + void Append(X_USER_PROFILE_SETTING_DATA* data, + SettingByteStream* stream) override { + Setting::Append(data, stream); + data->s64 = value; } }; struct DoubleSetting : public Setting { DoubleSetting(uint32_t setting_id, double value) : Setting(setting_id, Type::DOUBLE, 8, true), value(value) {} double value; - size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr, - size_t buffer_offset) override { - buffer_offset = - Setting::Append(user_data, buffer, buffer_ptr, buffer_offset); - xe::store_and_swap(user_data + kValueOffset, value); - return buffer_offset; + void Append(X_USER_PROFILE_SETTING_DATA* data, + SettingByteStream* stream) override { + Setting::Append(data, stream); + data->f64 = value; } }; struct UnicodeSetting : public Setting { UnicodeSetting(uint32_t setting_id, const std::u16string& value) : Setting(setting_id, Type::WSTRING, 8, true), value(value) {} std::u16string value; - size_t extra_size() const override { - return value.empty() ? 0 : 2 * (static_cast(value.size()) + 1); - } - size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr, - size_t buffer_offset) override { - buffer_offset = - Setting::Append(user_data, buffer, buffer_ptr, buffer_offset); - int32_t length; + void Append(X_USER_PROFILE_SETTING_DATA* data, + SettingByteStream* stream) override { + Setting::Append(data, stream); if (value.empty()) { - length = 0; - xe::store_and_swap(user_data + kValueOffset, 0); - xe::store_and_swap(user_data + kPointerOffset, 0); + data->unicode.size = 0; + data->unicode.ptr = 0; } else { - length = 2 * (static_cast(value.size()) + 1); - xe::store_and_swap(user_data + kValueOffset, length); - xe::store_and_swap( - user_data + kPointerOffset, - buffer_ptr + static_cast(buffer_offset)); - memcpy(buffer + buffer_offset, value.data(), length); + size_t count = value.size() + 1; + size_t size = 2 * count; + assert_true(size <= std::numeric_limits::max()); + data->unicode.size = static_cast(size); + data->unicode.ptr = stream->ptr(); + auto buffer = + reinterpret_cast(&stream->data()[stream->offset()]); + stream->Advance(size); + xe::copy_and_swap(buffer, (uint16_t*)value.data(), count); } - return buffer_offset + length; } }; struct FloatSetting : public Setting { FloatSetting(uint32_t setting_id, float value) : Setting(setting_id, Type::FLOAT, 4, true), value(value) {} float value; - size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr, - size_t buffer_offset) override { - buffer_offset = - Setting::Append(user_data, buffer, buffer_ptr, buffer_offset); - xe::store_and_swap(user_data + kValueOffset, value); - return buffer_offset; + void Append(X_USER_PROFILE_SETTING_DATA* data, + SettingByteStream* stream) override { + Setting::Append(data, stream); + data->f32 = value; } }; struct BinarySetting : public Setting { @@ -154,27 +189,19 @@ class UserProfile { BinarySetting(uint32_t setting_id, const std::vector& value) : Setting(setting_id, Type::BINARY, 8, true), value(value) {} std::vector value; - size_t extra_size() const override { - return static_cast(value.size()); - } - size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr, - size_t buffer_offset) override { - buffer_offset = - Setting::Append(user_data, buffer, buffer_ptr, buffer_offset); - int32_t length; + void Append(X_USER_PROFILE_SETTING_DATA* data, + SettingByteStream* stream) override { + Setting::Append(data, stream); if (value.empty()) { - length = 0; - xe::store_and_swap(user_data + kValueOffset, 0); - xe::store_and_swap(user_data + kPointerOffset, 0); + data->binary.size = 0; + data->binary.ptr = 0; } else { - length = static_cast(value.size()); - xe::store_and_swap(user_data + kValueOffset, length); - xe::store_and_swap( - user_data + kPointerOffset, - buffer_ptr + static_cast(buffer_offset)); - memcpy(buffer + buffer_offset, value.data(), length); + size_t size = value.size(); + assert_true(size <= std::numeric_limits::max()); + data->binary.size = static_cast(size); + data->binary.ptr = stream->ptr(); + stream->Write(value.data(), size); } - return buffer_offset + length; } std::vector Serialize() const override { return std::vector(value.data(), value.data() + value.size()); @@ -188,12 +215,10 @@ class UserProfile { DateTimeSetting(uint32_t setting_id, int64_t value) : Setting(setting_id, Type::DATETIME, 8, true), value(value) {} int64_t value; - size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr, - size_t buffer_offset) override { - buffer_offset = - Setting::Append(user_data, buffer, buffer_ptr, buffer_offset); - xe::store_and_swap(user_data + kValueOffset, value); - return buffer_offset; + void Append(X_USER_PROFILE_SETTING_DATA* data, + SettingByteStream* stream) override { + Setting::Append(data, stream); + data->filetime = value; } }; diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 625b977cd..2155504e8 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -14,6 +14,7 @@ #include "xenia/base/string_util.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xam/user_profile.h" #include "xenia/kernel/xam/xam_private.h" #include "xenia/kernel/xenumerator.h" #include "xenia/kernel/xthread.h" @@ -146,25 +147,14 @@ typedef struct { } X_USER_READ_PROFILE_SETTINGS; static_assert_size(X_USER_READ_PROFILE_SETTINGS, 8); -typedef struct { - xe::be from; - xe::be unk04; - xe::be user_index; - xe::be unk0C; - xe::be setting_id; - xe::be unk14; - uint8_t setting_data[16]; -} X_USER_READ_PROFILE_SETTING; -static_assert_size(X_USER_READ_PROFILE_SETTING, 40); - // https://github.com/oukiar/freestyledash/blob/master/Freestyle/Tools/Generic/xboxtools.cpp -uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, - uint32_t xuid_count, lpqword_t xuids, - uint32_t setting_count, - lpdword_t setting_ids, uint32_t unk, - lpdword_t buffer_size_ptr, - lpvoid_t buffer_ptr, - uint32_t overlapped_ptr) { +uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, + uint32_t xuid_count, be* xuids, + uint32_t setting_count, + be* setting_ids, uint32_t unk, + be* buffer_size_ptr, + uint8_t* buffer, + XAM_OVERLAPPED* overlapped) { if (!xuid_count) { assert_null(xuids); } else { @@ -173,8 +163,11 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, // TODO(gibbed): allow proper lookup of arbitrary XUIDs const auto& user_profile = kernel_state()->user_profile(); assert_true(static_cast(xuids[0]) == user_profile->xuid()); + // TODO(gibbed): we assert here, but in case a title passes xuid_count > 1 + // until it's implemented for release builds... + xuid_count = 1; } - assert_zero(unk); + assert_zero(unk); // probably flags // must have at least 1 to 32 settings if (setting_count < 1 || setting_count > 32) { @@ -188,35 +181,33 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, // if buffer size is non-zero, buffer pointer must be valid auto buffer_size = static_cast(*buffer_size_ptr); - if (buffer_size && !buffer_ptr) { + if (buffer_size && !buffer) { return X_ERROR_INVALID_PARAMETER; } uint32_t needed_header_size = 0; - uint32_t needed_extra_size = 0; + uint32_t needed_data_size = 0; for (uint32_t i = 0; i < setting_count; ++i) { - needed_header_size += sizeof(X_USER_READ_PROFILE_SETTING); + needed_header_size += sizeof(X_USER_PROFILE_SETTING); UserProfile::Setting::Key setting_key; setting_key.value = static_cast(setting_ids[i]); switch (static_cast(setting_key.type)) { case UserProfile::Setting::Type::WSTRING: - case UserProfile::Setting::Type::BINARY: { - needed_extra_size += setting_key.size; + case UserProfile::Setting::Type::BINARY: + needed_data_size += setting_key.size; break; - } - default: { + default: break; - } } } if (xuids) { - // needed_header_size *= xuid_count; - // needed_extra_size *= !xuid_count; + needed_header_size *= xuid_count; + needed_data_size *= xuid_count; } needed_header_size += sizeof(X_USER_READ_PROFILE_SETTINGS); - uint32_t needed_size = needed_header_size + needed_extra_size; - if (!buffer_ptr || buffer_size < needed_size) { + uint32_t needed_size = needed_header_size + needed_data_size; + if (!buffer || buffer_size < needed_size) { if (!buffer_size) { *buffer_size_ptr = needed_size; } @@ -226,11 +217,12 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, // Title ID = 0 means us. // 0xfffe07d1 = profile? - if (user_index) { + if (!xuids && user_index) { // Only support user 0. - if (overlapped_ptr) { - kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, - X_ERROR_NO_SUCH_USER); + if (overlapped) { + kernel_state()->CompleteOverlappedImmediate( + kernel_state()->memory()->HostToGuestVirtual(overlapped), + X_ERROR_NO_SUCH_USER); return X_ERROR_IO_PENDING; } return X_ERROR_NO_SUCH_USER; @@ -257,49 +249,49 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, } if (any_missing) { // TODO(benvanik): don't fail? most games don't even check! - if (overlapped_ptr) { - kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, - X_ERROR_INVALID_PARAMETER); + if (overlapped) { + kernel_state()->CompleteOverlappedImmediate( + kernel_state()->memory()->HostToGuestVirtual(overlapped), + X_ERROR_INVALID_PARAMETER); return X_ERROR_IO_PENDING; } return X_ERROR_INVALID_PARAMETER; } - auto out_header = buffer_ptr.as(); + auto out_header = reinterpret_cast(buffer); + auto out_setting = reinterpret_cast(&out_header[1]); out_header->setting_count = static_cast(setting_count); - out_header->settings_ptr = buffer_ptr.guest_address() + 8; + out_header->settings_ptr = + kernel_state()->memory()->HostToGuestVirtual(out_setting); - auto out_setting = - reinterpret_cast(&out_header[1]); - - size_t buffer_offset = needed_header_size; + UserProfile::SettingByteStream out_stream( + kernel_state()->memory()->HostToGuestVirtual(buffer), buffer, buffer_size, + needed_header_size); for (uint32_t n = 0; n < setting_count; ++n) { uint32_t setting_id = setting_ids[n]; auto setting = user_profile->GetSetting(setting_id); - std::memset(out_setting, 0, sizeof(X_USER_READ_PROFILE_SETTING)); + std::memset(out_setting, 0, sizeof(X_USER_PROFILE_SETTING)); out_setting->from = !setting || !setting->is_set ? 0 : setting->is_title_specific() ? 2 : 1; - out_setting->user_index = static_cast(user_index); + if (xuids) { + out_setting->xuid = user_profile->xuid(); + } else { + out_setting->user_index = static_cast(user_index); + } out_setting->setting_id = setting_id; if (setting && setting->is_set) { - buffer_offset = - setting->Append(&out_setting->setting_data[0], buffer_ptr, - buffer_ptr.guest_address(), buffer_offset); + setting->Append(&out_setting->data, &out_stream); } - // TODO(benvanik): why did I do this? - /*else { - std::memset(&out_setting->setting_data[0], 0, - sizeof(out_setting->setting_data)); - }*/ ++out_setting; } - if (overlapped_ptr) { - kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, - X_ERROR_SUCCESS); + if (overlapped) { + kernel_state()->CompleteOverlappedImmediate( + kernel_state()->memory()->HostToGuestVirtual(overlapped), + X_ERROR_SUCCESS); return X_ERROR_IO_PENDING; } return X_ERROR_SUCCESS; @@ -308,57 +300,35 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index, dword_result_t XamUserReadProfileSettings_entry( dword_t title_id, dword_t user_index, dword_t xuid_count, lpqword_t xuids, dword_t setting_count, lpdword_t setting_ids, lpdword_t buffer_size_ptr, - lpvoid_t buffer_ptr, dword_t overlapped_ptr) { - return xeXamUserReadProfileSettingsEx( - title_id, user_index, xuid_count, xuids, setting_count, setting_ids, 0, - buffer_size_ptr, buffer_ptr, overlapped_ptr); + lpvoid_t buffer_ptr, pointer_t overlapped) { + return XamUserReadProfileSettingsEx(title_id, user_index, xuid_count, xuids, + setting_count, setting_ids, 0, + buffer_size_ptr, buffer_ptr, overlapped); } DECLARE_XAM_EXPORT1(XamUserReadProfileSettings, kUserProfiles, kImplemented); dword_result_t XamUserReadProfileSettingsEx_entry( dword_t title_id, dword_t user_index, dword_t xuid_count, lpqword_t xuids, dword_t setting_count, lpdword_t setting_ids, lpdword_t buffer_size_ptr, - dword_t unk_2, lpvoid_t buffer_ptr, dword_t overlapped_ptr) { - return xeXamUserReadProfileSettingsEx( - title_id, user_index, xuid_count, xuids, setting_count, setting_ids, - unk_2, buffer_size_ptr, buffer_ptr, overlapped_ptr); + dword_t unk_2, lpvoid_t buffer_ptr, pointer_t overlapped) { + return XamUserReadProfileSettingsEx(title_id, user_index, xuid_count, xuids, + setting_count, setting_ids, unk_2, + buffer_size_ptr, buffer_ptr, overlapped); } DECLARE_XAM_EXPORT1(XamUserReadProfileSettingsEx, kUserProfiles, kImplemented); -typedef struct { - xe::be from; - xe::be unk_04; - xe::be unk_08; - xe::be unk_0c; - xe::be setting_id; - xe::be unk_14; - - // UserProfile::Setting::Type. Appears to be 8-in-32 field, and the upper 24 - // are not always zeroed by the game. - uint8_t type; - - xe::be unk_1c; - // TODO(sabretooth): not sure if this is a union, but it seems likely. - // Haven't run into cases other than "binary data" yet. - union { - struct { - xe::be length; - xe::be ptr; - } binary; - }; -} X_USER_WRITE_PROFILE_SETTING; - dword_result_t XamUserWriteProfileSettings_entry( dword_t title_id, dword_t user_index, dword_t setting_count, - pointer_t settings, dword_t overlapped_ptr) { + pointer_t settings, + pointer_t overlapped) { if (!setting_count || !settings) { return X_ERROR_INVALID_PARAMETER; } if (user_index) { // Only support user 0. - if (overlapped_ptr) { - kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, + if (overlapped) { + kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_NO_SUCH_USER); return X_ERROR_IO_PENDING; } @@ -369,39 +339,39 @@ dword_result_t XamUserWriteProfileSettings_entry( const auto& user_profile = kernel_state()->user_profile(); for (uint32_t n = 0; n < setting_count; ++n) { - const X_USER_WRITE_PROFILE_SETTING& settings_data = settings[n]; + const X_USER_PROFILE_SETTING& setting = settings[n]; + + auto setting_type = + static_cast(setting.data.type); + if (setting_type == UserProfile::Setting::Type::UNSET) { + continue; + } + XELOGD( "XamUserWriteProfileSettings: setting index [{}]:" " from={} setting_id={:08X} data.type={}", - n, (uint32_t)settings_data.from, (uint32_t)settings_data.setting_id, - settings_data.type); + n, (uint32_t)setting.from, (uint32_t)setting.setting_id, + setting.data.type); - xam::UserProfile::Setting::Type settingType = - static_cast(settings_data.type); - - switch (settingType) { + switch (setting_type) { case UserProfile::Setting::Type::CONTENT: case UserProfile::Setting::Type::BINARY: { - uint8_t* settings_data_ptr = kernel_state()->memory()->TranslateVirtual( - settings_data.binary.ptr); - size_t settings_data_len = settings_data.binary.length; - std::vector data_vec; - - if (settings_data.binary.ptr) { + uint8_t* binary_ptr = + kernel_state()->memory()->TranslateVirtual(setting.data.binary.ptr); + size_t binary_size = setting.data.binary.size; + std::vector bytes; + if (setting.data.binary.ptr) { // Copy provided data - data_vec.resize(settings_data_len); - std::memcpy(data_vec.data(), settings_data_ptr, settings_data_len); + bytes.resize(binary_size); + std::memcpy(bytes.data(), binary_ptr, binary_size); } else { // Data pointer was NULL, so just fill with zeroes - data_vec.resize(settings_data_len, 0); + bytes.resize(binary_size, 0); } - user_profile->AddSetting( std::make_unique( - settings_data.setting_id, data_vec)); - + setting.setting_id, bytes)); } break; - case UserProfile::Setting::Type::WSTRING: case UserProfile::Setting::Type::DOUBLE: case UserProfile::Setting::Type::FLOAT: @@ -410,14 +380,13 @@ dword_result_t XamUserWriteProfileSettings_entry( case UserProfile::Setting::Type::DATETIME: default: { XELOGE("XamUserWriteProfileSettings: Unimplemented data type {}", - settingType); + setting_type); } break; }; } - if (overlapped_ptr) { - kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, - X_ERROR_SUCCESS); + if (overlapped) { + kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS); return X_ERROR_IO_PENDING; } return X_ERROR_SUCCESS; From 08a2a5b7ac1939e17163c537dff4c306b7003f24 Mon Sep 17 00:00:00 2001 From: Margen67 Date: Mon, 24 Jan 2022 07:19:04 -0800 Subject: [PATCH 58/88] [AppVeyor] Upgrade to VS2022 --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 960ce805e..9083c80b9 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -23,7 +23,7 @@ skip_branch_with_pr: true pull_requests: do_not_increment_build_number: true -os: Visual Studio 2019 +os: Visual Studio 2022 init: - ps: | From c481b0483c0755b7e49dfc7f47b9bf4522911cdf Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Tue, 25 Jan 2022 08:05:51 -0600 Subject: [PATCH 59/88] Add links to release-builds-windows. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 24599aed2..8d58acfcb 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ Discussing illegal activities will get you banned. ## Status -Buildbot | Status --------- | ------ -[Windows](https://ci.appveyor.com/project/benvanik/xenia/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/ftqiy86kdfawyx3a/branch/master?svg=true)](https://ci.appveyor.com/project/benvanik/xenia/branch/master) +Buildbot | Status | Releases +-------- | ------ | -------- +[Windows](https://ci.appveyor.com/project/benvanik/xenia/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/ftqiy86kdfawyx3a/branch/master?svg=true)](https://ci.appveyor.com/project/benvanik/xenia/branch/master) | [Latest](https://github.com/xenia-project/release-builds-windows/releases/latest) â—¦ [All](https://github.com/xenia-project/release-builds-windows/releases) [Linux](https://cloud.drone.io/xenia-project/xenia) | [![Build status](https://cloud.drone.io/api/badges/xenia-project/xenia/status.svg)](https://cloud.drone.io/xenia-project/xenia) Quite a few real games run. Quite a few don't. From 16be84cdb06c7aad556b527c41a9726f6a6d0fe9 Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Sat, 22 Jan 2022 20:52:50 +0100 Subject: [PATCH 60/88] [CI] Add valgrind step to drone --- .drone.star | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/.drone.star b/.drone.star index 09d933902..4c86fb5f6 100644 --- a/.drone.star +++ b/.drone.star @@ -185,6 +185,17 @@ def pipeline_linux_desktop(name, image, arch, cc): # # Building # + + { + 'name': 'build-premake-debug-tests', + 'image': image, + 'volumes': [volume_build('premake')], + 'commands': [ + command_cc(cc), + './xenia-build build --no_premake -j$(nproc) --config=Debug --target=xenia-base-tests', + ], + 'depends_on': ['toolchain-premake'], + }, { 'name': 'build-premake-debug-all', 'image': image, @@ -193,7 +204,7 @@ def pipeline_linux_desktop(name, image, arch, cc): command_cc(cc), './xenia-build build --no_premake -j$(nproc) --config=Debug', ], - 'depends_on': ['toolchain-premake'], + 'depends_on': ['build-premake-debug-tests'], }, { @@ -206,7 +217,6 @@ def pipeline_linux_desktop(name, image, arch, cc): ], 'depends_on': ['toolchain-premake'], }, - { 'name': 'build-premake-release-all', 'image': image, @@ -241,7 +251,6 @@ def pipeline_linux_desktop(name, image, arch, cc): ], 'depends_on': ['toolchain-cmake'], }, - { 'name': 'build-cmake-release-all', 'image': image, @@ -258,8 +267,19 @@ def pipeline_linux_desktop(name, image, arch, cc): # # Tests # + { - 'name': 'test-premake', + 'name': 'test-premake-debug-valgrind', + 'image': image, + 'volumes': [volume_build('premake')], + 'commands': [ + 'valgrind --error-exitcode=99 ./build/bin/Linux/Debug/xenia-base-tests', + ], + 'depends_on': ['build-premake-debug-tests'], + }, + + { + 'name': 'test-premake-release', 'image': image, 'volumes': [volume_build('premake')], 'commands': [ @@ -269,7 +289,7 @@ def pipeline_linux_desktop(name, image, arch, cc): }, { - 'name': 'test-cmake', + 'name': 'test-cmake-release', 'image': image, 'volumes': [volume_build('cmake')], 'commands': [ @@ -282,6 +302,7 @@ def pipeline_linux_desktop(name, image, arch, cc): # # Stat # + { 'name': 'stat', 'image': image, From 464795eece967336a8f65d7148d506517984f051 Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Sat, 22 Jan 2022 21:04:38 +0100 Subject: [PATCH 61/88] [CI] No more GCC Release builds GNU ld is awfully slow, even more so with LTO --- .drone.star | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.drone.star b/.drone.star index 4c86fb5f6..50b5c9ec3 100644 --- a/.drone.star +++ b/.drone.star @@ -1,8 +1,8 @@ def main(ctx): return [ pipeline_lint(), - pipeline_linux_desktop('x86_64-linux-clang', image_linux_x86_64(), 'amd64', 'clang'), - pipeline_linux_desktop('x86_64-linux-gcc', image_linux_x86_64(), 'amd64', 'gcc'), + pipeline_linux_desktop('x86_64-linux-clang', image_linux_x86_64(), 'amd64', 'clang', True), + pipeline_linux_desktop('x86_64-linux-gcc', image_linux_x86_64(), 'amd64', 'gcc', False), # GCC release linking is really slow pipeline_android('x86_64-android', image_linux_x86_64(), 'amd64', 'Android-x86_64'), pipeline_android('aarch64-android', image_linux_x86_64(), 'amd64', 'Android-ARM64'), ] @@ -104,7 +104,7 @@ def pipeline_lint(): ], } -def pipeline_linux_desktop(name, image, arch, cc): +def pipeline_linux_desktop(name, image, arch, cc, build_release_all): return { 'kind': 'pipeline', 'type': 'docker', @@ -217,6 +217,7 @@ def pipeline_linux_desktop(name, image, arch, cc): ], 'depends_on': ['toolchain-premake'], }, + ] + ([ { 'name': 'build-premake-release-all', 'image': image, @@ -227,6 +228,7 @@ def pipeline_linux_desktop(name, image, arch, cc): ], 'depends_on': ['build-premake-release-tests'], }, + ] if build_release_all else []) + [ { 'name': 'build-cmake-debug-all', @@ -251,6 +253,7 @@ def pipeline_linux_desktop(name, image, arch, cc): ], 'depends_on': ['toolchain-cmake'], }, + ] + ([ { 'name': 'build-cmake-release-all', 'image': image, @@ -262,6 +265,7 @@ def pipeline_linux_desktop(name, image, arch, cc): ], 'depends_on': ['build-cmake-release-tests'], }, + ] if build_release_all else []) + [ # @@ -333,11 +337,15 @@ def pipeline_linux_desktop(name, image, arch, cc): ''' ], 'depends_on': [ - 'build-premake-debug-all', - 'build-premake-release-all', - 'build-cmake-debug-all', - 'build-cmake-release-all', - ], + 'build-premake-debug-all', + 'build-cmake-debug-all', + ] + ([ + 'build-premake-release-all', + 'build-cmake-release-all', + ] if build_release_all else [ + 'build-premake-release-tests', + 'build-cmake-release-tests', + ]), }, ], } From dbbf40120513396eaa4c8783d0741d2528ed2971 Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Sat, 22 Jan 2022 20:53:18 +0100 Subject: [PATCH 62/88] [Base] Align test memory --- src/xenia/base/testing/memory_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xenia/base/testing/memory_test.cc b/src/xenia/base/testing/memory_test.cc index 0594b53d9..3f3c1f963 100644 --- a/src/xenia/base/testing/memory_test.cc +++ b/src/xenia/base/testing/memory_test.cc @@ -455,8 +455,8 @@ TEST_CASE("copy_and_swap_64_unaligned", "[copy_and_swap]") { TEST_CASE("copy_and_swap_16_in_32_aligned", "[copy_and_swap]") { constexpr size_t count = 17; - std::array src{}; - std::array dst{}; + alignas(16) std::array src{}; + alignas(16) std::array dst{}; // Check alignment (if this fails, adjust allocation) REQUIRE((reinterpret_cast(src.data()) & 0xF) == 0); From b76e0a1bd805ab70983f901b632fe80b4ccf676a Mon Sep 17 00:00:00 2001 From: Margen67 Date: Wed, 15 Dec 2021 15:12:03 -0800 Subject: [PATCH 63/88] Update to new issue forms --- .github/ISSUE_TEMPLATE/bug_report.md | 32 --------------- .github/ISSUE_TEMPLATE/bug_report.yaml | 54 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 32 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 673faa6af..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Bug report -about: Template for bug reports. -title: '' -labels: '' -assignees: '' - ---- - - - -[//]: # (Describe what's going wrong:) - -[//]: # (Describe what should happen:) - -[//]: # (If applicable, provide a callstack here - esp. for crashes) - -[//]: # (If applicable, upload a logfile and link it here) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000..949966176 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,54 @@ +name: Bug report +description: Template for bug reports. +title: 'Bug: ' +body: + - type: markdown + attributes: + value: | + Try to create a very concise title that's straight to the point. + + **THIS IS NOT A SUPPORT FORUM!** For support, first read the wiki: https://github.com/xenia-project/xenia/wiki + If your question wasn't answered there or you need help, proceed to #help in the Discord server: https://discord.gg/Q9mxZf9 + + DO NOT CREATE ISSUES ABOUT SPECIFIC GAMES IN THIS REPOSITORY! + A game specific issue would be e.g. "Game X crashes after you hit a character a certain way" + A Xenia issue would be e.g. "Kernel export NtDoSomething does nothing" + For specific games, visit https://github.com/xenia-project/game-compatibility#game-compatibility + - type: checkboxes + id: validation + attributes: + label: Validation + options: + - label: I've read the [FAQ](https://github.com/xenia-project/xenia/wiki/FAQ). + required: true + - label: The Xenia build used is from the master branch (not MLBS/AlexVS/Canary/pull requests, etc.) + required: true + - label: This issue isn't for tech support (help with Xenia). + required: true + - label: If this issue occurs in a specific game, I've done analysis to locate the faulty subsystem of the emulator and a potential reason in it. + required: true + - label: I've checked if this issue hasn't already been reported. + required: true + - label: 'My device meets the minimum requirements: https://github.com/xenia-project/xenia/wiki/Quickstart#system-requirements' + required: true + - label: '(If building) I have read the building doc: https://github.com/xenia-project/xenia/blob/master/docs/building.md' + - type: textarea + id: problem + attributes: + label: Describe what's going wrong + validations: + required: true + - type: textarea + id: what-should-happen + attributes: + label: Describe what should happen + validations: + required: true + - type: textarea + id: callstack + attributes: + label: If applicable, provide a callstack here, especially for crashes + - type: textarea + id: logfile + attributes: + label: If applicable, upload a logfile and link it here From 7418011ab5f1553fd17d5e210ca2743dacef30f0 Mon Sep 17 00:00:00 2001 From: Caroline Joy Bell Date: Sun, 23 Jan 2022 16:38:39 -0800 Subject: [PATCH 64/88] Reflect the closure of #1333 #1333 has been closed, so the README should reflect that. This line can be replaced with another suggestion from the good-first-issue tag. I suggest contributing to #1526 in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d58acfcb..c0d0b5c8e 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Fixes and optimizations are always welcome (please!), but in addition to that there are some major work areas still untouched: * Help work through [missing functionality/bugs in games](https://github.com/xenia-project/xenia/labels/compat) -* Add input drivers for [third-party controllers](https://github.com/xenia-project/xenia/issues/1333) +* Reduce the size of Xenia's [huge log files](https://github.com/xenia-project/xenia/issues/1526) * Skilled with Linux? A strong contributor is needed to [help with porting](https://github.com/xenia-project/xenia/labels/platform-linux) See more projects [good for contributors](https://github.com/xenia-project/xenia/labels/good%20first%20issue). It's a good idea to ask on Discord and check the issues page before beginning work on From bd9a290b30d1ab1315d2df8049475df8c4eaad55 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Sun, 23 Jan 2022 10:25:01 -0800 Subject: [PATCH 65/88] [x64] Add `GFNI`-based optimization for `VECTOR_SH{R,L}_V128(Int8)` In the `Int8` case of `VECTOR_SH{R,L}_V128`, when all the values are the same, then a single-instruction `gf2p8affineqb` can be emitted that does an int8-based arithmetic-shift, utilizing GF(8) arithmetic. More info here: https://wunkolo.github.io/post/2020/11/gf2p8affineqb-int8-shifting/ Also fixes the iteration-type for when detecting if all of the simd lanes are the same value(was iterating `u16` and not `u8`) --- src/xenia/cpu/backend/x64/x64_seq_vector.cc | 42 ++++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_seq_vector.cc b/src/xenia/cpu/backend/x64/x64_seq_vector.cc index c428018ba..72761aa6f 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_vector.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_vector.cc @@ -731,6 +731,25 @@ struct VECTOR_SHL_V128 static void EmitInt8(X64Emitter& e, const EmitArgType& i) { // TODO(benvanik): native version (with shift magic). if (i.src2.is_constant) { + if (e.IsFeatureEnabled(kX64EmitGFNI)) { + const auto& shamt = i.src2.constant(); + bool all_same = true; + for (size_t n = 0; n < 16 - n; ++n) { + if (shamt.u8[n] != shamt.u8[n + 1]) { + all_same = false; + break; + } + } + if (all_same) { + // Every count is the same, so we can use gf2p8affineqb. + const uint8_t shift_amount = shamt.u8[0]; + const uint64_t shift_matrix = + 0x0102040810204080 >> (shift_amount * 8); + e.vgf2p8affineqb(i.dest, i.src1, + e.StashConstantXmm(0, vec128q(shift_matrix)), 0); + return; + } + } e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant())); } else { e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2)); @@ -920,6 +939,25 @@ struct VECTOR_SHR_V128 static void EmitInt8(X64Emitter& e, const EmitArgType& i) { // TODO(benvanik): native version (with shift magic). if (i.src2.is_constant) { + if (e.IsFeatureEnabled(kX64EmitGFNI)) { + const auto& shamt = i.src2.constant(); + bool all_same = true; + for (size_t n = 0; n < 16 - n; ++n) { + if (shamt.u8[n] != shamt.u8[n + 1]) { + all_same = false; + break; + } + } + if (all_same) { + // Every count is the same, so we can use gf2p8affineqb. + const uint8_t shift_amount = shamt.u8[0]; + const uint64_t shift_matrix = 0x0102040810204080 + << (shift_amount * 8); + e.vgf2p8affineqb(i.dest, i.src1, + e.StashConstantXmm(0, vec128q(shift_matrix)), 0); + return; + } + } e.lea(e.GetNativeParam(1), e.StashConstantXmm(1, i.src2.constant())); } else { e.lea(e.GetNativeParam(1), e.StashXmm(1, i.src2)); @@ -1087,8 +1125,8 @@ struct VECTOR_SHA_V128 if (e.IsFeatureEnabled(kX64EmitGFNI)) { const auto& shamt = i.src2.constant(); bool all_same = true; - for (size_t n = 0; n < 8 - n; ++n) { - if (shamt.u16[n] != shamt.u16[n + 1]) { + for (size_t n = 0; n < 16 - n; ++n) { + if (shamt.u8[n] != shamt.u8[n + 1]) { all_same = false; break; } From f8350b5536bf0c80a40135b873971685d31ee926 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Sun, 23 Jan 2022 11:14:31 -0800 Subject: [PATCH 66/88] [x64] Add `VECTOR_SH{R,L}_I8_SAME_CONSTANT` unit test This is to target the new GFNI-based optimization for the Int8 case. --- src/xenia/cpu/testing/vector_shl_test.cc | 22 ++++++++++++++++++++++ src/xenia/cpu/testing/vector_shr_test.cc | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/xenia/cpu/testing/vector_shl_test.cc b/src/xenia/cpu/testing/vector_shl_test.cc index cdf61c4f8..bb821edca 100644 --- a/src/xenia/cpu/testing/vector_shl_test.cc +++ b/src/xenia/cpu/testing/vector_shl_test.cc @@ -58,6 +58,28 @@ TEST_CASE("VECTOR_SHL_I8_CONSTANT", "[instr]") { }); } +// This targets the "all_same" optimization of the Int8 specialization of +// VECTOR_SHL_V128 +TEST_CASE("VECTOR_SHL_I8_SAME_CONSTANT", "[instr]") { + TestFunction test([](HIRBuilder& b) { + StoreVR( + b, 3, + b.VectorShl(LoadVR(b, 4), b.LoadConstantVec128(vec128b(5)), INT8_TYPE)); + b.Return(); + }); + test.Run( + [](PPCContext* ctx) { + ctx->v[4] = vec128b(0x7E, 0x7E, 0x7E, 0x7F, 0x80, 0xFF, 0x01, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + }, + [](PPCContext* ctx) { + auto result = ctx->v[3]; + REQUIRE(result == vec128b(0xC0, 0xC0, 0xC0, 0xE0, 0x00, 0xE0, 0x20, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00)); + }); +} + TEST_CASE("VECTOR_SHL_I16", "[instr]") { TestFunction test([](HIRBuilder& b) { StoreVR(b, 3, b.VectorShl(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE)); diff --git a/src/xenia/cpu/testing/vector_shr_test.cc b/src/xenia/cpu/testing/vector_shr_test.cc index 8ee36ce73..000b0234e 100644 --- a/src/xenia/cpu/testing/vector_shr_test.cc +++ b/src/xenia/cpu/testing/vector_shr_test.cc @@ -58,6 +58,28 @@ TEST_CASE("VECTOR_SHR_I8_CONSTANT", "[instr]") { }); } +// This targets the "all_same" optimization of the Int8 specialization of +// VECTOR_SHR_V128 +TEST_CASE("VECTOR_SHR_I8_SAME_CONSTANT", "[instr]") { + TestFunction test([](HIRBuilder& b) { + StoreVR( + b, 3, + b.VectorShr(LoadVR(b, 4), b.LoadConstantVec128(vec128b(3)), INT8_TYPE)); + b.Return(); + }); + test.Run( + [](PPCContext* ctx) { + ctx->v[4] = vec128b(0x7E, 0x7E, 0x7E, 0x7F, 0x80, 0xFF, 0x01, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + }, + [](PPCContext* ctx) { + auto result = ctx->v[3]; + REQUIRE(result == vec128b(0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x1F, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00)); + }); +} + TEST_CASE("VECTOR_SHR_I16", "[instr]") { TestFunction test([](HIRBuilder& b) { StoreVR(b, 3, b.VectorShr(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE)); From 24205ee860e2066bd1e6ccd53b499a07802d80cf Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 24 Jan 2022 08:06:05 -0800 Subject: [PATCH 67/88] [x64] Fix `VECTOR_SH{L,R,A}_V128(Int8)` masking [AltiVec](https://www.nxp.com/docs/en/reference-manual/ALTIVECPEM.pdf) doc says that it just uses the lower `log2(n)` bits of the shift-amount rather than the whole element-sized value. So there is no need to handle an overflow. Also adjusts 64-bit literals to utilize the explicit `UINT64_C` type. --- src/xenia/cpu/backend/x64/x64_seq_vector.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/xenia/cpu/backend/x64/x64_seq_vector.cc b/src/xenia/cpu/backend/x64/x64_seq_vector.cc index 72761aa6f..4daea260b 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_vector.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_vector.cc @@ -742,9 +742,9 @@ struct VECTOR_SHL_V128 } if (all_same) { // Every count is the same, so we can use gf2p8affineqb. - const uint8_t shift_amount = shamt.u8[0]; + const uint8_t shift_amount = shamt.u8[0] & 0b111; const uint64_t shift_matrix = - 0x0102040810204080 >> (shift_amount * 8); + UINT64_C(0x0102040810204080) >> (shift_amount * 8); e.vgf2p8affineqb(i.dest, i.src1, e.StashConstantXmm(0, vec128q(shift_matrix)), 0); return; @@ -950,8 +950,8 @@ struct VECTOR_SHR_V128 } if (all_same) { // Every count is the same, so we can use gf2p8affineqb. - const uint8_t shift_amount = shamt.u8[0]; - const uint64_t shift_matrix = 0x0102040810204080 + const uint8_t shift_amount = shamt.u8[0] & 0b111; + const uint64_t shift_matrix = UINT64_C(0x0102040810204080) << (shift_amount * 8); e.vgf2p8affineqb(i.dest, i.src1, e.StashConstantXmm(0, vec128q(shift_matrix)), 0); @@ -1133,12 +1133,11 @@ struct VECTOR_SHA_V128 } if (all_same) { // Every count is the same, so we can use gf2p8affineqb. - const uint8_t shift_amount = shamt.u8[0]; + const uint8_t shift_amount = shamt.u8[0] & 0b111; const uint64_t shift_matrix = - shift_amount < 8 - ? (0x0102040810204080ULL << (shift_amount * 8)) | - (0x8080808080808080ULL >> (64 - shift_amount * 8)) - : 0x8080808080808080ULL; + (UINT64_C(0x0102040810204080) << (shift_amount * 8)) | + (UINT64_C(0x8080808080808080) >> (64 - shift_amount * 8)); + ; e.vgf2p8affineqb(i.dest, i.src1, e.StashConstantXmm(0, vec128q(shift_matrix)), 0); return; From 1d51b574ec96f0aefbd388f193b8453b4c15d8e3 Mon Sep 17 00:00:00 2001 From: TranzRail Date: Sun, 23 Jan 2022 09:23:43 -0800 Subject: [PATCH 68/88] [Kernel] Add PVR opcode (includes cvars support) --- src/xenia/cpu/cpu_flags.cc | 7 +++++++ src/xenia/cpu/cpu_flags.h | 3 +++ src/xenia/cpu/ppc/ppc_emit_control.cc | 10 ++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/xenia/cpu/cpu_flags.cc b/src/xenia/cpu/cpu_flags.cc index fab5ef2f0..614dabae8 100644 --- a/src/xenia/cpu/cpu_flags.cc +++ b/src/xenia/cpu/cpu_flags.cc @@ -38,6 +38,13 @@ DEFINE_bool( DEFINE_bool(validate_hir, false, "Perform validation checks on the HIR during compilation.", "CPU"); +DEFINE_uint64( + pvr, 0x710700, + "Processor version and revision number.\nBits 0 to 15 are the version " + "number.\nBits 16 to 31 are the revision number.\nNote: Some XEXs (such as " + "mfgbootlauncher.xex) may check for a value that's less than 0x710700.", + "CPU"); + // Breakpoints: DEFINE_uint64(break_on_instruction, 0, "int3 before the given guest address is executed.", "CPU"); diff --git a/src/xenia/cpu/cpu_flags.h b/src/xenia/cpu/cpu_flags.h index 65a15a87d..811e97756 100644 --- a/src/xenia/cpu/cpu_flags.h +++ b/src/xenia/cpu/cpu_flags.h @@ -26,6 +26,9 @@ DECLARE_bool(disable_global_lock); DECLARE_bool(validate_hir); +DECLARE_uint64(pvr); + +// Breakpoints: DECLARE_uint64(break_on_instruction); DECLARE_int32(break_condition_gpr); DECLARE_uint64(break_condition_value); diff --git a/src/xenia/cpu/ppc/ppc_emit_control.cc b/src/xenia/cpu/ppc/ppc_emit_control.cc index ec09ea458..416f30578 100644 --- a/src/xenia/cpu/ppc/ppc_emit_control.cc +++ b/src/xenia/cpu/ppc/ppc_emit_control.cc @@ -620,6 +620,16 @@ int InstrEmit_mfspr(PPCHIRBuilder& f, const InstrData& i) { // TBU v = f.Shr(f.LoadClock(), 32); break; + case 287: + // [ Processor Version Register (PVR) ] + // PVR is a 32 bit, read-only register within the supervisor level. + // Bits 0 to 15 are the version number. + // Bits 16 to 31 are the revision number. + // Known Values: 0x710600?, 0x710700, 0x710800 (Corona?); + // Note: Some XEXs (such as mfgbootlauncher.xex) may check for a value + // that's less than 0x710700. + v = f.LoadConstantUint64(cvars::pvr); + break; default: XEINSTRNOTIMPLEMENTED(); return 1; From 372bdd3ec97007eceef85a886f1f67e4fe9eec16 Mon Sep 17 00:00:00 2001 From: Pseudo-Kernel Date: Sat, 29 Jan 2022 17:49:00 +0900 Subject: [PATCH 69/88] [APU] XMA: Fix audio loop handling. Handles audio loop if loop_start < loop_end. Need to handle additional cases like loop_start > loop_end. --- src/xenia/apu/xma_context.cc | 56 +++++++++++++++++++++++++++++++++--- src/xenia/apu/xma_context.h | 2 ++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index a9662a0d8..e9047654a 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -177,6 +177,25 @@ void XmaContext::SwapInputBuffer(XMA_CONTEXT_DATA* data) { data->input_buffer_read_offset = 0; } +bool XmaContext::TrySetupNextLoop(XMA_CONTEXT_DATA* data, + bool ignore_input_buffer_offset) { + // Setup the input buffer offset if next loop exists. + // TODO(Pseudo-Kernel): Need to handle loop in the following cases. + // 1. loop_start == loop_end == 0 + // 2. loop_start > loop_end && loop_count > 0 + if (data->loop_count > 0 && data->loop_start < data->loop_end && + (ignore_input_buffer_offset || + data->input_buffer_read_offset >= data->loop_end)) { + // Loop back to the beginning. + data->input_buffer_read_offset = data->loop_start; + if (data->loop_count < 255) { + data->loop_count--; + } + return true; + } + return false; +} + /* void XmaContext::NextPacket( uint8_t* input_buffer, @@ -364,6 +383,7 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { assert_false(data->stop_when_done); assert_false(data->interrupt_when_done); static int total_samples = 0; + bool reuse_input_buffer = false; // Decode until we can't write any more data. while (output_remaining_bytes > 0) { if (!data->input_buffer_0_valid && !data->input_buffer_1_valid) { @@ -371,6 +391,10 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { break; } + // Setup the input buffer if we are at loop_end. + // The input buffer must not be swapped out until all loops are processed. + reuse_input_buffer = TrySetupNextLoop(data, false); + // assert_true(packets_skip_ == 0); // assert_true(split_frame_len_ == 0); // assert_true(split_frame_len_partial_ == 0); @@ -392,7 +416,13 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { packets_skip_--; packet_idx++; if (packet_idx >= current_input_packet_count) { - SwapInputBuffer(data); + if (!reuse_input_buffer) { + // Last packet. Try setup once more. + reuse_input_buffer = TrySetupNextLoop(data, true); + } + if (!reuse_input_buffer) { + SwapInputBuffer(data); + } return; } } @@ -524,7 +554,13 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { packet += kBytesPerPacket; packet_idx++; if (packet_idx >= current_input_packet_count) { - SwapInputBuffer(data); + if (!reuse_input_buffer) { + // Last packet. Try setup once more. + reuse_input_buffer = TrySetupNextLoop(data, true); + } + if (!reuse_input_buffer) { + SwapInputBuffer(data); + } return; } } @@ -606,7 +642,13 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { packets_skip_--; packet_idx++; if (packet_idx >= current_input_packet_count) { - SwapInputBuffer(data); + if (!reuse_input_buffer) { + // Last packet. Try setup once more. + reuse_input_buffer = TrySetupNextLoop(data, true); + } + if (!reuse_input_buffer) { + SwapInputBuffer(data); + } return; } } @@ -618,7 +660,13 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) { // Next packet but we already skipped to it if (packet_idx >= current_input_packet_count) { // Buffer is fully used - SwapInputBuffer(data); + if (!reuse_input_buffer) { + // Last packet. Try setup once more. + reuse_input_buffer = TrySetupNextLoop(data, true); + } + if (!reuse_input_buffer) { + SwapInputBuffer(data); + } break; } offset = diff --git a/src/xenia/apu/xma_context.h b/src/xenia/apu/xma_context.h index d9e9b360a..52e97ee47 100644 --- a/src/xenia/apu/xma_context.h +++ b/src/xenia/apu/xma_context.h @@ -171,6 +171,8 @@ class XmaContext { private: static void SwapInputBuffer(XMA_CONTEXT_DATA* data); + static bool TrySetupNextLoop(XMA_CONTEXT_DATA* data, + bool ignore_input_buffer_offset); static void NextPacket(XMA_CONTEXT_DATA* data); static int GetSampleRate(int id); // Get the offset of the next frame. Does not traverse packets. From fe3f0f26e43ed7e01e0207119f11d24d16cd5540 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sat, 29 Jan 2022 13:22:03 +0300 Subject: [PATCH 70/88] [UI] Image post-processing and full presentation/window rework [GPU] Add FXAA post-processing [UI] Add FidelityFX FSR and CAS post-processing [UI] Add blue noise dithering from 10bpc to 8bpc [GPU] Apply the DC PWL gamma ramp closer to the spec, supporting fully white color [UI] Allow the GPU CP thread to present on the host directly, bypassing the UI thread OS paint event [UI] Allow variable refresh rate (or tearing) [UI] Present the newest frame (restart) on DXGI [UI] Replace GraphicsContext with a far more advanced Presenter with more coherent surface connection and UI overlay state management [UI] Connect presentation to windows via the Surface class, not native window handles [Vulkan] Switch to simpler Vulkan setup with no instance/device separation due to interdependencies and to pass fewer objects around [Vulkan] Lower the minimum required Vulkan version to 1.0 [UI/GPU] Various cleanup, mainly ComPtr usage [UI] Support per-monitor DPI awareness v2 on Windows [UI] DPI-scale Dear ImGui [UI] Replace the remaining non-detachable window delegates with unified window event and input listeners [UI] Allow listeners to safely destroy or close the window, and to register/unregister listeners without use-after-free and the ABA problem [UI] Explicit Z ordering of input listeners and UI overlays, top-down for input, bottom-up for drawing [UI] Add explicit window lifecycle phases [UI] Replace Window virtual functions with explicit desired state, its application, actual state, its feedback [UI] GTK: Apply the initial size to the drawing area [UI] Limit internal UI frame rate to that of the monitor [UI] Hide the cursor using a timer instead of polling due to no repeated UI thread paints with GPU CP thread presentation, and only within the window --- .gitmodules | 6 + src/xenia/app/emulator_window.cc | 740 +- src/xenia/app/emulator_window.h | 107 +- src/xenia/app/xenia_main.cc | 68 +- src/xenia/base/cvar.h | 38 +- src/xenia/base/filesystem.h | 6 +- src/xenia/base/filesystem_posix.cc | 4 +- src/xenia/base/filesystem_win.cc | 4 +- src/xenia/base/main_win.cc | 80 +- src/xenia/base/math.h | 7 +- src/xenia/base/profiling.cc | 274 +- src/xenia/base/profiling.h | 69 +- src/xenia/config.cc | 4 + src/xenia/config.h | 1 + src/xenia/debug/ui/debug_window.cc | 76 +- src/xenia/debug/ui/debug_window.h | 26 +- src/xenia/emulator.cc | 100 +- src/xenia/emulator.h | 59 +- src/xenia/gpu/command_processor.cc | 86 +- src/xenia/gpu/command_processor.h | 82 +- .../gpu/d3d12/d3d12_command_processor.cc | 1026 +- src/xenia/gpu/d3d12/d3d12_command_processor.h | 111 +- src/xenia/gpu/d3d12/d3d12_graphics_system.cc | 265 +- src/xenia/gpu/d3d12/d3d12_graphics_system.h | 31 +- .../gpu/d3d12/d3d12_primitive_processor.cc | 4 +- .../gpu/d3d12/d3d12_render_target_cache.cc | 32 +- src/xenia/gpu/d3d12/d3d12_shared_memory.cc | 29 +- src/xenia/gpu/d3d12/pipeline_cache.cc | 15 +- src/xenia/gpu/d3d12/texture_cache.cc | 41 +- src/xenia/gpu/draw_util.cc | 159 +- src/xenia/gpu/draw_util.h | 9 - src/xenia/gpu/graphics_system.cc | 107 +- src/xenia/gpu/graphics_system.h | 26 +- src/xenia/gpu/null/null_command_processor.cc | 6 +- src/xenia/gpu/null/null_command_processor.h | 4 +- src/xenia/gpu/null/null_graphics_system.cc | 21 +- src/xenia/gpu/null/null_graphics_system.h | 6 +- src/xenia/gpu/premake5.lua | 1 + src/xenia/gpu/shaders/apply_gamma_pwl.cs.hlsl | 2 + src/xenia/gpu/shaders/apply_gamma_pwl.hlsli | 41 + .../shaders/apply_gamma_pwl_fxaa_luma.cs.hlsl | 5 + .../gpu/shaders/apply_gamma_table.cs.hlsl | 2 + src/xenia/gpu/shaders/apply_gamma_table.hlsli | 23 + .../apply_gamma_table_fxaa_luma.cs.hlsl | 5 + src/xenia/gpu/shaders/bytecode/.clang-format | 1 + .../bytecode/d3d12_5_1/adaptive_quad_hs.h | 10 +- .../bytecode/d3d12_5_1/adaptive_triangle_hs.h | 10 +- .../bytecode/d3d12_5_1/apply_gamma_pwl_cs.h | 409 + .../d3d12_5_1/apply_gamma_pwl_fxaa_luma_cs.h | 413 + .../bytecode/d3d12_5_1/apply_gamma_table_cs.h | 281 + .../apply_gamma_table_fxaa_luma_cs.h | 284 + .../bytecode/d3d12_5_1/clear_uint2_ps.h | 10 +- .../bytecode/d3d12_5_1/continuous_quad_hs.h | 10 +- .../d3d12_5_1/continuous_triangle_hs.h | 10 +- .../bytecode/d3d12_5_1/discrete_quad_hs.h | 10 +- .../bytecode/d3d12_5_1/discrete_triangle_hs.h | 10 +- .../bytecode/d3d12_5_1/float24_round_ps.h | 10 +- .../bytecode/d3d12_5_1/float24_truncate_ps.h | 10 +- .../bytecode/d3d12_5_1/fullscreen_tc_vs.h | 166 - .../bytecode/d3d12_5_1/fullscreen_vs.h | 10 +- .../gpu/shaders/bytecode/d3d12_5_1/fxaa_cs.h | 1270 + .../bytecode/d3d12_5_1/fxaa_extreme_cs.h | 1956 ++ .../d3d12_5_1/host_depth_store_1xmsaa_cs.h | 10 +- .../d3d12_5_1/host_depth_store_2xmsaa_cs.h | 10 +- .../d3d12_5_1/host_depth_store_4xmsaa_cs.h | 10 +- .../d3d12_5_1/passthrough_position_xy_vs.h | 10 +- .../d3d12_5_1/primitive_point_list_gs.h | 10 +- .../d3d12_5_1/primitive_quad_list_gs.h | 10 +- .../d3d12_5_1/primitive_rectangle_list_gs.h | 10 +- .../d3d12_5_1/resolve_clear_32bpp_cs.h | 10 +- .../d3d12_5_1/resolve_clear_32bpp_scaled_cs.h | 10 +- .../d3d12_5_1/resolve_clear_64bpp_cs.h | 10 +- .../d3d12_5_1/resolve_clear_64bpp_scaled_cs.h | 10 +- .../resolve_fast_32bpp_1x2xmsaa_cs.h | 10 +- .../resolve_fast_32bpp_1x2xmsaa_scaled_cs.h | 10 +- .../d3d12_5_1/resolve_fast_32bpp_4xmsaa_cs.h | 10 +- .../resolve_fast_32bpp_4xmsaa_scaled_cs.h | 10 +- .../resolve_fast_64bpp_1x2xmsaa_cs.h | 10 +- .../resolve_fast_64bpp_1x2xmsaa_scaled_cs.h | 10 +- .../d3d12_5_1/resolve_fast_64bpp_4xmsaa_cs.h | 10 +- .../resolve_fast_64bpp_4xmsaa_scaled_cs.h | 10 +- .../d3d12_5_1/resolve_full_128bpp_cs.h | 10 +- .../d3d12_5_1/resolve_full_128bpp_scaled_cs.h | 10 +- .../d3d12_5_1/resolve_full_16bpp_cs.h | 10 +- .../d3d12_5_1/resolve_full_16bpp_scaled_cs.h | 10 +- .../d3d12_5_1/resolve_full_32bpp_cs.h | 10 +- .../d3d12_5_1/resolve_full_32bpp_scaled_cs.h | 10 +- .../d3d12_5_1/resolve_full_64bpp_cs.h | 10 +- .../d3d12_5_1/resolve_full_64bpp_scaled_cs.h | 10 +- .../bytecode/d3d12_5_1/resolve_full_8bpp_cs.h | 10 +- .../d3d12_5_1/resolve_full_8bpp_scaled_cs.h | 10 +- .../shaders/bytecode/d3d12_5_1/stretch_ps.h | 167 - .../d3d12_5_1/tessellation_adaptive_vs.h | 10 +- .../d3d12_5_1/tessellation_indexed_vs.h | 10 +- .../d3d12_5_1/texture_load_128bpb_cs.h | 10 +- .../d3d12_5_1/texture_load_128bpb_scaled_cs.h | 10 +- .../d3d12_5_1/texture_load_16bpb_cs.h | 10 +- .../d3d12_5_1/texture_load_16bpb_scaled_cs.h | 10 +- .../d3d12_5_1/texture_load_32bpb_cs.h | 10 +- .../d3d12_5_1/texture_load_32bpb_scaled_cs.h | 10 +- .../d3d12_5_1/texture_load_64bpb_cs.h | 10 +- .../d3d12_5_1/texture_load_64bpb_scaled_cs.h | 10 +- .../bytecode/d3d12_5_1/texture_load_8bpb_cs.h | 10 +- .../d3d12_5_1/texture_load_8bpb_scaled_cs.h | 10 +- .../bytecode/d3d12_5_1/texture_load_ctx1_cs.h | 10 +- .../d3d12_5_1/texture_load_depth_float_cs.h | 10 +- .../texture_load_depth_float_scaled_cs.h | 10 +- .../d3d12_5_1/texture_load_depth_unorm_cs.h | 10 +- .../texture_load_depth_unorm_scaled_cs.h | 10 +- .../d3d12_5_1/texture_load_dxn_rg8_cs.h | 10 +- .../d3d12_5_1/texture_load_dxt1_rgba8_cs.h | 10 +- .../d3d12_5_1/texture_load_dxt3_rgba8_cs.h | 10 +- .../d3d12_5_1/texture_load_dxt3a_cs.h | 10 +- .../d3d12_5_1/texture_load_dxt3aas1111_cs.h | 10 +- .../d3d12_5_1/texture_load_dxt5_rgba8_cs.h | 10 +- .../d3d12_5_1/texture_load_dxt5a_r8_cs.h | 10 +- .../texture_load_r10g11b11_rgba16_cs.h | 10 +- .../texture_load_r10g11b11_rgba16_scaled_cs.h | 10 +- .../texture_load_r10g11b11_rgba16_snorm_cs.h | 10 +- ...re_load_r10g11b11_rgba16_snorm_scaled_cs.h | 10 +- .../texture_load_r11g11b10_rgba16_cs.h | 10 +- .../texture_load_r11g11b10_rgba16_scaled_cs.h | 10 +- .../texture_load_r11g11b10_rgba16_snorm_cs.h | 10 +- ...re_load_r11g11b10_rgba16_snorm_scaled_cs.h | 10 +- .../texture_load_r4g4b4a4_b4g4r4a4_cs.h | 10 +- ...texture_load_r4g4b4a4_b4g4r4a4_scaled_cs.h | 10 +- .../texture_load_r5g5b5a1_b5g5r5a1_cs.h | 10 +- ...texture_load_r5g5b5a1_b5g5r5a1_scaled_cs.h | 10 +- ...xture_load_r5g5b6_b5g6r5_swizzle_rbga_cs.h | 10 +- ...oad_r5g5b6_b5g6r5_swizzle_rbga_scaled_cs.h | 10 +- .../d3d12_5_1/texture_load_r5g6b5_b5g6r5_cs.h | 10 +- .../texture_load_r5g6b5_b5g6r5_scaled_cs.h | 10 +- src/xenia/gpu/shaders/fullscreen_tc.vs.hlsl | 7 - src/xenia/gpu/shaders/fxaa.cs.hlsl | 5 + src/xenia/gpu/shaders/fxaa.hlsli | 48 + src/xenia/gpu/shaders/fxaa_extreme.cs.hlsl | 5 + src/xenia/gpu/shaders/stretch.ps.hlsl | 8 - src/xenia/gpu/shaders/stretch_gamma.ps.hlsl | 20 - src/xenia/gpu/trace_dump.cc | 19 +- src/xenia/gpu/trace_player.cc | 22 +- src/xenia/gpu/trace_player.h | 11 +- src/xenia/gpu/trace_viewer.cc | 97 +- src/xenia/gpu/trace_viewer.h | 55 +- src/xenia/gpu/vulkan/buffer_cache.cc | 106 +- src/xenia/gpu/vulkan/buffer_cache.h | 10 +- src/xenia/gpu/vulkan/pipeline_cache.cc | 129 +- src/xenia/gpu/vulkan/pipeline_cache.h | 8 +- src/xenia/gpu/vulkan/render_cache.cc | 172 +- src/xenia/gpu/vulkan/render_cache.h | 14 +- .../gpu/vulkan/shaders/bytecode/.clang-format | 2 + .../vulkan_spirv}/dummy_frag.h | 2 +- .../bytecode/vulkan_spirv/dummy_frag.spv | Bin 0 -> 548 bytes .../vulkan_spirv}/dummy_frag.txt | 2 +- .../vulkan_spirv}/line_quad_list_geom.h | 2 +- .../vulkan_spirv/line_quad_list_geom.spv | Bin 0 -> 2144 bytes .../vulkan_spirv}/line_quad_list_geom.txt | 2 +- .../vulkan_spirv}/point_list_geom.h | 2 +- .../bytecode/vulkan_spirv/point_list_geom.spv | Bin 0 -> 2888 bytes .../vulkan_spirv}/point_list_geom.txt | 2 +- .../vulkan_spirv}/quad_list_geom.h | 2 +- .../bytecode/vulkan_spirv/quad_list_geom.spv | Bin 0 -> 1996 bytes .../vulkan_spirv}/quad_list_geom.txt | 2 +- .../vulkan_spirv}/rect_list_geom.h | 64 +- .../bytecode/vulkan_spirv/rect_list_geom.spv | Bin 0 -> 4440 bytes .../vulkan_spirv}/rect_list_geom.txt | 54 +- src/xenia/gpu/vulkan/texture_cache.cc | 141 +- src/xenia/gpu/vulkan/texture_cache.h | 11 +- src/xenia/gpu/vulkan/texture_config.h | 2 +- .../gpu/vulkan/vulkan_command_processor.cc | 692 +- .../gpu/vulkan/vulkan_command_processor.h | 40 +- .../gpu/vulkan/vulkan_graphics_system.cc | 318 +- src/xenia/gpu/vulkan/vulkan_graphics_system.h | 23 +- src/xenia/gpu/vulkan/vulkan_shader.cc | 32 +- src/xenia/gpu/vulkan/vulkan_shader.h | 10 +- .../gpu/vulkan/vulkan_trace_dump_main.cc | 27 +- .../gpu/vulkan/vulkan_trace_viewer_main.cc | 6 +- src/xenia/hid/hid_demo.cc | 220 +- src/xenia/hid/input_driver.cc | 20 - src/xenia/hid/input_driver.h | 16 +- src/xenia/hid/nop/nop_hid.cc | 5 +- src/xenia/hid/nop/nop_hid.h | 3 +- src/xenia/hid/nop/nop_input_driver.cc | 3 +- src/xenia/hid/nop/nop_input_driver.h | 4 +- src/xenia/hid/premake5.lua | 1 - src/xenia/hid/sdl/sdl_hid.cc | 5 +- src/xenia/hid/sdl/sdl_hid.h | 3 +- src/xenia/hid/sdl/sdl_input_driver.cc | 4 +- src/xenia/hid/sdl/sdl_input_driver.h | 8 +- src/xenia/hid/winkey/winkey_hid.cc | 5 +- src/xenia/hid/winkey/winkey_hid.h | 3 +- src/xenia/hid/winkey/winkey_input_driver.cc | 66 +- src/xenia/hid/winkey/winkey_input_driver.h | 28 +- src/xenia/hid/xinput/xinput_hid.cc | 5 +- src/xenia/hid/xinput/xinput_hid.h | 3 +- src/xenia/hid/xinput/xinput_input_driver.cc | 5 +- src/xenia/hid/xinput/xinput_input_driver.h | 6 +- src/xenia/kernel/util/shim_utils.h | 2 +- src/xenia/kernel/xam/xam_nui.cc | 27 +- src/xenia/kernel/xam/xam_ui.cc | 30 +- src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc | 2 +- src/xenia/ui/d3d12/d3d12_api.h | 5 +- src/xenia/ui/d3d12/d3d12_context.cc | 379 - src/xenia/ui/d3d12/d3d12_context.h | 112 - .../ui/d3d12/d3d12_descriptor_heap_pool.cc | 25 +- .../ui/d3d12/d3d12_descriptor_heap_pool.h | 7 +- src/xenia/ui/d3d12/d3d12_immediate_drawer.cc | 384 +- src/xenia/ui/d3d12/d3d12_immediate_drawer.h | 95 +- src/xenia/ui/d3d12/d3d12_presenter.cc | 1466 + src/xenia/ui/d3d12/d3d12_presenter.h | 332 + src/xenia/ui/d3d12/d3d12_provider.cc | 45 +- src/xenia/ui/d3d12/d3d12_provider.h | 28 +- .../ui/d3d12/d3d12_submission_tracker.cc | 126 + src/xenia/ui/d3d12/d3d12_submission_tracker.h | 93 + .../ui/d3d12/d3d12_upload_buffer_pool.cc | 20 +- src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h | 5 +- src/xenia/ui/file_picker.h | 4 +- src/xenia/ui/file_picker_gtk.cc | 10 +- src/xenia/ui/file_picker_win.cc | 9 +- src/xenia/ui/graphics_context.cc | 48 - src/xenia/ui/graphics_context.h | 90 - src/xenia/ui/graphics_provider.h | 16 +- src/xenia/ui/graphics_upload_buffer_pool.cc | 21 +- src/xenia/ui/graphics_upload_buffer_pool.h | 3 +- src/xenia/ui/graphics_util.cc | 63 + src/xenia/ui/graphics_util.h | 30 + src/xenia/ui/imgui_dialog.cc | 36 +- src/xenia/ui/imgui_dialog.h | 21 +- src/xenia/ui/imgui_drawer.cc | 342 +- src/xenia/ui/imgui_drawer.h | 90 +- src/xenia/ui/immediate_drawer.cc | 108 + src/xenia/ui/immediate_drawer.h | 59 +- src/xenia/ui/menu_item.cc | 6 +- src/xenia/ui/menu_item.h | 16 +- src/xenia/ui/microprofile_drawer.cc | 36 +- src/xenia/ui/microprofile_drawer.h | 21 +- src/xenia/ui/premake5.lua | 5 + src/xenia/ui/presenter.cc | 1446 + src/xenia/ui/presenter.h | 1034 + src/xenia/ui/renderdoc_api.cc | 76 + src/xenia/ui/renderdoc_api.h | 39 + src/xenia/ui/shaders/bytecode/.clang-format | 1 + .../guest_output_bilinear_dither_ps.h | 1226 + .../d3d12_5_1/guest_output_bilinear_ps.h} | 285 +- .../guest_output_ffx_cas_resample_dither_ps.h | 2227 ++ .../guest_output_ffx_cas_resample_ps.h | 1264 + .../guest_output_ffx_cas_sharpen_dither_ps.h | 1454 + .../guest_output_ffx_cas_sharpen_ps.h | 491 + .../d3d12_5_1/guest_output_ffx_fsr_easu_ps.h | 2098 ++ .../guest_output_ffx_fsr_rcas_dither_ps.h | 1457 + .../d3d12_5_1/guest_output_ffx_fsr_rcas_ps.h | 498 + .../guest_output_triangle_strip_rect_vs.h | 220 + .../shaders/bytecode/d3d12_5_1/immediate_ps.h | 10 +- .../shaders/bytecode/d3d12_5_1/immediate_vs.h | 213 +- .../guest_output_bilinear_dither_frag.h | 592 + .../guest_output_bilinear_dither_frag.spv | Bin 0 -> 7056 bytes .../guest_output_bilinear_dither_frag.txt | 365 + .../vulkan_spirv/guest_output_bilinear_frag.h | 120 + .../guest_output_bilinear_frag.spv | Bin 0 -> 1392 bytes .../guest_output_bilinear_frag.txt | 81 + ...uest_output_ffx_cas_resample_dither_frag.h | 1216 + ...st_output_ffx_cas_resample_dither_frag.spv | Bin 0 -> 14540 bytes ...st_output_ffx_cas_resample_dither_frag.txt | 728 + .../guest_output_ffx_cas_resample_frag.h | 745 + .../guest_output_ffx_cas_resample_frag.spv | Bin 0 -> 8892 bytes .../guest_output_ffx_cas_resample_frag.txt | 445 + ...guest_output_ffx_cas_sharpen_dither_frag.h | 1212 + ...est_output_ffx_cas_sharpen_dither_frag.spv | Bin 0 -> 14492 bytes ...est_output_ffx_cas_sharpen_dither_frag.txt | 726 + .../guest_output_ffx_cas_sharpen_frag.h | 741 + .../guest_output_ffx_cas_sharpen_frag.spv | Bin 0 -> 8844 bytes .../guest_output_ffx_cas_sharpen_frag.txt | 443 + .../guest_output_ffx_fsr_easu_frag.h | 1332 + .../guest_output_ffx_fsr_easu_frag.spv | Bin 0 -> 15936 bytes .../guest_output_ffx_fsr_easu_frag.txt | 790 + .../guest_output_ffx_fsr_rcas_dither_frag.h | 817 + .../guest_output_ffx_fsr_rcas_dither_frag.spv | Bin 0 -> 9748 bytes .../guest_output_ffx_fsr_rcas_dither_frag.txt | 491 + .../guest_output_ffx_fsr_rcas_frag.h | 345 + .../guest_output_ffx_fsr_rcas_frag.spv | Bin 0 -> 4084 bytes .../guest_output_ffx_fsr_rcas_frag.txt | 207 + .../guest_output_triangle_strip_rect_vert.h | 109 + .../guest_output_triangle_strip_rect_vert.spv | Bin 0 -> 1252 bytes .../guest_output_triangle_strip_rect_vert.txt | 74 + .../bytecode/vulkan_spirv/immediate_frag.h | 71 + .../bytecode/vulkan_spirv/immediate_frag.spv | Bin 0 -> 804 bytes .../bytecode/vulkan_spirv/immediate_frag.txt | 54 + .../bytecode/vulkan_spirv/immediate_vert.h | 117 + .../bytecode/vulkan_spirv/immediate_vert.spv | Bin 0 -> 1352 bytes .../bytecode/vulkan_spirv/immediate_vert.txt | 82 + src/xenia/ui/shaders/dither_8bpc.xesli | 34 + .../ui/shaders/guest_output_bilinear.frag | 3 + .../ui/shaders/guest_output_bilinear.glsli | 38 + .../ui/shaders/guest_output_bilinear.hlsli | 31 + .../ui/shaders/guest_output_bilinear.ps.hlsl | 1 + .../shaders/guest_output_bilinear_dither.frag | 4 + .../guest_output_bilinear_dither.ps.hlsl | 2 + .../guest_output_ffx_cas_resample.frag | 3 + .../guest_output_ffx_cas_resample.glsli | 57 + .../guest_output_ffx_cas_resample.hlsli | 49 + .../guest_output_ffx_cas_resample.ps.hlsl | 1 + .../guest_output_ffx_cas_resample_dither.frag | 4 + ...est_output_ffx_cas_resample_dither.ps.hlsl | 2 + .../shaders/guest_output_ffx_cas_sharpen.frag | 3 + .../guest_output_ffx_cas_sharpen.glsli | 53 + .../guest_output_ffx_cas_sharpen.hlsli | 44 + .../guest_output_ffx_cas_sharpen.ps.hlsl | 1 + .../guest_output_ffx_cas_sharpen_dither.frag | 4 + ...uest_output_ffx_cas_sharpen_dither.ps.hlsl | 2 + .../ui/shaders/guest_output_ffx_fsr_easu.frag | 38 + .../shaders/guest_output_ffx_fsr_easu.ps.hlsl | 39 + .../ui/shaders/guest_output_ffx_fsr_rcas.frag | 3 + .../shaders/guest_output_ffx_fsr_rcas.glsli | 45 + .../shaders/guest_output_ffx_fsr_rcas.hlsli | 36 + .../shaders/guest_output_ffx_fsr_rcas.ps.hlsl | 1 + .../guest_output_ffx_fsr_rcas_dither.frag | 4 + .../guest_output_ffx_fsr_rcas_dither.ps.hlsl | 2 + .../guest_output_triangle_strip_rect.vert | 17 + .../guest_output_triangle_strip_rect.vs.hlsl | 14 + src/xenia/ui/shaders/immediate.frag | 14 + src/xenia/ui/shaders/immediate.vert | 20 + src/xenia/ui/shaders/immediate.vs.hlsl | 4 +- src/xenia/ui/shaders/noise.xesli | 47 + src/xenia/ui/shaders/xesl.xesli | 45 + src/xenia/ui/surface.h | 83 + src/xenia/ui/surface_android.cc | 34 + src/xenia/ui/surface_android.h | 37 + src/xenia/ui/surface_gnulinux.cc | 31 + src/xenia/ui/surface_gnulinux.h | 39 + src/xenia/ui/surface_win.cc | 35 + src/xenia/ui/surface_win.h | 45 + src/xenia/ui/ui_drawer.h | 38 + src/xenia/ui/ui_event.h | 65 +- src/xenia/ui/vulkan/blitter.cc | 122 +- src/xenia/ui/vulkan/blitter.h | 9 +- src/xenia/ui/vulkan/circular_buffer.cc | 86 +- src/xenia/ui/vulkan/circular_buffer.h | 7 +- src/xenia/ui/vulkan/fenced_pools.cc | 51 +- src/xenia/ui/vulkan/fenced_pools.h | 35 +- src/xenia/ui/vulkan/functions/device_1_0.inc | 15 +- .../functions/device_ext_debug_marker.inc | 5 - .../ui/vulkan/functions/instance_1_0.inc | 1 - .../functions/instance_ext_debug_report.inc | 3 - .../functions/instance_ext_debug_utils.inc | 4 + ...ce_khr_get_physical_device_properties2.inc | 4 + .../functions/instance_khr_win32_surface.inc | 1 + .../ui/vulkan/shaders/bin/blit_depth_frag.spv | Bin 660 -> 0 bytes .../ui/vulkan/shaders/bin/immediate_frag.h | 109 - .../ui/vulkan/shaders/bin/immediate_frag.spv | Bin 1252 -> 0 bytes .../ui/vulkan/shaders/bin/immediate_frag.txt | 83 - .../ui/vulkan/shaders/bin/immediate_vert.h | 128 - .../ui/vulkan/shaders/bin/immediate_vert.spv | Bin 1488 -> 0 bytes .../ui/vulkan/shaders/bin/immediate_vert.txt | 90 - .../ui/vulkan/shaders/bytecode/.clang-format | 2 + .../vulkan_spirv}/blit_color_frag.h | 2 +- .../vulkan_spirv}/blit_color_frag.spv | Bin 1000 -> 1000 bytes .../vulkan_spirv}/blit_color_frag.txt | 2 +- .../vulkan_spirv}/blit_depth_frag.h | 2 +- .../bytecode/vulkan_spirv/blit_depth_frag.spv | Bin 0 -> 660 bytes .../vulkan_spirv}/blit_depth_frag.txt | 2 +- .../vulkan_spirv}/blit_vert.h | 2 +- .../vulkan_spirv}/blit_vert.spv | Bin 1732 -> 1732 bytes .../vulkan_spirv}/blit_vert.txt | 2 +- src/xenia/ui/vulkan/shaders/immediate.frag | 28 - src/xenia/ui/vulkan/shaders/immediate.vert | 23 - src/xenia/ui/vulkan/vulkan.cc | 18 - src/xenia/ui/vulkan/vulkan.h | 43 - src/xenia/ui/vulkan/vulkan_context.cc | 206 - src/xenia/ui/vulkan/vulkan_context.h | 67 - src/xenia/ui/vulkan/vulkan_device.cc | 427 - src/xenia/ui/vulkan/vulkan_device.h | 145 - .../ui/vulkan/vulkan_immediate_drawer.cc | 1825 +- src/xenia/ui/vulkan/vulkan_immediate_drawer.h | 166 +- src/xenia/ui/vulkan/vulkan_instance.cc | 634 - src/xenia/ui/vulkan/vulkan_instance.h | 145 - src/xenia/ui/vulkan/vulkan_mem_alloc.h | 32 +- src/xenia/ui/vulkan/vulkan_presenter.cc | 2548 ++ src/xenia/ui/vulkan/vulkan_presenter.h | 518 + src/xenia/ui/vulkan/vulkan_provider.cc | 1153 +- src/xenia/ui/vulkan/vulkan_provider.h | 275 +- .../ui/vulkan/vulkan_submission_tracker.cc | 174 + .../ui/vulkan/vulkan_submission_tracker.h | 135 + src/xenia/ui/vulkan/vulkan_swap_chain.cc | 824 - src/xenia/ui/vulkan/vulkan_swap_chain.h | 106 - .../ui/vulkan/vulkan_upload_buffer_pool.cc | 199 + .../ui/vulkan/vulkan_upload_buffer_pool.h | 67 + src/xenia/ui/vulkan/vulkan_util.cc | 643 +- src/xenia/ui/vulkan/vulkan_util.h | 210 +- src/xenia/ui/vulkan/vulkan_window_demo.cc | 2 +- src/xenia/ui/window.cc | 894 +- src/xenia/ui/window.h | 804 +- src/xenia/ui/window_android.cc | 74 +- src/xenia/ui/window_android.h | 38 +- src/xenia/ui/window_demo.cc | 107 +- src/xenia/ui/window_demo.h | 37 +- src/xenia/ui/window_gtk.cc | 810 +- src/xenia/ui/window_gtk.h | 117 +- src/xenia/ui/window_listener.h | 49 +- src/xenia/ui/window_win.cc | 1650 +- src/xenia/ui/window_win.h | 170 +- src/xenia/ui/windowed_app_context_android.h | 3 +- src/xenia/ui/windowed_app_context_win.cc | 38 +- src/xenia/ui/windowed_app_context_win.h | 58 +- src/xenia/xbox.h | 3 +- third_party/.clang-format | 2 + third_party/FidelityFX-CAS | 1 + third_party/FidelityFX-FSR | 1 + third_party/fxaa/FXAA3_11.h | 2065 ++ third_party/vulkan/vk_mem_alloc.h | 22071 ++++++++++++---- third_party/vulkan/vk_platform.h | 18 +- third_party/vulkan/vulkan.h | 47 +- third_party/vulkan/vulkan_android.h | 45 +- third_party/vulkan/vulkan_beta.h | 833 + third_party/vulkan/vulkan_core.h | 8772 +++++- third_party/vulkan/vulkan_directfb.h | 54 + third_party/vulkan/vulkan_fuchsia.h | 258 + third_party/vulkan/vulkan_ggp.h | 58 + third_party/vulkan/vulkan_ios.h | 29 +- third_party/vulkan/vulkan_macos.h | 29 +- third_party/vulkan/vulkan_metal.h | 54 + third_party/vulkan/vulkan_mir.h | 65 - third_party/vulkan/vulkan_screen.h | 54 + third_party/vulkan/vulkan_vi.h | 27 +- third_party/vulkan/vulkan_wayland.h | 27 +- third_party/vulkan/vulkan_win32.h | 101 +- third_party/vulkan/vulkan_xcb.h | 27 +- third_party/vulkan/vulkan_xlib.h | 27 +- third_party/vulkan/vulkan_xlib_xrandr.h | 25 +- xenia-build | 17 +- 428 files changed, 75942 insertions(+), 18360 deletions(-) create mode 100644 src/xenia/gpu/shaders/apply_gamma_pwl.cs.hlsl create mode 100644 src/xenia/gpu/shaders/apply_gamma_pwl.hlsli create mode 100644 src/xenia/gpu/shaders/apply_gamma_pwl_fxaa_luma.cs.hlsl create mode 100644 src/xenia/gpu/shaders/apply_gamma_table.cs.hlsl create mode 100644 src/xenia/gpu/shaders/apply_gamma_table.hlsli create mode 100644 src/xenia/gpu/shaders/apply_gamma_table_fxaa_luma.cs.hlsl create mode 100644 src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_cs.h create mode 100644 src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_fxaa_luma_cs.h create mode 100644 src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_cs.h create mode 100644 src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_fxaa_luma_cs.h delete mode 100644 src/xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_tc_vs.h create mode 100644 src/xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_cs.h create mode 100644 src/xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_extreme_cs.h delete mode 100644 src/xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_ps.h delete mode 100644 src/xenia/gpu/shaders/fullscreen_tc.vs.hlsl create mode 100644 src/xenia/gpu/shaders/fxaa.cs.hlsl create mode 100644 src/xenia/gpu/shaders/fxaa.hlsli create mode 100644 src/xenia/gpu/shaders/fxaa_extreme.cs.hlsl delete mode 100644 src/xenia/gpu/shaders/stretch.ps.hlsl delete mode 100644 src/xenia/gpu/shaders/stretch_gamma.ps.hlsl create mode 100644 src/xenia/gpu/vulkan/shaders/bytecode/.clang-format rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/dummy_frag.h (98%) create mode 100644 src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.spv rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/dummy_frag.txt (96%) rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/line_quad_list_geom.h (99%) create mode 100644 src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/line_quad_list_geom.spv rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/line_quad_list_geom.txt (99%) rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/point_list_geom.h (99%) create mode 100644 src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/point_list_geom.spv rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/point_list_geom.txt (99%) rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/quad_list_geom.h (99%) create mode 100644 src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.spv rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/quad_list_geom.txt (99%) rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/rect_list_geom.h (92%) create mode 100644 src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.spv rename src/xenia/gpu/vulkan/shaders/{bin => bytecode/vulkan_spirv}/rect_list_geom.txt (92%) delete mode 100644 src/xenia/hid/input_driver.cc delete mode 100644 src/xenia/ui/d3d12/d3d12_context.cc delete mode 100644 src/xenia/ui/d3d12/d3d12_context.h create mode 100644 src/xenia/ui/d3d12/d3d12_presenter.cc create mode 100644 src/xenia/ui/d3d12/d3d12_presenter.h create mode 100644 src/xenia/ui/d3d12/d3d12_submission_tracker.cc create mode 100644 src/xenia/ui/d3d12/d3d12_submission_tracker.h delete mode 100644 src/xenia/ui/graphics_context.cc delete mode 100644 src/xenia/ui/graphics_context.h create mode 100644 src/xenia/ui/graphics_util.cc create mode 100644 src/xenia/ui/graphics_util.h create mode 100644 src/xenia/ui/immediate_drawer.cc create mode 100644 src/xenia/ui/presenter.cc create mode 100644 src/xenia/ui/presenter.h create mode 100644 src/xenia/ui/renderdoc_api.cc create mode 100644 src/xenia/ui/renderdoc_api.h create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_dither_ps.h rename src/xenia/{gpu/shaders/bytecode/d3d12_5_1/stretch_gamma_ps.h => ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_ps.h} (53%) create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_dither_ps.h create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_ps.h create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_dither_ps.h create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_ps.h create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_easu_ps.h create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_dither_ps.h create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_ps.h create mode 100644 src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_triangle_strip_rect_vs.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_dither_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_dither_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_dither_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.txt create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.h create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.spv create mode 100644 src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.txt create mode 100644 src/xenia/ui/shaders/dither_8bpc.xesli create mode 100644 src/xenia/ui/shaders/guest_output_bilinear.frag create mode 100644 src/xenia/ui/shaders/guest_output_bilinear.glsli create mode 100644 src/xenia/ui/shaders/guest_output_bilinear.hlsli create mode 100644 src/xenia/ui/shaders/guest_output_bilinear.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_bilinear_dither.frag create mode 100644 src/xenia/ui/shaders/guest_output_bilinear_dither.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_resample.frag create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_resample.glsli create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_resample.hlsli create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_resample.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_resample_dither.frag create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_resample_dither.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.frag create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.glsli create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.hlsli create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_sharpen_dither.frag create mode 100644 src/xenia/ui/shaders/guest_output_ffx_cas_sharpen_dither.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_ffx_fsr_easu.frag create mode 100644 src/xenia/ui/shaders/guest_output_ffx_fsr_easu.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.frag create mode 100644 src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.glsli create mode 100644 src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.hlsli create mode 100644 src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_ffx_fsr_rcas_dither.frag create mode 100644 src/xenia/ui/shaders/guest_output_ffx_fsr_rcas_dither.ps.hlsl create mode 100644 src/xenia/ui/shaders/guest_output_triangle_strip_rect.vert create mode 100644 src/xenia/ui/shaders/guest_output_triangle_strip_rect.vs.hlsl create mode 100644 src/xenia/ui/shaders/immediate.frag create mode 100644 src/xenia/ui/shaders/immediate.vert create mode 100644 src/xenia/ui/shaders/noise.xesli create mode 100644 src/xenia/ui/shaders/xesl.xesli create mode 100644 src/xenia/ui/surface.h create mode 100644 src/xenia/ui/surface_android.cc create mode 100644 src/xenia/ui/surface_android.h create mode 100644 src/xenia/ui/surface_gnulinux.cc create mode 100644 src/xenia/ui/surface_gnulinux.h create mode 100644 src/xenia/ui/surface_win.cc create mode 100644 src/xenia/ui/surface_win.h create mode 100644 src/xenia/ui/ui_drawer.h delete mode 100644 src/xenia/ui/vulkan/functions/device_ext_debug_marker.inc delete mode 100644 src/xenia/ui/vulkan/functions/instance_ext_debug_report.inc create mode 100644 src/xenia/ui/vulkan/functions/instance_ext_debug_utils.inc create mode 100644 src/xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc delete mode 100644 src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.spv delete mode 100644 src/xenia/ui/vulkan/shaders/bin/immediate_frag.h delete mode 100644 src/xenia/ui/vulkan/shaders/bin/immediate_frag.spv delete mode 100644 src/xenia/ui/vulkan/shaders/bin/immediate_frag.txt delete mode 100644 src/xenia/ui/vulkan/shaders/bin/immediate_vert.h delete mode 100644 src/xenia/ui/vulkan/shaders/bin/immediate_vert.spv delete mode 100644 src/xenia/ui/vulkan/shaders/bin/immediate_vert.txt create mode 100644 src/xenia/ui/vulkan/shaders/bytecode/.clang-format rename src/xenia/ui/vulkan/shaders/{bin => bytecode/vulkan_spirv}/blit_color_frag.h (99%) rename src/xenia/ui/vulkan/shaders/{bin => bytecode/vulkan_spirv}/blit_color_frag.spv (53%) rename src/xenia/ui/vulkan/shaders/{bin => bytecode/vulkan_spirv}/blit_color_frag.txt (98%) rename src/xenia/ui/vulkan/shaders/{bin => bytecode/vulkan_spirv}/blit_depth_frag.h (98%) create mode 100644 src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.spv rename src/xenia/ui/vulkan/shaders/{bin => bytecode/vulkan_spirv}/blit_depth_frag.txt (97%) rename src/xenia/ui/vulkan/shaders/{bin => bytecode/vulkan_spirv}/blit_vert.h (99%) rename src/xenia/ui/vulkan/shaders/{bin => bytecode/vulkan_spirv}/blit_vert.spv (61%) rename src/xenia/ui/vulkan/shaders/{bin => bytecode/vulkan_spirv}/blit_vert.txt (98%) delete mode 100644 src/xenia/ui/vulkan/shaders/immediate.frag delete mode 100644 src/xenia/ui/vulkan/shaders/immediate.vert delete mode 100644 src/xenia/ui/vulkan/vulkan.cc delete mode 100644 src/xenia/ui/vulkan/vulkan.h delete mode 100644 src/xenia/ui/vulkan/vulkan_context.cc delete mode 100644 src/xenia/ui/vulkan/vulkan_context.h delete mode 100644 src/xenia/ui/vulkan/vulkan_device.cc delete mode 100644 src/xenia/ui/vulkan/vulkan_device.h delete mode 100644 src/xenia/ui/vulkan/vulkan_instance.cc delete mode 100644 src/xenia/ui/vulkan/vulkan_instance.h create mode 100644 src/xenia/ui/vulkan/vulkan_presenter.cc create mode 100644 src/xenia/ui/vulkan/vulkan_presenter.h create mode 100644 src/xenia/ui/vulkan/vulkan_submission_tracker.cc create mode 100644 src/xenia/ui/vulkan/vulkan_submission_tracker.h delete mode 100644 src/xenia/ui/vulkan/vulkan_swap_chain.cc delete mode 100644 src/xenia/ui/vulkan/vulkan_swap_chain.h create mode 100644 src/xenia/ui/vulkan/vulkan_upload_buffer_pool.cc create mode 100644 src/xenia/ui/vulkan/vulkan_upload_buffer_pool.h create mode 100644 third_party/.clang-format create mode 160000 third_party/FidelityFX-CAS create mode 160000 third_party/FidelityFX-FSR create mode 100644 third_party/fxaa/FXAA3_11.h create mode 100644 third_party/vulkan/vulkan_beta.h create mode 100644 third_party/vulkan/vulkan_directfb.h create mode 100644 third_party/vulkan/vulkan_fuchsia.h create mode 100644 third_party/vulkan/vulkan_ggp.h create mode 100644 third_party/vulkan/vulkan_metal.h delete mode 100644 third_party/vulkan/vulkan_mir.h create mode 100644 third_party/vulkan/vulkan_screen.h diff --git a/.gitmodules b/.gitmodules index 5809e562b..5f5ee82b0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -76,3 +76,9 @@ [submodule "third_party/premake-androidndk"] path = third_party/premake-androidndk url = https://github.com/Triang3l/premake-androidndk.git +[submodule "third_party/FidelityFX-CAS"] + path = third_party/FidelityFX-CAS + url = https://github.com/GPUOpen-Effects/FidelityFX-CAS.git +[submodule "third_party/FidelityFX-FSR"] + path = third_party/FidelityFX-FSR + url = https://github.com/GPUOpen-Effects/FidelityFX-FSR.git diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index a91574604..097d59dfb 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -2,13 +2,20 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/app/emulator_window.h" +#include +#include +#include +#include +#include +#include + #include "third_party/fmt/include/fmt/format.h" #include "third_party/imgui/imgui.h" #include "xenia/base/assert.h" @@ -20,11 +27,17 @@ #include "xenia/base/profiling.h" #include "xenia/base/system.h" #include "xenia/base/threading.h" +#include "xenia/cpu/processor.h" #include "xenia/emulator.h" +#include "xenia/gpu/command_processor.h" #include "xenia/gpu/graphics_system.h" #include "xenia/ui/file_picker.h" +#include "xenia/ui/graphics_provider.h" #include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/presenter.h" +#include "xenia/ui/ui_event.h" #include "xenia/ui/virtual_key.h" // Autogenerated by `xb premake`. @@ -32,13 +45,91 @@ DECLARE_bool(debug); +DEFINE_bool(fullscreen, false, "Whether to launch the emulator in fullscreen.", + "Display"); + +DEFINE_string( + postprocess_antialiasing, "", + "Post-processing anti-aliasing effect to apply to the image output of the " + "game.\n" + "Using post-process anti-aliasing is heavily recommended when AMD " + "FidelityFX Contrast Adaptive Sharpening or Super Resolution 1.0 is " + "active.\n" + "Use: [none, fxaa, fxaa_extreme]\n" + " none (or any value not listed here):\n" + " Don't alter the original image.\n" + " fxaa:\n" + " NVIDIA Fast Approximate Anti-Aliasing 3.11, normal quality preset (12)." + "\n" + " fxaa_extreme:\n" + " NVIDIA Fast Approximate Anti-Aliasing 3.11, extreme quality preset " + "(39).", + "Display"); +DEFINE_string( + postprocess_scaling_and_sharpening, "", + "Post-processing effect to use for resampling and/or sharpening of the " + "final display output.\n" + "Use: [bilinear, cas, fsr]\n" + " bilinear (or any value not listed here):\n" + " Original image at 1:1, simple bilinear stretching for resampling.\n" + " cas:\n" + " Use AMD FidelityFX Contrast Adaptive Sharpening (CAS) for sharpening " + "at scaling factors of up to 2x2, with additional bilinear stretching for " + "larger factors.\n" + " fsr:\n" + " Use AMD FidelityFX Super Resolution 1.0 (FSR) for highest-quality " + "upscaling, or AMD FidelityFX Contrast Adaptive Sharpening for sharpening " + "while not scaling or downsampling.\n" + " For scaling by factors of more than 2x2, multiple FSR passes are done.", + "Display"); +DEFINE_double( + postprocess_ffx_cas_additional_sharpness, + xe::ui::Presenter::GuestOutputPaintConfig::kCasAdditionalSharpnessDefault, + "Additional sharpness for AMD FidelityFX Contrast Adaptive Sharpening " + "(CAS), from 0 to 1.\n" + "Higher is sharper.", + "Display"); +DEFINE_uint32( + postprocess_ffx_fsr_max_upsampling_passes, + xe::ui::Presenter::GuestOutputPaintConfig::kFsrMaxUpscalingPassesMax, + "Maximum number of upsampling passes performed in AMD FidelityFX Super " + "Resolution 1.0 (FSR) before falling back to bilinear stretching after the " + "final pass.\n" + "Each pass upscales only to up to 2x2 the previous size. If the game " + "outputs a 1280x720 image, 1 pass will upscale it to up to 2560x1440 " + "(below 4K), after 2 passes it will be upscaled to a maximum of 5120x2880 " + "(including 3840x2160 for 4K), and so on.\n" + "This variable has no effect if the display resolution isn't very high, " + "but may be reduced on resolutions like 4K or 8K in case the performance " + "impact of multiple FSR upsampling passes is too high, or if softer edges " + "are desired.\n" + "The default value is the maximum internally supported by Xenia.", + "Display"); +DEFINE_double( + postprocess_ffx_fsr_sharpness_reduction, + xe::ui::Presenter::GuestOutputPaintConfig::kFsrSharpnessReductionDefault, + "Sharpness reduction for AMD FidelityFX Super Resolution 1.0 (FSR), in " + "stops.\n" + "Lower is sharper.", + "Display"); +// Dithering to 8bpc is enabled by default since the effect is minor, only +// effects what can't be shown normally by host displays, and nothing is changed +// by it for 8bpc source without resampling. +DEFINE_bool( + postprocess_dither, true, + "Dither the final image output from the internal precision to 8 bits per " + "channel so gradients are smoother.\n" + "On a 10bpc display, the lower 2 bits will still be kept, but noise will " + "be added to them - disabling may be recommended for 10bpc, but it " + "depends on the 10bpc displaying capabilities of the actual display used.", + "Display"); + namespace xe { namespace app { using xe::ui::FileDropEvent; using xe::ui::KeyEvent; using xe::ui::MenuItem; -using xe::ui::MouseEvent; using xe::ui::UIEvent; const std::string kBaseTitle = "Xenia"; @@ -47,7 +138,12 @@ EmulatorWindow::EmulatorWindow(Emulator* emulator, ui::WindowedAppContext& app_context) : emulator_(emulator), app_context_(app_context), - window_(ui::Window::Create(app_context, kBaseTitle)) { + window_listener_(*this), + window_(ui::Window::Create(app_context, kBaseTitle, 1280, 720)), + imgui_drawer_( + std::make_unique(window_.get(), kZOrderImGui)), + display_config_game_config_load_callback_( + new DisplayConfigGameConfigLoadCallback(*emulator, *this)) { base_title_ = kBaseTitle + #ifdef DEBUG #if _NO_DEBUG_HEAP == 1 @@ -76,109 +172,327 @@ std::unique_ptr EmulatorWindow::Create( return emulator_window; } -bool EmulatorWindow::Initialize() { - if (!window_->Initialize()) { - XELOGE("Failed to initialize platform window"); - return false; +EmulatorWindow::~EmulatorWindow() { + // Notify the ImGui drawer that the immediate drawer is being destroyed. + ShutdownGraphicsSystemPresenterPainting(); +} + +ui::Presenter* EmulatorWindow::GetGraphicsSystemPresenter() const { + gpu::GraphicsSystem* graphics_system = emulator_->graphics_system(); + return graphics_system ? graphics_system->presenter() : nullptr; +} + +void EmulatorWindow::SetupGraphicsSystemPresenterPainting() { + ShutdownGraphicsSystemPresenterPainting(); + + if (!window_) { + return; } - UpdateTitle(); + ui::Presenter* presenter = GetGraphicsSystemPresenter(); + if (!presenter) { + return; + } - window_->on_closed.AddListener( - [this](UIEvent* e) { app_context_.QuitFromUIThread(); }); + ApplyDisplayConfigForCvars(); - window_->on_file_drop.AddListener( - [this](FileDropEvent* e) { FileDrop(e->filename()); }); + window_->SetPresenter(presenter); - window_->on_key_down.AddListener([this](KeyEvent* e) { - bool handled = true; - switch (e->virtual_key()) { - case ui::VirtualKey::kO: { - if (e->is_ctrl_pressed()) { - FileOpen(); - } - } break; - case ui::VirtualKey::kMultiply: { - CpuTimeScalarReset(); - } break; - case ui::VirtualKey::kSubtract: { - CpuTimeScalarSetHalf(); - } break; - case ui::VirtualKey::kAdd: { - CpuTimeScalarSetDouble(); - } break; + immediate_drawer_ = + emulator_->graphics_system()->provider()->CreateImmediateDrawer(); + if (immediate_drawer_) { + immediate_drawer_->SetPresenter(presenter); + imgui_drawer_->SetPresenterAndImmediateDrawer(presenter, + immediate_drawer_.get()); + Profiler::SetUserIO(kZOrderProfiler, window_.get(), presenter, + immediate_drawer_.get()); + } +} - case ui::VirtualKey::kF3: { - Profiler::ToggleDisplay(); - } break; +void EmulatorWindow::ShutdownGraphicsSystemPresenterPainting() { + Profiler::SetUserIO(kZOrderProfiler, window_.get(), nullptr, nullptr); + imgui_drawer_->SetPresenterAndImmediateDrawer(nullptr, nullptr); + immediate_drawer_.reset(); + if (window_) { + window_->SetPresenter(nullptr); + } +} - case ui::VirtualKey::kF4: { - GpuTraceFrame(); - } break; - case ui::VirtualKey::kF5: { - GpuClearCaches(); - } break; -#ifdef DEBUG - case ui::VirtualKey::kF7: { - // Save to file - // TODO: Choose path based on user input, or from options - // TODO: Spawn a new thread to do this. - emulator()->SaveToFile("test.sav"); - } break; - case ui::VirtualKey::kF8: { - // Restore from file - // TODO: Choose path from user - // TODO: Spawn a new thread to do this. - emulator()->RestoreFromFile("test.sav"); - } break; -#endif // #ifdef DEBUG - case ui::VirtualKey::kF11: { - ToggleFullscreen(); - } break; - case ui::VirtualKey::kEscape: { - // Allow users to escape fullscreen (but not enter it). - if (window_->is_fullscreen()) { - window_->ToggleFullscreen(false); - } else { - handled = false; - } - } break; +void EmulatorWindow::OnEmulatorInitialized() { + emulator_initialized_ = true; + window_->SetMainMenuEnabled(true); + // When the user can see that the emulator isn't initializing anymore (the + // menu isn't disabled), enter fullscreen if requested. + if (cvars::fullscreen) { + window_->SetFullscreen(true); + } +} - case ui::VirtualKey::kPause: { - CpuBreakIntoDebugger(); - } break; - case ui::VirtualKey::kCancel: { - CpuBreakIntoHostDebugger(); - } break; +void EmulatorWindow::EmulatorWindowListener::OnClosing(ui::UIEvent& e) { + emulator_window_.app_context_.QuitFromUIThread(); +} - case ui::VirtualKey::kF1: { - ShowHelpWebsite(); - } break; +void EmulatorWindow::EmulatorWindowListener::OnFileDrop(ui::FileDropEvent& e) { + emulator_window_.FileDrop(e.filename()); +} - case ui::VirtualKey::kF2: { - ShowCommitID(); - } break; +void EmulatorWindow::EmulatorWindowListener::OnKeyDown(ui::KeyEvent& e) { + emulator_window_.OnKeyDown(e); +} - default: { - handled = false; - } break; - } - e->set_handled(handled); - }); +void EmulatorWindow::DisplayConfigGameConfigLoadCallback::PostGameConfigLoad() { + emulator_window_.ApplyDisplayConfigForCvars(); +} - window_->on_mouse_move.AddListener([this](MouseEvent* e) { - if (window_->is_fullscreen() && (e->dx() > 2 || e->dy() > 2)) { - if (!window_->is_cursor_visible()) { - window_->set_cursor_visible(true); +void EmulatorWindow::DisplayConfigDialog::OnDraw(ImGuiIO& io) { + gpu::GraphicsSystem* graphics_system = + emulator_window_.emulator_->graphics_system(); + if (!graphics_system) { + return; + } + + // In the top-left corner so it's close to the menu bar from where it was + // opened. + // Origin Y coordinate 20 was taken from the Dear ImGui demo. + ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(20, 20), ImGuiCond_FirstUseEver); + // Alpha from Dear ImGui tooltips (0.35 from the overlay provides too low + // visibility). Translucent so some effect of the changes can still be seen + // through it. + ImGui::SetNextWindowBgAlpha(0.6f); + bool dialog_open = true; + if (!ImGui::Begin("Post-processing", &dialog_open, + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_HorizontalScrollbar)) { + ImGui::End(); + return; + } + // Even if the close button has been pressed, still paint everything not to + // have one frame with an empty window. + + gpu::CommandProcessor* command_processor = + graphics_system->command_processor(); + if (command_processor) { + if (ImGui::TreeNodeEx( + "Anti-aliasing", + ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) { + gpu::CommandProcessor::SwapPostEffect current_swap_post_effect = + command_processor->GetDesiredSwapPostEffect(); + int new_swap_post_effect_index = int(current_swap_post_effect); + ImGui::RadioButton("None", &new_swap_post_effect_index, + int(gpu::CommandProcessor::SwapPostEffect::kNone)); + ImGui::RadioButton( + "NVIDIA Fast Approximate Anti-Aliasing 3.11 (FXAA), normal quality", + &new_swap_post_effect_index, + int(gpu::CommandProcessor::SwapPostEffect::kFxaa)); + ImGui::RadioButton( + "NVIDIA Fast Approximate Anti-Aliasing 3.11 (FXAA), extreme quality", + &new_swap_post_effect_index, + int(gpu::CommandProcessor::SwapPostEffect::kFxaaExtreme)); + gpu::CommandProcessor::SwapPostEffect new_swap_post_effect = + gpu::CommandProcessor::SwapPostEffect(new_swap_post_effect_index); + if (current_swap_post_effect != new_swap_post_effect) { + command_processor->SetDesiredSwapPostEffect(new_swap_post_effect); } - cursor_hide_time_ = Clock::QueryHostSystemTime() + 30000000; + // Override the values in the cvars to save them to the config at exit if + // the user has set them to anything new. + if (GetSwapPostEffectForCvarValue(cvars::postprocess_antialiasing) != + new_swap_post_effect) { + OVERRIDE_string(postprocess_antialiasing, + GetCvarValueForSwapPostEffect(new_swap_post_effect)); + } + + ImGui::TreePop(); + } + } + + ui::Presenter* presenter = graphics_system->presenter(); + if (presenter) { + const ui::Presenter::GuestOutputPaintConfig& current_presenter_config = + presenter->GetGuestOutputPaintConfigFromUIThread(); + ui::Presenter::GuestOutputPaintConfig new_presenter_config = + current_presenter_config; + + if (ImGui::TreeNodeEx( + "Resampling and sharpening", + ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) { + // Filtering effect. + int new_effect_index = int(new_presenter_config.GetEffect()); + ImGui::RadioButton( + "None / bilinear", &new_effect_index, + int(ui::Presenter::GuestOutputPaintConfig::Effect::kBilinear)); + ImGui::RadioButton( + "AMD FidelityFX Contrast Adaptive Sharpening (CAS)", + &new_effect_index, + int(ui::Presenter::GuestOutputPaintConfig::Effect::kCas)); + ImGui::RadioButton( + "AMD FidelityFX Super Resolution 1.0 (FSR)", &new_effect_index, + int(ui::Presenter::GuestOutputPaintConfig::Effect::kFsr)); + new_presenter_config.SetEffect( + ui::Presenter::GuestOutputPaintConfig::Effect(new_effect_index)); + + // effect_description must be one complete, but short enough, sentence per + // line, as TextWrapped doesn't work correctly in auto-resizing windows + // (in the initial frames, the window becomes extremely tall, and widgets + // added after the wrapped text have no effect on the width of the text). + const char* effect_description = nullptr; + switch (new_presenter_config.GetEffect()) { + case ui::Presenter::GuestOutputPaintConfig::Effect::kBilinear: + effect_description = + "Simple bilinear filtering is done if resampling is needed.\n" + "Otherwise, only anti-aliasing is done if enabled, or displaying " + "as is."; + break; + case ui::Presenter::GuestOutputPaintConfig::Effect::kCas: + effect_description = + "Sharpening and resampling to up to 2x2 to improve the fidelity " + "of details.\n" + "For scaling by more than 2x2, bilinear stretching is done " + "afterwards."; + break; + case ui::Presenter::GuestOutputPaintConfig::Effect::kFsr: + effect_description = + "High-quality edge-preserving upscaling to arbitrary target " + "resolutions.\n" + "For scaling by more than 2x2, multiple upsampling passes are " + "done.\n" + "If not upscaling, Contrast Adaptive Sharpening (CAS) is used " + "instead."; + break; + } + if (effect_description) { + ImGui::TextUnformatted(effect_description); + } + + if (new_presenter_config.GetEffect() == + ui::Presenter::GuestOutputPaintConfig::Effect::kCas || + new_presenter_config.GetEffect() == + ui::Presenter::GuestOutputPaintConfig::Effect::kFsr) { + if (effect_description) { + ImGui::Spacing(); + } + + ImGui::TextUnformatted( + "FXAA is highly recommended when using CAS or FSR."); + + ImGui::Spacing(); + + // 2 decimal places is more or less enough precision for the sharpness + // given the minor visual effect of small changes, the width of the + // slider, and readability convenience (2 decimal places is like an + // integer percentage). However, because Dear ImGui parses the string + // representation of the number and snaps the value to it internally, + // 2 decimal places actually offer less precision than the slider itself + // does. This is especially prominent in the low range of the non-linear + // FSR sharpness reduction slider. 3 decimal places are optimal in this + // case. + + if (new_presenter_config.GetEffect() == + ui::Presenter::GuestOutputPaintConfig::Effect::kFsr) { + float fsr_sharpness_reduction = + new_presenter_config.GetFsrSharpnessReduction(); + ImGui::TextUnformatted( + "FSR sharpness reduction when upscaling (lower is sharper):"); + // Power 2.0 as the reduction is in stops, used in exp2. + ImGui::SliderFloat( + "##FSRSharpnessReduction", &fsr_sharpness_reduction, + ui::Presenter::GuestOutputPaintConfig::kFsrSharpnessReductionMin, + ui::Presenter::GuestOutputPaintConfig::kFsrSharpnessReductionMax, + "%.3f stops", 2.0f); + ImGui::SameLine(); + if (ImGui::Button("Reset##ResetFSRSharpnessReduction")) { + fsr_sharpness_reduction = ui::Presenter::GuestOutputPaintConfig :: + kFsrSharpnessReductionDefault; + } + new_presenter_config.SetFsrSharpnessReduction( + fsr_sharpness_reduction); + } + + float cas_additional_sharpness = + new_presenter_config.GetCasAdditionalSharpness(); + ImGui::TextUnformatted( + new_presenter_config.GetEffect() == + ui::Presenter::GuestOutputPaintConfig::Effect::kFsr + ? "CAS additional sharpness when not upscaling (higher is " + "sharper):" + : "CAS additional sharpness (higher is sharper):"); + ImGui::SliderFloat( + "##CASAdditionalSharpness", &cas_additional_sharpness, + ui::Presenter::GuestOutputPaintConfig::kCasAdditionalSharpnessMin, + ui::Presenter::GuestOutputPaintConfig::kCasAdditionalSharpnessMax, + "%.3f"); + ImGui::SameLine(); + if (ImGui::Button("Reset##ResetCASAdditionalSharpness")) { + cas_additional_sharpness = ui::Presenter::GuestOutputPaintConfig :: + kCasAdditionalSharpnessDefault; + } + new_presenter_config.SetCasAdditionalSharpness( + cas_additional_sharpness); + + // There's no need to expose the setting for the maximum number of FSR + // EASU passes as it's largely meaningless if the user doesn't have a + // very high-resolution monitor compared to the original image size as + // most of the values of the slider will have no effect, and that's just + // very fine-grained performance control for a fixed-overhead pass only + // for huge screen resolutions. + } + + ImGui::TreePop(); } - e->set_handled(false); - }); + if (ImGui::TreeNodeEx("Dithering", ImGuiTreeNodeFlags_Framed | + ImGuiTreeNodeFlags_DefaultOpen)) { + bool dither = current_presenter_config.GetDither(); + ImGui::Checkbox( + "Dither the final output to 8bpc to make gradients smoother", + &dither); + new_presenter_config.SetDither(dither); - window_->on_paint.AddListener([this](UIEvent* e) { CheckHideCursor(); }); + ImGui::TreePop(); + } + + presenter->SetGuestOutputPaintConfigFromUIThread(new_presenter_config); + + // Override the values in the cvars to save them to the config at exit if + // the user has set them to anything new. + ui::Presenter::GuestOutputPaintConfig cvars_presenter_config = + GetGuestOutputPaintConfigForCvars(); + if (cvars_presenter_config.GetEffect() != + new_presenter_config.GetEffect()) { + OVERRIDE_string(postprocess_scaling_and_sharpening, + GetCvarValueForGuestOutputPaintEffect( + new_presenter_config.GetEffect())); + } + if (cvars_presenter_config.GetCasAdditionalSharpness() != + new_presenter_config.GetCasAdditionalSharpness()) { + OVERRIDE_double(postprocess_ffx_cas_additional_sharpness, + new_presenter_config.GetCasAdditionalSharpness()); + } + if (cvars_presenter_config.GetFsrSharpnessReduction() != + new_presenter_config.GetFsrSharpnessReduction()) { + OVERRIDE_double(postprocess_ffx_fsr_sharpness_reduction, + new_presenter_config.GetFsrSharpnessReduction()); + } + if (cvars_presenter_config.GetDither() != + new_presenter_config.GetDither()) { + OVERRIDE_bool(postprocess_dither, new_presenter_config.GetDither()); + } + } + + ImGui::End(); + + if (!dialog_open) { + emulator_window_.ToggleDisplayConfigDialog(); + // `this` might have been destroyed by ToggleDisplayConfigDialog. + return; + } +} + +bool EmulatorWindow::Initialize() { + window_->AddListener(&window_listener_); + window_->AddInputListener(&window_listener_, kZOrderEmulatorWindowInput); // Main menu. // FIXME: This code is really messy. @@ -198,9 +512,9 @@ bool EmulatorWindow::Initialize() { MenuItem::Type::kString, "Show content directory...", std::bind(&EmulatorWindow::ShowContentDirectory, this))); file_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); - file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, "E&xit", - "Alt+F4", - [this]() { window_->Close(); })); + file_menu->AddChild( + MenuItem::Create(MenuItem::Type::kString, "E&xit", "Alt+F4", + [this]() { window_->RequestClose(); })); } main_menu->AddChild(std::move(file_menu)); @@ -253,14 +567,20 @@ bool EmulatorWindow::Initialize() { } main_menu->AddChild(std::move(gpu_menu)); - // Window menu. - auto window_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Window"); + // Display menu. + auto display_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Display"); { - window_menu->AddChild( + display_menu->AddChild(MenuItem::Create( + MenuItem::Type::kString, "&Post-processing settings", "F6", + std::bind(&EmulatorWindow::ToggleDisplayConfigDialog, this))); + } + display_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); + { + display_menu->AddChild( MenuItem::Create(MenuItem::Type::kString, "&Fullscreen", "F11", std::bind(&EmulatorWindow::ToggleFullscreen, this))); } - main_menu->AddChild(std::move(window_menu)); + main_menu->AddChild(std::move(display_menu)); // Help menu. auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help"); @@ -284,16 +604,196 @@ bool EmulatorWindow::Initialize() { } main_menu->AddChild(std::move(help_menu)); - window_->set_main_menu(std::move(main_menu)); + window_->SetMainMenu(std::move(main_menu)); - window_->Resize(1280, 720); + window_->SetMainMenuEnabled(false); - window_->DisableMainMenu(); + UpdateTitle(); + + if (!window_->Open()) { + XELOGE("Failed to open the platform window"); + return false; + } + + Profiler::SetUserIO(kZOrderProfiler, window_.get(), nullptr, nullptr); return true; } +const char* EmulatorWindow::GetCvarValueForSwapPostEffect( + gpu::CommandProcessor::SwapPostEffect effect) { + switch (effect) { + case gpu::CommandProcessor::SwapPostEffect::kFxaa: + return "fxaa"; + case gpu::CommandProcessor::SwapPostEffect::kFxaaExtreme: + return "fxaa_extreme"; + default: + return ""; + } +} + +gpu::CommandProcessor::SwapPostEffect +EmulatorWindow::GetSwapPostEffectForCvarValue(const std::string& cvar_value) { + if (cvar_value == GetCvarValueForSwapPostEffect( + gpu::CommandProcessor::SwapPostEffect::kFxaa)) { + return gpu::CommandProcessor::SwapPostEffect::kFxaa; + } + if (cvar_value == GetCvarValueForSwapPostEffect( + gpu::CommandProcessor::SwapPostEffect::kFxaaExtreme)) { + return gpu::CommandProcessor::SwapPostEffect::kFxaaExtreme; + } + return gpu::CommandProcessor::SwapPostEffect::kNone; +} + +const char* EmulatorWindow::GetCvarValueForGuestOutputPaintEffect( + ui::Presenter::GuestOutputPaintConfig::Effect effect) { + switch (effect) { + case ui::Presenter::GuestOutputPaintConfig::Effect::kCas: + return "cas"; + case ui::Presenter::GuestOutputPaintConfig::Effect::kFsr: + return "fsr"; + default: + return ""; + } +} + +ui::Presenter::GuestOutputPaintConfig::Effect +EmulatorWindow::GetGuestOutputPaintEffectForCvarValue( + const std::string& cvar_value) { + if (cvar_value == GetCvarValueForGuestOutputPaintEffect( + ui::Presenter::GuestOutputPaintConfig::Effect::kCas)) { + return ui::Presenter::GuestOutputPaintConfig::Effect::kCas; + } + if (cvar_value == GetCvarValueForGuestOutputPaintEffect( + ui::Presenter::GuestOutputPaintConfig::Effect::kFsr)) { + return ui::Presenter::GuestOutputPaintConfig::Effect::kFsr; + } + return ui::Presenter::GuestOutputPaintConfig::Effect::kBilinear; +} + +ui::Presenter::GuestOutputPaintConfig +EmulatorWindow::GetGuestOutputPaintConfigForCvars() { + ui::Presenter::GuestOutputPaintConfig paint_config; + paint_config.SetEffect(GetGuestOutputPaintEffectForCvarValue( + cvars::postprocess_scaling_and_sharpening)); + paint_config.SetCasAdditionalSharpness( + float(cvars::postprocess_ffx_cas_additional_sharpness)); + paint_config.SetFsrMaxUpsamplingPasses( + cvars::postprocess_ffx_fsr_max_upsampling_passes); + paint_config.SetFsrSharpnessReduction( + float(cvars::postprocess_ffx_fsr_sharpness_reduction)); + paint_config.SetDither(cvars::postprocess_dither); + return paint_config; +} + +void EmulatorWindow::ApplyDisplayConfigForCvars() { + gpu::GraphicsSystem* graphics_system = emulator_->graphics_system(); + if (!graphics_system) { + return; + } + + gpu::CommandProcessor* command_processor = + graphics_system->command_processor(); + if (command_processor) { + command_processor->SetDesiredSwapPostEffect( + GetSwapPostEffectForCvarValue(cvars::postprocess_antialiasing)); + } + + ui::Presenter* presenter = graphics_system->presenter(); + if (presenter) { + presenter->SetGuestOutputPaintConfigFromUIThread( + GetGuestOutputPaintConfigForCvars()); + } +} + +void EmulatorWindow::OnKeyDown(ui::KeyEvent& e) { + if (!emulator_initialized_) { + return; + } + + switch (e.virtual_key()) { + case ui::VirtualKey::kO: { + if (!e.is_ctrl_pressed()) { + return; + } + FileOpen(); + } break; + case ui::VirtualKey::kMultiply: { + CpuTimeScalarReset(); + } break; + case ui::VirtualKey::kSubtract: { + CpuTimeScalarSetHalf(); + } break; + case ui::VirtualKey::kAdd: { + CpuTimeScalarSetDouble(); + } break; + + case ui::VirtualKey::kF3: { + Profiler::ToggleDisplay(); + } break; + + case ui::VirtualKey::kF4: { + GpuTraceFrame(); + } break; + case ui::VirtualKey::kF5: { + GpuClearCaches(); + } break; + + case ui::VirtualKey::kF6: { + ToggleDisplayConfigDialog(); + } break; + case ui::VirtualKey::kF11: { + ToggleFullscreen(); + } break; + case ui::VirtualKey::kEscape: { + // Allow users to escape fullscreen (but not enter it). + if (!window_->IsFullscreen()) { + return; + } + SetFullscreen(false); + } break; + +#ifdef DEBUG + case ui::VirtualKey::kF7: { + // Save to file + // TODO: Choose path based on user input, or from options + // TODO: Spawn a new thread to do this. + emulator()->SaveToFile("test.sav"); + } break; + case ui::VirtualKey::kF8: { + // Restore from file + // TODO: Choose path from user + // TODO: Spawn a new thread to do this. + emulator()->RestoreFromFile("test.sav"); + } break; +#endif // #ifdef DEBUG + + case ui::VirtualKey::kPause: { + CpuBreakIntoDebugger(); + } break; + case ui::VirtualKey::kCancel: { + CpuBreakIntoHostDebugger(); + } break; + + case ui::VirtualKey::kF1: { + ShowHelpWebsite(); + } break; + + case ui::VirtualKey::kF2: { + ShowCommitID(); + } break; + + default: + return; + } + + e.set_handled(true); +} + void EmulatorWindow::FileDrop(const std::filesystem::path& filename) { + if (!emulator_initialized_) { + return; + } auto result = emulator_->LaunchPath(filename); if (XFAILED(result)) { // TODO: Display a message box. @@ -316,7 +816,7 @@ void EmulatorWindow::FileOpen() { //{"Content Package (*.xcp)", "*.xcp" }, {"All Files (*.*)", "*.*"}, }); - if (file_picker->Show(window_->native_handle())) { + if (file_picker->Show(window_.get())) { auto selected_files = file_picker->selected_files(); if (!selected_files.empty()) { path = selected_files[0]; @@ -361,17 +861,6 @@ void EmulatorWindow::ShowContentDirectory() { LaunchFileExplorer(target_path); } -void EmulatorWindow::CheckHideCursor() { - if (!window_->is_fullscreen()) { - // Only hide when fullscreen. - return; - } - - if (Clock::QueryHostSystemTime() > cursor_hide_time_) { - window_->set_cursor_visible(false); - } -} - void EmulatorWindow::CpuTimeScalarReset() { Clock::set_guest_time_scalar(1.0); UpdateTitle(); @@ -389,7 +878,7 @@ void EmulatorWindow::CpuTimeScalarSetDouble() { void EmulatorWindow::CpuBreakIntoDebugger() { if (!cvars::debug) { - xe::ui::ImGuiDialog::ShowMessageBox(window_.get(), "Xenia Debugger", + xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(), "Xenia Debugger", "Xenia must be launched with the " "--debug flag in order to enable " "debugging."); @@ -415,13 +904,26 @@ void EmulatorWindow::GpuClearCaches() { emulator()->graphics_system()->ClearCaches(); } -void EmulatorWindow::ToggleFullscreen() { - window_->ToggleFullscreen(!window_->is_fullscreen()); +void EmulatorWindow::SetFullscreen(bool fullscreen) { + if (window_->IsFullscreen() == fullscreen) { + return; + } + window_->SetFullscreen(fullscreen); + window_->SetCursorVisibility(fullscreen + ? ui::Window::CursorVisibility::kAutoHidden + : ui::Window::CursorVisibility::kVisible); +} - // Hide the cursor after a second if we're going fullscreen - cursor_hide_time_ = Clock::QueryHostSystemTime() + 30000000; - if (!window_->is_fullscreen()) { - window_->set_cursor_visible(true); +void EmulatorWindow::ToggleFullscreen() { + SetFullscreen(!window_->IsFullscreen()); +} + +void EmulatorWindow::ToggleDisplayConfigDialog() { + if (!display_config_dialog_) { + display_config_dialog_ = std::unique_ptr( + new DisplayConfigDialog(imgui_drawer_.get(), *this)); + } else { + display_config_dialog_.reset(); } } @@ -477,7 +979,7 @@ void EmulatorWindow::UpdateTitle() { sb.Append(u8" (Preloading shaders\u2026)"); } - window_->set_title(sb.to_string_view()); + window_->SetTitle(sb.to_string_view()); } void EmulatorWindow::SetInitializingShaderStorage(bool initializing) { diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index 6dfb69080..ad6c4daee 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -13,42 +13,124 @@ #include #include +#include "xenia/emulator.h" +#include "xenia/gpu/command_processor.h" +#include "xenia/ui/imgui_dialog.h" +#include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/immediate_drawer.h" #include "xenia/ui/menu_item.h" +#include "xenia/ui/presenter.h" #include "xenia/ui/window.h" +#include "xenia/ui/window_listener.h" #include "xenia/ui/windowed_app_context.h" #include "xenia/xbox.h" -namespace xe { -class Emulator; -} // namespace xe - namespace xe { namespace app { class EmulatorWindow { public: + enum : size_t { + // The UI is on top of the game and is open in special cases, so + // lowest-priority. + kZOrderHidInput, + kZOrderImGui, + kZOrderProfiler, + // Emulator window controls are expected to be always accessible by the + // user, so highest-priority. + kZOrderEmulatorWindowInput, + }; + + virtual ~EmulatorWindow(); + static std::unique_ptr Create( Emulator* emulator, ui::WindowedAppContext& app_context); Emulator* emulator() const { return emulator_; } ui::WindowedAppContext& app_context() const { return app_context_; } ui::Window* window() const { return window_.get(); } + ui::ImGuiDrawer* imgui_drawer() const { return imgui_drawer_.get(); } + + ui::Presenter* GetGraphicsSystemPresenter() const; + void SetupGraphicsSystemPresenterPainting(); + void ShutdownGraphicsSystemPresenterPainting(); + + void OnEmulatorInitialized(); void UpdateTitle(); + void SetFullscreen(bool fullscreen); void ToggleFullscreen(); void SetInitializingShaderStorage(bool initializing); private: + class EmulatorWindowListener final : public ui::WindowListener, + public ui::WindowInputListener { + public: + explicit EmulatorWindowListener(EmulatorWindow& emulator_window) + : emulator_window_(emulator_window) {} + + void OnClosing(ui::UIEvent& e) override; + void OnFileDrop(ui::FileDropEvent& e) override; + + void OnKeyDown(ui::KeyEvent& e) override; + + private: + EmulatorWindow& emulator_window_; + }; + + class DisplayConfigGameConfigLoadCallback + : public Emulator::GameConfigLoadCallback { + public: + DisplayConfigGameConfigLoadCallback(Emulator& emulator, + EmulatorWindow& emulator_window) + : Emulator::GameConfigLoadCallback(emulator), + emulator_window_(emulator_window) {} + + void PostGameConfigLoad() override; + + private: + EmulatorWindow& emulator_window_; + }; + + class DisplayConfigDialog final : public ui::ImGuiDialog { + public: + DisplayConfigDialog(ui::ImGuiDrawer* imgui_drawer, + EmulatorWindow& emulator_window) + : ui::ImGuiDialog(imgui_drawer), emulator_window_(emulator_window) {} + + protected: + void OnDraw(ImGuiIO& io) override; + + private: + EmulatorWindow& emulator_window_; + }; + explicit EmulatorWindow(Emulator* emulator, ui::WindowedAppContext& app_context); bool Initialize(); + // For comparisons, use GetSwapPostEffectForCvarValue instead as the default + // fallback may be used for multiple values. + static const char* GetCvarValueForSwapPostEffect( + gpu::CommandProcessor::SwapPostEffect effect); + static gpu::CommandProcessor::SwapPostEffect GetSwapPostEffectForCvarValue( + const std::string& cvar_value); + // For comparisons, use GetGuestOutputPaintEffectForCvarValue instead as the + // default fallback may be used for multiple values. + static const char* GetCvarValueForGuestOutputPaintEffect( + ui::Presenter::GuestOutputPaintConfig::Effect effect); + static ui::Presenter::GuestOutputPaintConfig::Effect + GetGuestOutputPaintEffectForCvarValue(const std::string& cvar_value); + static ui::Presenter::GuestOutputPaintConfig + GetGuestOutputPaintConfigForCvars(); + void ApplyDisplayConfigForCvars(); + + void OnKeyDown(ui::KeyEvent& e); void FileDrop(const std::filesystem::path& filename); void FileOpen(); void FileClose(); void ShowContentDirectory(); - void CheckHideCursor(); void CpuTimeScalarReset(); void CpuTimeScalarSetHalf(); void CpuTimeScalarSetDouble(); @@ -56,15 +138,26 @@ class EmulatorWindow { void CpuBreakIntoHostDebugger(); void GpuTraceFrame(); void GpuClearCaches(); + void ToggleDisplayConfigDialog(); void ShowHelpWebsite(); void ShowCommitID(); Emulator* emulator_; ui::WindowedAppContext& app_context_; + EmulatorWindowListener window_listener_; std::unique_ptr window_; + std::unique_ptr imgui_drawer_; + std::unique_ptr + display_config_game_config_load_callback_; + // Creation may fail, in this case immediate drawer UI must not be drawn. + std::unique_ptr immediate_drawer_; + + bool emulator_initialized_ = false; + std::string base_title_; - uint64_t cursor_hide_time_ = 0; bool initializing_shader_storage_ = false; + + std::unique_ptr display_config_dialog_; }; } // namespace app diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 39a2a72b4..98ceb6578 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -28,6 +28,7 @@ #include "xenia/emulator.h" #include "xenia/ui/file_picker.h" #include "xenia/ui/window.h" +#include "xenia/ui/window_listener.h" #include "xenia/ui/windowed_app.h" #include "xenia/ui/windowed_app_context.h" #include "xenia/vfs/devices/host_path_device.h" @@ -62,8 +63,6 @@ DEFINE_string(gpu, "any", "Graphics system. Use: [any, d3d12, vulkan, null]", DEFINE_string(hid, "any", "Input system. Use: [any, nop, sdl, winkey, xinput]", "HID"); -DEFINE_bool(fullscreen, false, "Toggles fullscreen", "GPU"); - DEFINE_path( storage_root, "", "Root path for persistent internal data storage (config, etc.), or empty " @@ -192,6 +191,17 @@ class EmulatorApp final : public xe::ui::WindowedApp { } }; + class DebugWindowClosedListener final : public xe::ui::WindowListener { + public: + explicit DebugWindowClosedListener(EmulatorApp& emulator_app) + : emulator_app_(emulator_app) {} + + void OnClosing(xe::ui::UIEvent& e) override; + + private: + EmulatorApp& emulator_app_; + }; + explicit EmulatorApp(xe::ui::WindowedAppContext& app_context); static std::unique_ptr CreateAudioSystem( @@ -203,6 +213,8 @@ class EmulatorApp final : public xe::ui::WindowedApp { void EmulatorThread(); void ShutdownEmulatorThreadFromUIThread(); + DebugWindowClosedListener debug_window_closed_listener_; + std::unique_ptr emulator_; std::unique_ptr emulator_window_; @@ -215,8 +227,15 @@ class EmulatorApp final : public xe::ui::WindowedApp { std::thread emulator_thread_; }; +void EmulatorApp::DebugWindowClosedListener::OnClosing(xe::ui::UIEvent& e) { + EmulatorApp* emulator_app = &emulator_app_; + emulator_app->emulator_->processor()->set_debug_listener(nullptr); + emulator_app->debug_window_.reset(); +} + EmulatorApp::EmulatorApp(xe::ui::WindowedAppContext& app_context) - : xe::ui::WindowedApp(app_context, "xenia", "[Path to .iso/.xex]") { + : xe::ui::WindowedApp(app_context, "xenia", "[Path to .iso/.xex]"), + debug_window_closed_listener_(*this) { AddPositionalOption("target"); } @@ -252,9 +271,10 @@ std::vector> EmulatorApp::CreateInputDrivers( ui::Window* window) { std::vector> drivers; if (cvars::hid.compare("nop") == 0) { - drivers.emplace_back(xe::hid::nop::Create(window)); + drivers.emplace_back( + xe::hid::nop::Create(window, EmulatorWindow::kZOrderHidInput)); } else { - Factory factory; + Factory factory; #if XE_PLATFORM_WIN32 factory.Add("xinput", xe::hid::xinput::Create); #endif // XE_PLATFORM_WIN32 @@ -263,14 +283,16 @@ std::vector> EmulatorApp::CreateInputDrivers( // WinKey input driver should always be the last input driver added! factory.Add("winkey", xe::hid::winkey::Create); #endif // XE_PLATFORM_WIN32 - for (auto& driver : factory.CreateAll(cvars::hid, window)) { + for (auto& driver : factory.CreateAll(cvars::hid, window, + EmulatorWindow::kZOrderHidInput)) { if (XSUCCEEDED(driver->Setup())) { drivers.emplace_back(std::move(driver)); } } if (drivers.empty()) { // Fallback to nop if none created. - drivers.emplace_back(xe::hid::nop::Create(window)); + drivers.emplace_back( + xe::hid::nop::Create(window, EmulatorWindow::kZOrderHidInput)); } } return drivers; @@ -365,6 +387,9 @@ void EmulatorApp::OnDestroy() { // The profiler needs to shut down before the graphics context. Profiler::Shutdown(); + // Write all cvar overrides to the config. + config::SaveConfig(); + // TODO(DrChat): Remove this code and do a proper exit. XELOGI("Cheap-skate exit!"); std::quick_exit(EXIT_SUCCESS); @@ -378,15 +403,18 @@ void EmulatorApp::EmulatorThread() { // Setup and initialize all subsystems. If we can't do something // (unsupported system, memory issues, etc) this will fail early. - X_STATUS result = - emulator_->Setup(emulator_window_->window(), CreateAudioSystem, - CreateGraphicsSystem, CreateInputDrivers); + X_STATUS result = emulator_->Setup( + emulator_window_->window(), emulator_window_->imgui_drawer(), + CreateAudioSystem, CreateGraphicsSystem, CreateInputDrivers); if (XFAILED(result)) { XELOGE("Failed to setup emulator: {:08X}", result); app_context().RequestDeferredQuit(); return; } + app_context().CallInUIThread( + [this]() { emulator_window_->SetupGraphicsSystemPresenterPainting(); }); + if (cvars::mount_scratch) { auto scratch_device = std::make_unique( "\\SCRATCH", "scratch", false); @@ -455,12 +483,8 @@ void EmulatorApp::EmulatorThread() { app_context().CallInUIThreadSynchronous([this]() { debug_window_ = xe::debug::ui::DebugWindow::Create(emulator_.get(), app_context()); - debug_window_->window()->on_closed.AddListener( - [this](xe::ui::UIEvent* e) { - emulator_->processor()->set_debug_listener(nullptr); - app_context().CallInUIThread( - [this]() { debug_window_.reset(); }); - }); + debug_window_->window()->AddListener( + &debug_window_closed_listener_); }); // If failed to enqueue the UI thread call, this will just be null. return debug_window_.get(); @@ -489,9 +513,9 @@ void EmulatorApp::EmulatorThread() { } }); - // Enable the main menu now that the emulator is properly loaded + // Enable emulator input now that the emulator is properly loaded. app_context().CallInUIThread( - [this]() { emulator_window_->window()->EnableMainMenu(); }); + [this]() { emulator_window_->OnEmulatorInitialized(); }); // Grab path from the flag or unnamed argument. std::filesystem::path path; @@ -499,12 +523,6 @@ void EmulatorApp::EmulatorThread() { path = cvars::target; } - // Toggles fullscreen - if (cvars::fullscreen) { - app_context().CallInUIThread( - [this]() { emulator_window_->ToggleFullscreen(); }); - } - if (!path.empty()) { // Normalize the path and make absolute. auto abs_path = std::filesystem::absolute(path); diff --git a/src/xenia/base/cvar.h b/src/xenia/base/cvar.h index 1f92d0766..585039fd6 100644 --- a/src/xenia/base/cvar.h +++ b/src/xenia/base/cvar.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -86,6 +86,10 @@ class ConfigVar : public CommandVar, virtual public IConfigVar { void LoadGameConfigValue(std::shared_ptr result) override; void SetConfigValue(T val); void SetGameConfigValue(T val); + // Changes the actual value used to the one specified, and also makes it the + // one that will be stored when the global config is written next time. After + // overriding, however, the next game config loaded may still change it. + void OverrideConfigValue(T val); private: std::string category_; @@ -260,6 +264,16 @@ void ConfigVar::SetGameConfigValue(T val) { UpdateValue(); } template +void ConfigVar::OverrideConfigValue(T val) { + config_value_ = std::make_unique(val); + // The user explicitly changes the value at runtime and wants it to take + // effect immediately. Drop everything with a higher priority. The next game + // config load, however, may still change it. + game_config_value_.reset(); + this->commandline_value_.reset(); + UpdateValue(); +} +template void ConfigVar::ResetConfigValueToDefault() { SetConfigValue(this->default_value_); } @@ -373,6 +387,28 @@ ICommandVar* define_cmdvar(const char* name, T* default_value, extern type name; \ } +#define ACCESS_CVar(name) (*cv::cv_##name) + +// dynamic_cast is needed because of virtual inheritance. +#define OVERRIDE_CVar(name, type, value) \ + dynamic_cast*>(&ACCESS_CVar(name)) \ + ->OverrideConfigValue(value); + +#define OVERRIDE_bool(name, value) OVERRIDE_CVar(name, bool, value) + +#define OVERRIDE_int32(name, value) OVERRIDE_CVar(name, int32_t, value) + +#define OVERRIDE_uint32(name, value) OVERRIDE_CVar(name, uint32_t, value) + +#define OVERRIDE_uint64(name, value) OVERRIDE_CVar(name, uint64_t, value) + +#define OVERRIDE_double(name, value) OVERRIDE_CVar(name, double, value) + +#define OVERRIDE_string(name, value) OVERRIDE_CVar(name, std::string, value) + +#define OVERRIDE_path(name, value) \ + OVERRIDE_CVar(name, std::filesystem::path, value) + // Interface for changing the default value of a variable with auto-upgrading of // users' configs (to distinguish between a leftover old default and an explicit // override), without having to rename the variable. diff --git a/src/xenia/base/filesystem.h b/src/xenia/base/filesystem.h index 036a6a7a4..c8cff150f 100644 --- a/src/xenia/base/filesystem.h +++ b/src/xenia/base/filesystem.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -41,8 +41,8 @@ std::filesystem::path GetUserFolder(); // attempting to create it. bool CreateParentFolder(const std::filesystem::path& path); -// Creates an empty file at the given path. -bool CreateFile(const std::filesystem::path& path); +// Creates an empty file at the given path, overwriting if it exists. +bool CreateEmptyFile(const std::filesystem::path& path); // Opens the file at the given path with the specified mode. // This behaves like fopen and the returned handle can be used with stdio. diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index 497c98bd3..2e9ddb2c5 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -122,7 +122,7 @@ static uint64_t convertUnixtimeToWinFiletime(time_t unixtime) { return filetime; } -bool CreateFile(const std::filesystem::path& path) { +bool CreateEmptyFile(const std::filesystem::path& path) { int file = creat(path.c_str(), 0774); if (file >= 0) { close(file); diff --git a/src/xenia/base/filesystem_win.cc b/src/xenia/base/filesystem_win.cc index 9daf15af5..f1f16c063 100644 --- a/src/xenia/base/filesystem_win.cc +++ b/src/xenia/base/filesystem_win.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -60,7 +60,7 @@ std::filesystem::path GetUserFolder() { return result; } -bool CreateFile(const std::filesystem::path& path) { +bool CreateEmptyFile(const std::filesystem::path& path) { auto handle = CreateFileW(path.c_str(), 0, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (handle == INVALID_HANDLE_VALUE) { diff --git a/src/xenia/base/main_win.cc b/src/xenia/base/main_win.cc index 33bd65e75..b6eaa4a7b 100644 --- a/src/xenia/base/main_win.cc +++ b/src/xenia/base/main_win.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -19,37 +19,64 @@ // Autogenerated by `xb premake`. #include "build/version.h" -// For RequestHighPerformance. +// For RequestWin32MMCSS. +#include +// For RequestWin32HighResolutionTimer. #include -DEFINE_bool(win32_high_freq, true, - "Requests high performance from the NT kernel", "Kernel"); +DEFINE_bool(win32_high_resolution_timer, true, + "Requests high-resolution timer from the NT kernel", "Win32"); +DEFINE_bool( + win32_mmcss, true, + "Opt in the Multimedia Class Scheduler Service (MMCSS) scheduling for " + "prioritized access to CPU resources", + "Win32"); namespace xe { -static void RequestHighPerformance() { -#if XE_PLATFORM_WIN32 - NTSTATUS(*NtQueryTimerResolution) - (OUT PULONG MinimumResolution, OUT PULONG MaximumResolution, - OUT PULONG CurrentResolution); +static void RequestWin32HighResolutionTimer() { + HMODULE ntdll_module = GetModuleHandleW(L"ntdll.dll"); + if (!ntdll_module) { + return; + } - NTSTATUS(*NtSetTimerResolution) - (IN ULONG DesiredResolution, IN BOOLEAN SetResolution, - OUT PULONG CurrentResolution); - - NtQueryTimerResolution = (decltype(NtQueryTimerResolution))GetProcAddress( - GetModuleHandleW(L"ntdll.dll"), "NtQueryTimerResolution"); - NtSetTimerResolution = (decltype(NtSetTimerResolution))GetProcAddress( - GetModuleHandleW(L"ntdll.dll"), "NtSetTimerResolution"); - if (!NtQueryTimerResolution || !NtSetTimerResolution) { + // clang-format off + NTSTATUS (NTAPI* nt_query_timer_resolution)(OUT PULONG MinimumResolution, + OUT PULONG MaximumResolution, + OUT PULONG CurrentResolution); + NTSTATUS (NTAPI* nt_set_timer_resolution)(IN ULONG DesiredResolution, + IN BOOLEAN SetResolution, + OUT PULONG CurrentResolution); + // clang-format on + nt_query_timer_resolution = + reinterpret_cast( + GetProcAddress(ntdll_module, "NtQueryTimerResolution")); + nt_set_timer_resolution = reinterpret_cast( + GetProcAddress(ntdll_module, "NtSetTimerResolution")); + if (!nt_query_timer_resolution || !nt_set_timer_resolution) { return; } ULONG minimum_resolution, maximum_resolution, current_resolution; - NtQueryTimerResolution(&minimum_resolution, &maximum_resolution, - ¤t_resolution); - NtSetTimerResolution(maximum_resolution, TRUE, ¤t_resolution); -#endif + nt_query_timer_resolution(&minimum_resolution, &maximum_resolution, + ¤t_resolution); + nt_set_timer_resolution(maximum_resolution, TRUE, ¤t_resolution); +} + +static void RequestWin32MMCSS() { + HMODULE dwmapi_module = LoadLibraryW(L"dwmapi.dll"); + if (!dwmapi_module) { + return; + } + // clang-format off + HRESULT (STDAPICALLTYPE* dwm_enable_mmcss)(BOOL fEnableMMCSS); + // clang-format on + dwm_enable_mmcss = reinterpret_cast( + GetProcAddress(dwmapi_module, "DwmEnableMMCSS")); + if (dwm_enable_mmcss) { + dwm_enable_mmcss(TRUE); + } + FreeLibrary(dwmapi_module); } bool ParseWin32LaunchArguments( @@ -103,9 +130,12 @@ int InitializeWin32App(const std::string_view app_name) { #endif XE_BUILD_BRANCH "@" XE_BUILD_COMMIT_SHORT " on " XE_BUILD_DATE); - // Request high performance timing. - if (cvars::win32_high_freq) { - RequestHighPerformance(); + // Request high-performance timing and scheduling. + if (cvars::win32_high_resolution_timer) { + RequestWin32HighResolutionTimer(); + } + if (cvars::win32_mmcss) { + RequestWin32MMCSS(); } return 0; diff --git a/src/xenia/base/math.h b/src/xenia/base/math.h index ac3ef8056..de92fbc7e 100644 --- a/src/xenia/base/math.h +++ b/src/xenia/base/math.h @@ -53,8 +53,11 @@ constexpr T align(T value, T alignment) { // Rounds the given number up to the next highest multiple. template -constexpr T round_up(T value, V multiple) { - return value ? (((value + multiple - 1) / multiple) * multiple) : multiple; +constexpr T round_up(T value, V multiple, bool force_non_zero = true) { + if (force_non_zero && !value) { + return multiple; + } + return (value + multiple - 1) / multiple * multiple; } // Using the same conventions as in shading languages, returning 0 for NaN. diff --git a/src/xenia/base/profiling.cc b/src/xenia/base/profiling.cc index 038c26058..800ab8680 100644 --- a/src/xenia/base/profiling.cc +++ b/src/xenia/base/profiling.cc @@ -2,11 +2,12 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ +#include #include // NOTE: this must be included before microprofile as macro expansion needs @@ -36,6 +37,7 @@ #include "xenia/base/assert.h" #include "xenia/base/cvar.h" #include "xenia/base/profiling.h" +#include "xenia/ui/ui_event.h" #include "xenia/ui/virtual_key.h" #include "xenia/ui/window.h" @@ -44,21 +46,27 @@ #endif // XE_OPTION_PROFILING #if XE_OPTION_PROFILING_UI -#undef DrawText #include "xenia/ui/microprofile_drawer.h" #endif // XE_OPTION_PROFILING_UI +DEFINE_bool(profiler_dpi_scaling, false, + "Apply window DPI scaling to the profiler.", "UI"); DEFINE_bool(show_profiler, false, "Show profiling UI by default.", "UI"); namespace xe { -#if XE_OPTION_PROFILING_UI -ui::Window* Profiler::window_ = nullptr; -std::unique_ptr Profiler::drawer_ = nullptr; -#endif // XE_OPTION_PROFILING_UI - #if XE_OPTION_PROFILING +Profiler::ProfilerWindowInputListener Profiler::input_listener_; +size_t Profiler::z_order_ = 0; +ui::Window* Profiler::window_ = nullptr; +#if XE_OPTION_PROFILING_UI +Profiler::ProfilerUIDrawer Profiler::ui_drawer_; +ui::Presenter* Profiler::presenter_ = nullptr; +std::unique_ptr Profiler::drawer_; +bool Profiler::dpi_scaling_ = false; +#endif // XE_OPTION_PROFILING_UI + bool Profiler::is_enabled() { return true; } bool Profiler::is_visible() { return is_enabled() && MicroProfileIsDrawing(); } @@ -79,6 +87,7 @@ void Profiler::Initialize() { g_MicroProfile.nActiveBars |= 0x1 | 0x2; #if XE_OPTION_PROFILING_UI + dpi_scaling_ = cvars::profiler_dpi_scaling; MicroProfileInitUI(); g_MicroProfileUI.bShowSpikes = true; g_MicroProfileUI.nOpacityBackground = 0x40u << 24; @@ -102,7 +111,7 @@ void Profiler::Dump() { } void Profiler::Shutdown() { - drawer_.reset(); + SetUserIO(0, nullptr, nullptr, nullptr); window_ = nullptr; MicroProfileShutdown(); } @@ -119,144 +128,208 @@ void Profiler::ThreadEnter(const char* name) { void Profiler::ThreadExit() { MicroProfileOnThreadExit(); } -bool Profiler::OnKeyDown(ui::VirtualKey virtual_key) { +void Profiler::ProfilerWindowInputListener::OnKeyDown(ui::KeyEvent& e) { // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx - switch (virtual_key) { + bool handled = true; + switch (e.virtual_key()) { case ui::VirtualKey::kOem3: // ` MicroProfileTogglePause(); - return true; + break; #if XE_OPTION_PROFILING_UI case ui::VirtualKey::kTab: - MicroProfileToggleDisplayMode(); - return true; + ToggleDisplay(); + break; case ui::VirtualKey::k1: MicroProfileModKey(1); - return true; + break; #endif // XE_OPTION_PROFILING_UI default: + handled = false; break; } - return false; + if (handled) { + e.set_handled(true); + } + PostInputEvent(); } -bool Profiler::OnKeyUp(ui::VirtualKey virtual_key) { - switch (virtual_key) { +void Profiler::ProfilerWindowInputListener::OnKeyUp(ui::KeyEvent& e) { + bool handled = true; + switch (e.virtual_key()) { #if XE_OPTION_PROFILING_UI case ui::VirtualKey::k1: MicroProfileModKey(0); - return true; + break; #endif // XE_OPTION_PROFILING_UI default: + handled = false; break; } - return false; + if (handled) { + e.set_handled(true); + } + PostInputEvent(); } #if XE_OPTION_PROFILING_UI -void Profiler::OnMouseDown(bool left_button, bool right_button) { - MicroProfileMouseButton(left_button, right_button); +void Profiler::ProfilerWindowInputListener::OnMouseDown(ui::MouseEvent& e) { + Profiler::SetMousePosition(e.x(), e.y(), 0); + MicroProfileMouseButton(e.button() == ui::MouseEvent::Button::kLeft, + e.button() == ui::MouseEvent::Button::kRight); + e.set_handled(true); + PostInputEvent(); } -void Profiler::OnMouseUp() { MicroProfileMouseButton(0, 0); } - -void Profiler::OnMouseMove(int x, int y) { MicroProfileMousePosition(x, y, 0); } - -void Profiler::OnMouseWheel(int x, int y, int dy) { - MicroProfileMousePosition(x, y, dy); +void Profiler::ProfilerWindowInputListener::OnMouseUp(ui::MouseEvent& e) { + Profiler::SetMousePosition(e.x(), e.y(), 0); + MicroProfileMouseButton(0, 0); + e.set_handled(true); + PostInputEvent(); } -void Profiler::ToggleDisplay() { MicroProfileToggleDisplayMode(); } +void Profiler::ProfilerWindowInputListener::OnMouseMove(ui::MouseEvent& e) { + Profiler::SetMousePosition(e.x(), e.y(), 0); + e.set_handled(true); + PostInputEvent(); +} + +void Profiler::ProfilerWindowInputListener::OnMouseWheel(ui::MouseEvent& e) { + Profiler::SetMousePosition(e.x(), e.y(), e.scroll_y()); + e.set_handled(true); + PostInputEvent(); +} void Profiler::TogglePause() { MicroProfileTogglePause(); } #else -void Profiler::OnMouseDown(bool left_button, bool right_button) {} - -void Profiler::OnMouseUp() {} - -void Profiler::OnMouseMove(int x, int y) {} - -void Profiler::OnMouseWheel(int x, int y, int dy) {} - -void Profiler::ToggleDisplay() {} - void Profiler::TogglePause() {} #endif // XE_OPTION_PROFILING_UI -void Profiler::set_window(ui::Window* window) { - assert_null(window_); +void Profiler::ToggleDisplay() { + bool was_visible = is_visible(); + MicroProfileToggleDisplayMode(); + if (is_visible() != was_visible) { + if (window_) { + if (was_visible) { + window_->RemoveInputListener(&input_listener_); + } else { + window_->AddInputListener(&input_listener_, z_order_); + } + } +#if XE_OPTION_PROFILING_UI + if (presenter_) { + if (was_visible) { + presenter_->RemoveUIDrawerFromUIThread(&ui_drawer_); + } else { + presenter_->AddUIDrawerFromUIThread(&ui_drawer_, z_order_); + } + } +#endif // XE_OPTION_PROFILING_UI + } +} + +void Profiler::SetUserIO(size_t z_order, ui::Window* window, + ui::Presenter* presenter, + ui::ImmediateDrawer* immediate_drawer) { +#if XE_OPTION_PROFILING_UI + if (presenter_ && is_visible()) { + presenter_->RemoveUIDrawerFromUIThread(&ui_drawer_); + } + drawer_.reset(); + presenter_ = nullptr; +#endif // XE_OPTION_PROFILING_UI + + if (window_) { + if (is_visible()) { + window_->RemoveInputListener(&input_listener_); + } + window_ = nullptr; + } + if (!window) { return; } + + z_order_ = z_order; window_ = window; - drawer_ = std::make_unique(window); - window_->on_painted.AddListener([](ui::UIEvent* e) { Profiler::Present(); }); +#if XE_OPTION_PROFILING_UI + if (presenter && immediate_drawer) { + presenter_ = presenter; + drawer_ = std::make_unique(immediate_drawer); + } +#endif // XE_OPTION_PROFILING_UI - // Pass through mouse events. - window_->on_mouse_down.AddListener([](ui::MouseEvent* e) { - if (Profiler::is_visible()) { - Profiler::OnMouseDown(e->button() == ui::MouseEvent::Button::kLeft, - e->button() == ui::MouseEvent::Button::kRight); - e->set_handled(true); - window_->Invalidate(); + if (is_visible()) { + window_->AddInputListener(&input_listener_, z_order_); +#if XE_OPTION_PROFILING_UI + if (presenter_) { + presenter_->AddUIDrawerFromUIThread(&ui_drawer_, z_order_); } - }); - window_->on_mouse_up.AddListener([](ui::MouseEvent* e) { - if (Profiler::is_visible()) { - Profiler::OnMouseUp(); - e->set_handled(true); - window_->Invalidate(); - } - }); - window_->on_mouse_move.AddListener([](ui::MouseEvent* e) { - if (Profiler::is_visible()) { - Profiler::OnMouseMove(e->x(), e->y()); - e->set_handled(true); - window_->Invalidate(); - } - }); - window_->on_mouse_wheel.AddListener([](ui::MouseEvent* e) { - if (Profiler::is_visible()) { - Profiler::OnMouseWheel(e->x(), e->y(), -e->dy()); - e->set_handled(true); - window_->Invalidate(); - } - }); - - // Watch for toggle/mode keys and such. - window_->on_key_down.AddListener([](ui::KeyEvent* e) { - if (Profiler::is_visible()) { - Profiler::OnKeyDown(e->virtual_key()); - e->set_handled(true); - window_->Invalidate(); - } - }); - window_->on_key_up.AddListener([](ui::KeyEvent* e) { - if (Profiler::is_visible()) { - Profiler::OnKeyUp(e->virtual_key()); - e->set_handled(true); - window_->Invalidate(); - } - }); +#endif // XE_OPTION_PROFILING_UI + } +} + +void Profiler::Flip() { + MicroProfileFlip(); + // This can be called from non-UI threads, so not trying to access the drawer + // to trigger redraw here as it's owned and managed exclusively by the UI + // thread. Relying on continuous painting currently. } -void Profiler::Present() { - SCOPE_profile_cpu_f("internal"); #if XE_OPTION_PROFILING_UI - if (!window_ || !drawer_) { +void Profiler::ProfilerUIDrawer::Draw(ui::UIDrawContext& ui_draw_context) { + if (!window_ || !presenter_ || !drawer_) { return; } - drawer_->Begin(); - MicroProfileDraw(window_->scaled_width(), window_->scaled_height()); + SCOPE_profile_cpu_f("internal"); + uint32_t coordinate_space_width = dpi_scaling_ + ? window_->GetActualLogicalWidth() + : window_->GetActualPhysicalWidth(); + uint32_t coordinate_space_height = dpi_scaling_ + ? window_->GetActualLogicalHeight() + : window_->GetActualPhysicalHeight(); + drawer_->Begin(ui_draw_context, coordinate_space_width, + coordinate_space_height); + MicroProfileDraw(coordinate_space_width, coordinate_space_height); drawer_->End(); -#endif // XE_OPTION_PROFILING_UI + // Continuous repaint. + if (is_visible()) { + presenter_->RequestUIPaintFromUIThread(); + } } +#endif // XE_OPTION_PROFILING_UI -void Profiler::Flip() { MicroProfileFlip(); } +#if XE_OPTION_PROFILING_UI +void Profiler::SetMousePosition(int32_t x, int32_t y, int32_t wheel_delta) { + if (!window_) { + return; + } + if (dpi_scaling_) { + x = window_->PositionToLogical(x); + y = window_->PositionToLogical(y); + } + MicroProfileMousePosition(uint32_t(std::max(int32_t(0), x)), + uint32_t(std::max(int32_t(0), y)), wheel_delta); +} +#endif // XE_OPTION_PROFILING_UI + +void Profiler::PostInputEvent() { + // The profiler can be hidden from within the profiler (Mode > Off). + if (!is_visible()) { + window_->RemoveInputListener(&input_listener_); +#if XE_OPTION_PROFILING_UI + if (presenter_) { + presenter_->RemoveUIDrawerFromUIThread(&ui_drawer_); + } +#endif // XE_OPTION_PROFILING_UI + return; + } + // Relying on continuous painting currently, no need to request drawing. +} #else @@ -268,16 +341,11 @@ void Profiler::Shutdown() {} uint32_t Profiler::GetColor(const char* str) { return 0; } void Profiler::ThreadEnter(const char* name) {} void Profiler::ThreadExit() {} -bool Profiler::OnKeyDown(ui::VirtualKey virtual_key) { return false; } -bool Profiler::OnKeyUp(ui::VirtualKey virtual_key) { return false; } -void Profiler::OnMouseDown(bool left_button, bool right_button) {} -void Profiler::OnMouseUp() {} -void Profiler::OnMouseMove(int x, int y) {} -void Profiler::OnMouseWheel(int x, int y, int dy) {} void Profiler::ToggleDisplay() {} void Profiler::TogglePause() {} -void Profiler::set_window(ui::Window* window) {} -void Profiler::Present() {} +void Profiler::SetUserIO(size_t z_order, ui::Window* window, + ui::Presenter* presenter, + ui::ImmediateDrawer* immediate_drawer) {} void Profiler::Flip() {} #endif // XE_OPTION_PROFILING @@ -316,7 +384,7 @@ void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText, if (!drawer) { return; } - drawer->DrawText(nX, nY, nColor, pText, nLen); + drawer->DrawTextString(nX, nY, nColor, pText, nLen); } #endif // XE_OPTION_PROFILING_UI diff --git a/src/xenia/base/profiling.h b/src/xenia/base/profiling.h index a55600d7e..24b875967 100644 --- a/src/xenia/base/profiling.h +++ b/src/xenia/base/profiling.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,10 +10,15 @@ #ifndef XENIA_BASE_PROFILING_H_ #define XENIA_BASE_PROFILING_H_ +#include +#include #include +#include "xenia/base/platform.h" #include "xenia/base/string.h" +#include "xenia/ui/ui_drawer.h" #include "xenia/ui/virtual_key.h" +#include "xenia/ui/window_listener.h" #if XE_PLATFORM_WIN32 #define XE_OPTION_PROFILING 1 @@ -30,7 +35,9 @@ namespace xe { namespace ui { +class ImmediateDrawer; class MicroprofileDrawer; +class Presenter; class Window; } // namespace ui } // namespace xe @@ -172,27 +179,65 @@ class Profiler { // Deactivates the calling thread for profiling. static void ThreadExit(); - static bool OnKeyDown(ui::VirtualKey virtual_key); - static bool OnKeyUp(ui::VirtualKey virtual_key); - static void OnMouseDown(bool left_button, bool right_button); - static void OnMouseUp(); - static void OnMouseMove(int x, int y); - static void OnMouseWheel(int x, int y, int dy); static void ToggleDisplay(); static void TogglePause(); - // Initializes input and drawing with the given display. - static void set_window(ui::Window* window); - // Gets the current display, if any. - static ui::MicroprofileDrawer* drawer() { return drawer_.get(); } + // Initializes input for the given window and drawing for the given presenter + // and immediate drawer. + static void SetUserIO(size_t z_order, ui::Window* window, + ui::Presenter* presenter, + ui::ImmediateDrawer* immediate_drawer); + // Gets the current drawer, if any. + static ui::MicroprofileDrawer* drawer() { +#if XE_OPTION_PROFILING_UI + return drawer_.get(); +#else + return nullptr; +#endif + } // Presents the profiler to the bound display, if any. - static void Present(); + static void Present(ui::UIDrawContext& ui_draw_context); // Starts a new frame on the profiler static void Flip(); private: +#if XE_OPTION_PROFILING + class ProfilerWindowInputListener final : public ui::WindowInputListener { + public: + void OnKeyDown(ui::KeyEvent& e) override; + void OnKeyUp(ui::KeyEvent& e) override; +#if XE_OPTION_PROFILING_UI + void OnMouseDown(ui::MouseEvent& e) override; + void OnMouseMove(ui::MouseEvent& e) override; + void OnMouseUp(ui::MouseEvent& e) override; + void OnMouseWheel(ui::MouseEvent& e) override; +#endif // XE_OPTION_PROFILING_UI + }; + // For now, no need for OnDpiChanged in a WindowListener because redrawing is + // done continuously. + +#if XE_OPTION_PROFILING_UI + class ProfilerUIDrawer final : public ui::UIDrawer { + public: + void Draw(ui::UIDrawContext& context) override; + }; +#endif // XE_OPTION_PROFILING_UI + +#if XE_OPTION_PROFILING_UI + static void SetMousePosition(int32_t x, int32_t y, int32_t wheel_delta); +#endif // XE_OPTION_PROFILING_UI + static void PostInputEvent(); + + static ProfilerWindowInputListener input_listener_; + static size_t z_order_; static ui::Window* window_; +#if XE_OPTION_PROFILING_UI + static ProfilerUIDrawer ui_drawer_; + static ui::Presenter* presenter_; static std::unique_ptr drawer_; + static bool dpi_scaling_; +#endif // XE_OPTION_PROFILING_UI +#endif // XE_OPTION_PROFILING }; } // namespace xe diff --git a/src/xenia/config.cc b/src/xenia/config.cc index be19bccda..dcdc5ac2b 100644 --- a/src/xenia/config.cc +++ b/src/xenia/config.cc @@ -105,6 +105,10 @@ void ReadGameConfig(const std::filesystem::path& file_path) { } void SaveConfig() { + if (config_path.empty()) { + return; + } + // All cvar defaults have been updated on loading - store the current date. auto defaults_date_cvar = dynamic_cast*>(cv::cv_defaults_date); diff --git a/src/xenia/config.h b/src/xenia/config.h index 63ef265d5..2271cea6c 100644 --- a/src/xenia/config.h +++ b/src/xenia/config.h @@ -15,6 +15,7 @@ namespace config { void SetupConfig(const std::filesystem::path& config_folder); void LoadGameConfig(const std::string_view title_id); +void SaveConfig(); } // namespace config #endif // XENIA_CONFIG_H_ diff --git a/src/xenia/debug/ui/debug_window.cc b/src/xenia/debug/ui/debug_window.cc index bb9ee8225..bcbf7e042 100644 --- a/src/xenia/debug/ui/debug_window.cc +++ b/src/xenia/debug/ui/debug_window.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -32,6 +32,8 @@ #include "xenia/kernel/xthread.h" #include "xenia/ui/graphics_provider.h" #include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/presenter.h" #include "xenia/ui/windowed_app_context.h" DEFINE_bool(imgui_debug, false, "Show ImGui debugging tools.", "UI"); @@ -49,14 +51,18 @@ using xe::ui::MenuItem; using xe::ui::MouseEvent; using xe::ui::UIEvent; -const std::string kBaseTitle = "Xenia Debugger"; +void DebugWindow::DebugDialog::OnDraw(ImGuiIO& io) { + debug_window_.DrawFrame(io); +} + +static const std::string kBaseTitle = "Xenia Debugger"; DebugWindow::DebugWindow(Emulator* emulator, xe::ui::WindowedAppContext& app_context) : emulator_(emulator), processor_(emulator->processor()), app_context_(app_context), - window_(xe::ui::Window::Create(app_context_, kBaseTitle)) { + window_(xe::ui::Window::Create(app_context_, kBaseTitle, 1500, 1000)) { if (cs_open(CS_ARCH_X86, CS_MODE_64, &capstone_handle_) != CS_ERR_OK) { assert_always("Failed to initialize capstone"); } @@ -86,44 +92,57 @@ std::unique_ptr DebugWindow::Create( } bool DebugWindow::Initialize() { - if (!window_->Initialize()) { - XELOGE("Failed to initialize platform window"); - return false; - } - // Main menu. auto main_menu = MenuItem::Create(MenuItem::Type::kNormal); auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, "&File"); { - file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, "&Close", - "Alt+F4", - [this]() { window_->Close(); })); + file_menu->AddChild( + MenuItem::Create(MenuItem::Type::kString, "&Close", "Alt+F4", + [this]() { window_->RequestClose(); })); } main_menu->AddChild(std::move(file_menu)); - window_->set_main_menu(std::move(main_menu)); + window_->SetMainMenu(std::move(main_menu)); - window_->Resize(1500, 1000); + // Open the window once it's configured. + if (!window_->Open()) { + XELOGE("Failed to open the platform window for the debugger"); + return false; + } - // Create the graphics context used for drawing. - auto provider = emulator_->display_window()->context()->provider(); - window_->set_context(provider->CreateContext(window_.get())); + // Setup drawing to the window. - // Enable imgui input. - window_->set_imgui_input_enabled(true); + xe::ui::GraphicsProvider& graphics_provider = + *emulator_->graphics_system()->provider(); - window_->on_painting.AddListener([this](UIEvent* e) { DrawFrame(); }); + presenter_ = graphics_provider.CreatePresenter(); + if (!presenter_) { + XELOGE("Failed to initialize the presenter for the debugger"); + return false; + } + immediate_drawer_ = graphics_provider.CreateImmediateDrawer(); + if (!immediate_drawer_) { + XELOGE("Failed to initialize the immediate drawer for the debugger"); + return false; + } + immediate_drawer_->SetPresenter(presenter_.get()); + + imgui_drawer_ = std::make_unique(window_.get(), 0); + imgui_drawer_->SetPresenterAndImmediateDrawer(presenter_.get(), + immediate_drawer_.get()); + debug_dialog_ = + std::unique_ptr(new DebugDialog(imgui_drawer_.get(), *this)); + + // Update the cache before the first frame. UpdateCache(); - window_->Invalidate(); + + // Begin drawing. + window_->SetPresenter(presenter_.get()); return true; } -void DebugWindow::DrawFrame() { - xe::ui::GraphicsContextLock lock(window_->context()); - - auto& io = window_->imgui_drawer()->GetIO(); - +void DebugWindow::DrawFrame(ImGuiIO& io) { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(-1, 0)); ImGui::Begin("main_window", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | @@ -242,9 +261,6 @@ void DebugWindow::DrawFrame() { ImGui::ShowDemoWindow(); ImGui::ShowMetricsWindow(); } - - // Continuous paint. - window_->Invalidate(); } void DebugWindow::DrawToolbar() { @@ -1443,7 +1459,7 @@ void DebugWindow::UpdateCache() { title += " (stepping)"; break; } - window_->set_title(title); + window_->SetTitle(title); }); cache_.is_running = @@ -1573,7 +1589,7 @@ void DebugWindow::OnBreakpointHit(Breakpoint* breakpoint, } void DebugWindow::Focus() const { - app_context_.CallInUIThread([this]() { window_->set_focus(true); }); + app_context_.CallInUIThread([this]() { window_->Focus(); }); } } // namespace ui diff --git a/src/xenia/debug/ui/debug_window.h b/src/xenia/debug/ui/debug_window.h index 7c9cfae4f..be7294940 100644 --- a/src/xenia/debug/ui/debug_window.h +++ b/src/xenia/debug/ui/debug_window.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -18,7 +18,11 @@ #include "xenia/cpu/debug_listener.h" #include "xenia/cpu/processor.h" #include "xenia/emulator.h" +#include "xenia/ui/imgui_dialog.h" +#include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/immediate_drawer.h" #include "xenia/ui/menu_item.h" +#include "xenia/ui/presenter.h" #include "xenia/ui/window.h" #include "xenia/ui/windowed_app_context.h" #include "xenia/xbox.h" @@ -48,11 +52,24 @@ class DebugWindow : public cpu::DebugListener { cpu::ThreadDebugInfo* thread_info) override; private: + class DebugDialog final : public xe::ui::ImGuiDialog { + public: + explicit DebugDialog(xe::ui::ImGuiDrawer* imgui_drawer, + DebugWindow& debug_window) + : xe::ui::ImGuiDialog(imgui_drawer), debug_window_(debug_window) {} + + protected: + void OnDraw(ImGuiIO& io) override; + + private: + DebugWindow& debug_window_; + }; + explicit DebugWindow(Emulator* emulator, xe::ui::WindowedAppContext& app_context); bool Initialize(); - void DrawFrame(); + void DrawFrame(ImGuiIO& io); void DrawToolbar(); void DrawFunctionsPane(); void DrawSourcePane(); @@ -93,7 +110,10 @@ class DebugWindow : public cpu::DebugListener { cpu::Processor* processor_ = nullptr; xe::ui::WindowedAppContext& app_context_; std::unique_ptr window_; - uint64_t last_draw_tick_count_ = 0; + std::unique_ptr presenter_; + std::unique_ptr immediate_drawer_; + std::unique_ptr imgui_drawer_; + std::unique_ptr debug_dialog_; uintptr_t capstone_handle_ = 0; diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 0af32710c..0fbf74d18 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -9,6 +9,7 @@ #include "xenia/emulator.h" +#include #include #include "config.h" @@ -23,7 +24,6 @@ #include "xenia/base/literals.h" #include "xenia/base/logging.h" #include "xenia/base/mapped_memory.h" -#include "xenia/base/profiling.h" #include "xenia/base/string.h" #include "xenia/cpu/backend/code_cache.h" #include "xenia/cpu/backend/x64/x64_backend.h" @@ -41,6 +41,7 @@ #include "xenia/kernel/xboxkrnl/xboxkrnl_module.h" #include "xenia/memory.h" #include "xenia/ui/imgui_dialog.h" +#include "xenia/ui/imgui_drawer.h" #include "xenia/ui/window.h" #include "xenia/ui/windowed_app_context.h" #include "xenia/vfs/devices/disc_image_device.h" @@ -63,6 +64,15 @@ namespace xe { using namespace xe::literals; +Emulator::GameConfigLoadCallback::GameConfigLoadCallback(Emulator& emulator) + : emulator_(emulator) { + emulator_.AddGameConfigLoadCallback(this); +} + +Emulator::GameConfigLoadCallback::~GameConfigLoadCallback() { + emulator_.RemoveGameConfigLoadCallback(this); +} + Emulator::Emulator(const std::filesystem::path& command_line, const std::filesystem::path& storage_root, const std::filesystem::path& content_root, @@ -116,7 +126,7 @@ Emulator::~Emulator() { } X_STATUS Emulator::Setup( - ui::Window* display_window, + ui::Window* display_window, ui::ImGuiDrawer* imgui_drawer, std::function(cpu::Processor*)> audio_system_factory, std::function()> @@ -126,6 +136,7 @@ X_STATUS Emulator::Setup( X_STATUS result = X_STATUS_UNSUCCESSFUL; display_window_ = display_window; + imgui_drawer_ = imgui_drawer; // Initialize clock. // 360 uses a 50MHz clock. @@ -212,8 +223,10 @@ X_STATUS Emulator::Setup( kernel_state_ = std::make_unique(this); // Setup the core components. - result = graphics_system_->Setup(processor_.get(), kernel_state_.get(), - display_window_); + result = graphics_system_->Setup( + processor_.get(), kernel_state_.get(), + display_window_ ? &display_window_->app_context() : nullptr, + display_window_ != nullptr); if (result) { return result; } @@ -236,14 +249,6 @@ X_STATUS Emulator::Setup( // Initialize emulator fallback exception handling last. ExceptionHandler::Install(Emulator::ExceptionCallbackThunk, this); - if (display_window_) { - // Finish initializing the display. - display_window_->app_context().CallInUIThreadSynchronous([this]() { - xe::ui::GraphicsContextLock context_lock(display_window_->context()); - Profiler::set_window(display_window_); - }); - } - return result; } @@ -417,7 +422,7 @@ void Emulator::Resume() { bool Emulator::SaveToFile(const std::filesystem::path& path) { Pause(); - filesystem::CreateFile(path); + filesystem::CreateEmptyFile(path); auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite, 0, 2_GiB); if (!map) { return false; @@ -587,14 +592,16 @@ bool Emulator::ExceptionCallback(Exception* ex) { } // Display a dialog telling the user the guest has crashed. - display_window()->app_context().CallInUIThreadSynchronous([this]() { - xe::ui::ImGuiDialog::ShowMessageBox( - display_window(), "Uh-oh!", - "The guest has crashed.\n\n" - "" - "Xenia has now paused itself.\n" - "A crash dump has been written into the log."); - }); + if (display_window_ && imgui_drawer_) { + display_window_->app_context().CallInUIThreadSynchronous([this]() { + xe::ui::ImGuiDialog::ShowMessageBox( + imgui_drawer_, "Uh-oh!", + "The guest has crashed.\n\n" + "" + "Xenia has now paused itself.\n" + "A crash dump has been written into the log."); + }); + } // Now suspend ourself (we should be a guest thread). current_thread->Suspend(nullptr); @@ -621,6 +628,41 @@ void Emulator::WaitUntilExit() { on_exit(); } +void Emulator::AddGameConfigLoadCallback(GameConfigLoadCallback* callback) { + assert_not_null(callback); + // Game config load callbacks handling is entirely in the UI thread. + assert_true(!display_window_ || + display_window_->app_context().IsInUIThread()); + // Check if already added. + if (std::find(game_config_load_callbacks_.cbegin(), + game_config_load_callbacks_.cend(), + callback) != game_config_load_callbacks_.cend()) { + return; + } + game_config_load_callbacks_.push_back(callback); +} + +void Emulator::RemoveGameConfigLoadCallback(GameConfigLoadCallback* callback) { + assert_not_null(callback); + // Game config load callbacks handling is entirely in the UI thread. + assert_true(!display_window_ || + display_window_->app_context().IsInUIThread()); + auto it = std::find(game_config_load_callbacks_.cbegin(), + game_config_load_callbacks_.cend(), callback); + if (it == game_config_load_callbacks_.cend()) { + return; + } + if (game_config_load_callback_loop_next_index_ != SIZE_MAX) { + // Actualize the next callback index after the erasure from the vector. + size_t existing_index = + size_t(std::distance(game_config_load_callbacks_.cbegin(), it)); + if (game_config_load_callback_loop_next_index_ > existing_index) { + --game_config_load_callback_loop_next_index_; + } + } + game_config_load_callbacks_.erase(it); +} + std::string Emulator::FindLaunchModule() { std::string path("game:\\"); @@ -677,6 +719,10 @@ static std::string format_version(xex2_version version) { X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, const std::string_view module_path) { + // Making changes to the UI (setting the icon) and executing game config load + // callbacks which expect to be called from the UI thread. + assert_true(display_window_->app_context().IsInUIThread()); + // Setup NullDevices for raw HDD partition accesses // Cache/STFC code baked into games tries reading/writing to these // By using a NullDevice that just returns success to all IO requests it @@ -729,7 +775,19 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, // Try and load the resource database (xex only). if (module->title_id()) { auto title_id = fmt::format("{:08X}", module->title_id()); + + // Load the per-game configuration file and make sure updates are handled by + // the callbacks. config::LoadGameConfig(title_id); + assert_true(game_config_load_callback_loop_next_index_ == SIZE_MAX); + game_config_load_callback_loop_next_index_ = 0; + while (game_config_load_callback_loop_next_index_ < + game_config_load_callbacks_.size()) { + game_config_load_callbacks_[game_config_load_callback_loop_next_index_++] + ->PostGameConfigLoad(); + } + game_config_load_callback_loop_next_index_ = SIZE_MAX; + uint32_t resource_data = 0; uint32_t resource_size = 0; if (XSUCCEEDED(module->GetSection(title_id.c_str(), &resource_data, diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index cd86e1f81..96d81d00a 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -10,9 +10,12 @@ #ifndef XENIA_EMULATOR_H_ #define XENIA_EMULATOR_H_ +#include +#include #include #include #include +#include #include "xenia/base/delegate.h" #include "xenia/base/exception_handler.h" @@ -38,6 +41,7 @@ class InputDriver; class InputSystem; } // namespace hid namespace ui { +class ImGuiDrawer; class Window; } // namespace ui } // namespace xe @@ -50,6 +54,37 @@ constexpr fourcc_t kEmulatorSaveSignature = make_fourcc("XSAV"); // This is responsible for initializing and managing all the various subsystems. class Emulator { public: + // This is the class for the top-level callbacks. They may be called in an + // undefined order, so among them there must be no dependencies on each other, + // especially hierarchical ones. If hierarchical handling is needed, for + // instance, if a specific implementation of a subsystem needs to handle + // changes, but the entire implementation must be reloaded, the implementation + // in this example _must not_ register / unregister its own callback - rather, + // the proper ordering and hierarchy should be constructed in a single + // callback (in this example, for the whole subsystem). + // + // All callbacks must be created and destroyed in the UI thread only (or the + // thread that takes its place in the architecture of the specific app if + // there's no UI), as they are invoked in the UI thread. + class GameConfigLoadCallback { + public: + GameConfigLoadCallback(Emulator& emulator); + GameConfigLoadCallback(const GameConfigLoadCallback& callback) = delete; + GameConfigLoadCallback& operator=(const GameConfigLoadCallback& callback) = + delete; + virtual ~GameConfigLoadCallback(); + + // The callback is invoked in the UI thread (or the thread that takes its + // place in the architecture of the specific app if there's no UI). + virtual void PostGameConfigLoad() = 0; + + protected: + Emulator& emulator() const { return emulator_; } + + private: + Emulator& emulator_; + }; + explicit Emulator(const std::filesystem::path& command_line, const std::filesystem::path& storage_root, const std::filesystem::path& content_root, @@ -82,9 +117,13 @@ class Emulator { // Are we currently running a title? bool is_title_open() const { return title_id_.has_value(); } - // Window used for displaying graphical output. + // Window used for displaying graphical output. Can be null. ui::Window* display_window() const { return display_window_; } + // ImGui drawer for various kinds of dialogs requested by the guest. Can be + // null. + ui::ImGuiDrawer* imgui_drawer() const { return imgui_drawer_; } + // Guest memory system modelling the RAM (both virtual and physical) of the // system. Memory* memory() const { return memory_.get(); } @@ -121,7 +160,7 @@ class Emulator { // Once this function returns a game can be launched using one of the Launch // functions. X_STATUS Setup( - ui::Window* display_window, + ui::Window* display_window, ui::ImGuiDrawer* imgui_drawer, std::function(cpu::Processor*)> audio_system_factory, std::function()> @@ -170,6 +209,9 @@ class Emulator { static bool ExceptionCallbackThunk(Exception* ex, void* data); bool ExceptionCallback(Exception* ex); + void AddGameConfigLoadCallback(GameConfigLoadCallback* callback); + void RemoveGameConfigLoadCallback(GameConfigLoadCallback* callback); + std::string FindLaunchModule(); X_STATUS CompleteLaunch(const std::filesystem::path& path, @@ -183,7 +225,8 @@ class Emulator { std::string title_name_; std::string title_version_; - ui::Window* display_window_; + ui::Window* display_window_ = nullptr; + ui::ImGuiDrawer* imgui_drawer_ = nullptr; std::unique_ptr memory_; @@ -196,6 +239,16 @@ class Emulator { std::unique_ptr file_system_; std::unique_ptr kernel_state_; + + // Accessible only from the thread that invokes those callbacks (the UI thread + // if the UI is available). + std::vector game_config_load_callbacks_; + // Using an index, not an iterator, because after the erasure, the adjustment + // must be done for the vector element indices that would be in the iterator + // range that would be invalidated. + // SIZE_MAX if not currently in the game config load callback loop. + size_t game_config_load_callback_loop_next_index_ = SIZE_MAX; + kernel::object_ref main_thread_; std::optional title_id_; // Currently running title ID diff --git a/src/xenia/gpu/command_processor.cc b/src/xenia/gpu/command_processor.cc index 2e1a43c36..07ff7f070 100644 --- a/src/xenia/gpu/command_processor.cc +++ b/src/xenia/gpu/command_processor.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -45,15 +45,12 @@ CommandProcessor::CommandProcessor(GraphicsSystem* graphics_system, CommandProcessor::~CommandProcessor() = default; -bool CommandProcessor::Initialize( - std::unique_ptr context) { - context_ = std::move(context); - +bool CommandProcessor::Initialize() { // Initialize the gamma ramps to their default (linear) values - taken from // what games set when starting. for (uint32_t i = 0; i < 256; ++i) { uint32_t value = i * 1023 / 255; - gamma_ramp_.normal[i].value = value | (value << 10) | (value << 20); + gamma_ramp_.table[i].value = value | (value << 10) | (value << 20); } for (uint32_t i = 0; i < 128; ++i) { uint32_t value = (i * 65535 / 127) & ~63; @@ -64,7 +61,7 @@ bool CommandProcessor::Initialize( gamma_ramp_.pwl[i].values[j].value = value; } } - dirty_gamma_ramp_normal_ = true; + dirty_gamma_ramp_table_ = true; dirty_gamma_ramp_pwl_ = true; worker_running_ = true; @@ -140,8 +137,18 @@ void CommandProcessor::CallInThread(std::function fn) { void CommandProcessor::ClearCaches() {} +void CommandProcessor::SetDesiredSwapPostEffect( + SwapPostEffect swap_post_effect) { + if (swap_post_effect_desired_ == swap_post_effect) { + return; + } + swap_post_effect_desired_ = swap_post_effect; + CallInThread([this, swap_post_effect]() { + swap_post_effect_actual_ = swap_post_effect; + }); +} + void CommandProcessor::WorkerThreadMain() { - context_->MakeCurrent(); if (!SetupContext()) { xe::FatalError("Unable to setup command processor internal state"); return; @@ -212,9 +219,6 @@ void CommandProcessor::Pause() { threading::Thread::GetCurrentThread()->Suspend(); }); - // HACK - Prevents a hang in IssueSwap() - swap_state_.pending = false; - fence.Wait(); } @@ -255,7 +259,7 @@ bool CommandProcessor::Restore(ByteStream* stream) { bool CommandProcessor::SetupContext() { return true; } -void CommandProcessor::ShutdownContext() { context_.reset(); } +void CommandProcessor::ShutdownContext() {} void CommandProcessor::InitializeRingBuffer(uint32_t ptr, uint32_t size_log2) { read_ptr_index_ = 0; @@ -326,14 +330,17 @@ void CommandProcessor::UpdateGammaRampValue(GammaRampType type, if (mask_lo) { switch (type) { - case GammaRampType::kNormal: + case GammaRampType::kTable: assert_true(regs->values[XE_GPU_REG_DC_LUT_RW_MODE].u32 == 0); - gamma_ramp_.normal[index].value = value; - dirty_gamma_ramp_normal_ = true; + gamma_ramp_.table[index].value = value; + dirty_gamma_ramp_table_ = true; break; case GammaRampType::kPWL: assert_true(regs->values[XE_GPU_REG_DC_LUT_RW_MODE].u32 == 1); - gamma_ramp_.pwl[index].values[gamma_ramp_rw_subindex_].value = value; + // The lower 6 bits are hardwired to 0. + // https://developer.amd.com/wordpress/media/2012/10/RRG-216M56-03oOEM.pdf + gamma_ramp_.pwl[index].values[gamma_ramp_rw_subindex_].value = + value & ~(uint32_t(63) | (uint32_t(63) << 16)); gamma_ramp_rw_subindex_ = (gamma_ramp_rw_subindex_ + 1) % 3; dirty_gamma_ramp_pwl_ = true; break; @@ -385,51 +392,6 @@ void CommandProcessor::PrepareForWait() { trace_writer_.Flush(); } void CommandProcessor::ReturnFromWait() {} -void CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, - uint32_t frontbuffer_width, - uint32_t frontbuffer_height) { - SCOPE_profile_cpu_f("gpu"); - if (!swap_request_handler_) { - return; - } - - // If there was a swap pending we drop it on the floor. - // This prevents the display from pulling the backbuffer out from under us. - // If we skip a lot then we may need to buffer more, but as the display - // thread should be fairly idle that shouldn't happen. - if (!cvars::vsync) { - std::lock_guard lock(swap_state_.mutex); - if (swap_state_.pending) { - swap_state_.pending = false; - // TODO(benvanik): frame skip counter. - XELOGW("Skipped frame!"); - } - } else { - // Spin until no more pending swap. - while (worker_running_) { - { - std::lock_guard lock(swap_state_.mutex); - if (!swap_state_.pending) { - break; - } - } - xe::threading::MaybeYield(); - } - } - - PerformSwap(frontbuffer_ptr, frontbuffer_width, frontbuffer_height); - - { - // Set pending so that the display will swap the next time it can. - std::lock_guard lock(swap_state_.mutex); - swap_state_.pending = true; - } - - // Notify the display a swap is pending so that our changes are picked up. - // It does the actual front/back buffer swap. - swap_request_handler_(); -} - uint32_t CommandProcessor::ExecutePrimaryBuffer(uint32_t read_index, uint32_t write_index) { SCOPE_profile_cpu_f("gpu"); @@ -837,7 +799,7 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingBuffer* reader, uint32_t frontbuffer_height = reader->ReadAndSwap(); reader->AdvanceRead((count - 4) * sizeof(uint32_t)); - if (swap_mode_ == SwapMode::kNormal) { + if (!ignore_swap_) { IssueSwap(frontbuffer_ptr, frontbuffer_width, frontbuffer_height); } diff --git a/src/xenia/gpu/command_processor.h b/src/xenia/gpu/command_processor.h index d2ee29d8f..caa49e300 100644 --- a/src/xenia/gpu/command_processor.h +++ b/src/xenia/gpu/command_processor.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -26,7 +26,7 @@ #include "xenia/gpu/xenos.h" #include "xenia/kernel/xthread.h" #include "xenia/memory.h" -#include "xenia/ui/graphics_context.h" +#include "xenia/ui/presenter.h" namespace xe { @@ -60,12 +60,20 @@ enum class SwapMode { enum class GammaRampType { kUnknown = 0, - kNormal, + kTable, kPWL, }; struct GammaRamp { - struct NormalEntry { + // A lot of gamma ramp (DC_LUT) documentation: + // https://developer.amd.com/wordpress/media/2012/10/RRG-216M56-03oOEM.pdf + // The ramps entries are BGR, not RGB. + // For the 256-entry table (used by Direct3D 9 for a 8bpc front buffer), + // 535107D4 has in-game settings allowing separate configuration. + // The component order of the PWL table is untested, however, it's likely BGR + // too, since DC_LUTA/B registers have values for blue first, and for red + // last. + struct TableEntry { union { uint32_t value; struct { @@ -81,6 +89,15 @@ struct GammaRamp { union { uint32_t value; struct { + // The lower 6 bits are always zero (these are 10-bit in the upper bits + // thus, not fully 16-bit). + // See DC_LUTA/B_CONTROL for information about the way they should be + // interpreted (`output = base + (multiplier * delta) / 2^increment`, + // where the increment is the value specified in DC_LUTA/B_CONTROL for + // the specific color channel, the base is 7 bits of the front buffer + // value above `increment` bits, the multiplier is the lower `increment` + // bits of it; the increment is nonzero, otherwise the 256-entry table + // should be used instead). uint16_t base; uint16_t delta; }; @@ -91,19 +108,25 @@ struct GammaRamp { union { PWLValue values[3]; struct { - PWLValue r; - PWLValue g; PWLValue b; + PWLValue g; + PWLValue r; }; }; }; - NormalEntry normal[256]; + TableEntry table[256]; PWLEntry pwl[128]; }; class CommandProcessor { public: + enum class SwapPostEffect { + kNone, + kFxaa, + kFxaaExtreme, + }; + CommandProcessor(GraphicsSystem* graphics_system, kernel::KernelState* kernel_state); virtual ~CommandProcessor(); @@ -114,21 +137,26 @@ class CommandProcessor { Shader* active_vertex_shader() const { return active_vertex_shader_; } Shader* active_pixel_shader() const { return active_pixel_shader_; } - virtual bool Initialize(std::unique_ptr context); + virtual bool Initialize(); virtual void Shutdown(); void CallInThread(std::function fn); virtual void ClearCaches(); - SwapState& swap_state() { return swap_state_; } - void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; } - void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, - uint32_t frontbuffer_height); - - void set_swap_request_handler(std::function fn) { - swap_request_handler_ = fn; + void SetIgnoreSwap(bool ignore_swap) { ignore_swap_ = ignore_swap; } + // "Desired" is for the external thread managing the post-processing effect. + SwapPostEffect GetDesiredSwapPostEffect() const { + return swap_post_effect_desired_; } + void SetDesiredSwapPostEffect(SwapPostEffect swap_post_effect); + // Implementations must not make assumptions that the front buffer will + // necessarily be a resolve destination - it may be a texture generated by any + // means like written to by the CPU or loaded from a file (the disclaimer + // screen right in the beginning of 4D530AA4 is not a resolved render target, + // for instance). + virtual void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, + uint32_t frontbuffer_height) = 0; // May be called not only from the command processor thread when the command // processor is paused, and the termination of this function may be explicitly @@ -179,9 +207,6 @@ class CommandProcessor { virtual void PrepareForWait(); virtual void ReturnFromWait(); - virtual void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, - uint32_t frontbuffer_height) = 0; - uint32_t ExecutePrimaryBuffer(uint32_t start_index, uint32_t end_index); virtual void OnPrimaryBufferEnd() {} void ExecuteIndirectBuffer(uint32_t ptr, uint32_t length); @@ -254,6 +279,14 @@ class CommandProcessor { bool major_mode_explicit) = 0; virtual bool IssueCopy() = 0; + // "Actual" is for the command processor thread, to be read by the + // implementations. + SwapPostEffect GetActualSwapPostEffect() const { + return swap_post_effect_actual_; + } + + // TODO(Triang3l): Write the gamma ramp (including the display controller + // write pointers) in the common code. virtual void InitializeTrace() = 0; Memory* memory_ = nullptr; @@ -274,10 +307,8 @@ class CommandProcessor { std::atomic worker_running_; kernel::object_ref worker_thread_; - std::unique_ptr context_; - SwapMode swap_mode_ = SwapMode::kNormal; - SwapState swap_state_; - std::function swap_request_handler_; + bool ignore_swap_ = false; + std::queue> pending_fns_; // MicroEngine binary from PM4_ME_INIT @@ -305,8 +336,13 @@ class CommandProcessor { GammaRamp gamma_ramp_ = {}; int gamma_ramp_rw_subindex_ = 0; - bool dirty_gamma_ramp_normal_ = true; + bool dirty_gamma_ramp_table_ = true; bool dirty_gamma_ramp_pwl_ = true; + + // By default (such as for tools), post-processing is disabled. + // "Desired" is for the external thread managing the post-processing effect. + SwapPostEffect swap_post_effect_desired_ = SwapPostEffect::kNone; + SwapPostEffect swap_post_effect_actual_ = SwapPostEffect::kNone; }; } // namespace gpu diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.cc b/src/xenia/gpu/d3d12/d3d12_command_processor.cc index 6a3f78bc0..ccab20972 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.cc +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -23,6 +23,7 @@ #include "xenia/gpu/draw_util.h" #include "xenia/gpu/gpu_flags.h" #include "xenia/gpu/xenos.h" +#include "xenia/ui/d3d12/d3d12_presenter.h" #include "xenia/ui/d3d12/d3d12_util.h" DEFINE_bool(d3d12_bindless, true, @@ -51,6 +52,16 @@ namespace xe { namespace gpu { namespace d3d12 { +// Generated with `xb buildshaders`. +namespace shaders { +#include "xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_cs.h" +#include "xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_fxaa_luma_cs.h" +#include "xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_cs.h" +#include "xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_fxaa_luma_cs.h" +#include "xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_cs.h" +#include "xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_extreme_cs.h" +} // namespace shaders + D3D12CommandProcessor::D3D12CommandProcessor( D3D12GraphicsSystem* graphics_system, kernel::KernelState* kernel_state) : CommandProcessor(graphics_system, kernel_state), @@ -71,7 +82,7 @@ void D3D12CommandProcessor::InitializeShaderStorage( void D3D12CommandProcessor::RequestFrameTrace( const std::filesystem::path& root_path) { // Capture with PIX if attached. - if (GetD3D12Context().GetD3D12Provider().GetGraphicsAnalysis() != nullptr) { + if (GetD3D12Provider().GetGraphicsAnalysis() != nullptr) { pix_capture_requested_.store(true, std::memory_order_relaxed); return; } @@ -86,7 +97,9 @@ void D3D12CommandProcessor::TracePlaybackWroteMemory(uint32_t base_ptr, void D3D12CommandProcessor::RestoreEdramSnapshot(const void* snapshot) { // Starting a new frame because descriptors may be needed. - BeginSubmission(true); + if (!BeginSubmission(true)) { + return; + } render_target_cache_->RestoreEdramSnapshot(snapshot); } @@ -364,8 +377,8 @@ ID3D12RootSignature* D3D12CommandProcessor::GetRootSignature( ++desc.NumParameters; } - ID3D12RootSignature* root_signature = ui::d3d12::util::CreateRootSignature( - GetD3D12Context().GetD3D12Provider(), desc); + ID3D12RootSignature* root_signature = + ui::d3d12::util::CreateRootSignature(GetD3D12Provider(), desc); if (root_signature == nullptr) { XELOGE( "Failed to create a root signature with {} pixel textures, {} pixel " @@ -430,7 +443,7 @@ uint64_t D3D12CommandProcessor::RequestViewBindfulDescriptors( deferred_command_list_.SetDescriptorHeaps(view_bindful_heap_current_, sampler_bindful_heap_current_); } - auto& provider = GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); cpu_handle_out = provider.OffsetViewDescriptor( view_bindful_heap_pool_->GetLastRequestHeapCPUStart(), descriptor_index); gpu_handle_out = provider.OffsetViewDescriptor( @@ -464,7 +477,7 @@ bool D3D12CommandProcessor::RequestOneUseSingleViewDescriptors( return true; } assert_not_null(handles_out); - auto& provider = GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); if (bindless_resources_used_) { // Request separate bindless descriptors that will be freed when this // submission is completed by the GPU. @@ -511,7 +524,7 @@ ui::d3d12::util::DescriptorCpuGpuHandlePair D3D12CommandProcessor::GetSystemBindlessViewHandlePair( SystemBindlessView view) const { assert_true(bindless_resources_used_); - auto& provider = GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); return std::make_pair(provider.OffsetViewDescriptor( view_bindless_heap_cpu_start_, uint32_t(view)), provider.OffsetViewDescriptor( @@ -623,7 +636,7 @@ uint64_t D3D12CommandProcessor::RequestSamplerBindfulDescriptors( deferred_command_list_.SetDescriptorHeaps(view_bindful_heap_current_, sampler_bindful_heap_current_); } - auto& provider = GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); cpu_handle_out = provider.OffsetSamplerDescriptor( sampler_bindful_heap_pool_->GetLastRequestHeapCPUStart(), descriptor_index); @@ -650,8 +663,8 @@ ID3D12Resource* D3D12CommandProcessor::RequestScratchGPUBuffer( size = xe::align(size, kScratchBufferSizeIncrement); - auto& provider = GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); D3D12_RESOURCE_DESC buffer_desc; ui::d3d12::util::FillBufferResourceDesc( buffer_desc, size, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); @@ -664,8 +677,7 @@ ID3D12Resource* D3D12CommandProcessor::RequestScratchGPUBuffer( return nullptr; } if (scratch_buffer_ != nullptr) { - buffers_for_deletion_.push_back( - std::make_pair(scratch_buffer_, submission_current_)); + resources_for_deletion_.emplace_back(submission_current_, scratch_buffer_); } scratch_buffer_ = buffer; scratch_buffer_size_ = size; @@ -789,76 +801,15 @@ std::string D3D12CommandProcessor::GetWindowTitleText() const { return title.str(); } -std::unique_ptr D3D12CommandProcessor::Capture() { - ID3D12Resource* readback_buffer = - RequestReadbackBuffer(uint32_t(swap_texture_copy_size_)); - if (!readback_buffer) { - return nullptr; - } - BeginSubmission(false); - PushTransitionBarrier(swap_texture_, - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, - D3D12_RESOURCE_STATE_COPY_SOURCE); - SubmitBarriers(); - D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; - location_source.pResource = swap_texture_; - location_source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - location_source.SubresourceIndex = 0; - location_dest.pResource = readback_buffer; - location_dest.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - location_dest.PlacedFootprint = swap_texture_copy_footprint_; - deferred_command_list_.CopyTexture(location_dest, location_source); - PushTransitionBarrier(swap_texture_, D3D12_RESOURCE_STATE_COPY_SOURCE, - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - if (!AwaitAllQueueOperationsCompletion()) { - return nullptr; - } - D3D12_RANGE readback_range; - readback_range.Begin = swap_texture_copy_footprint_.Offset; - readback_range.End = swap_texture_copy_size_; - void* readback_mapping; - if (FAILED(readback_buffer->Map(0, &readback_range, &readback_mapping))) { - return nullptr; - } - std::unique_ptr raw_image(new xe::ui::RawImage()); - auto swap_texture_size = GetSwapTextureSize(); - raw_image->width = swap_texture_size.first; - raw_image->height = swap_texture_size.second; - raw_image->stride = swap_texture_size.first * 4; - raw_image->data.resize(raw_image->stride * swap_texture_size.second); - const uint8_t* readback_source_data = - reinterpret_cast(readback_mapping) + - swap_texture_copy_footprint_.Offset; - static_assert( - ui::d3d12::D3D12Context::kSwapChainFormat == DXGI_FORMAT_B8G8R8A8_UNORM, - "D3D12CommandProcessor::Capture assumes swap_texture_ to be in " - "DXGI_FORMAT_B8G8R8A8_UNORM because it swaps red and blue"); - for (uint32_t i = 0; i < swap_texture_size.second; ++i) { - uint8_t* pixel_dest = raw_image->data.data() + i * raw_image->stride; - const uint8_t* pixel_source = - readback_source_data + - i * swap_texture_copy_footprint_.Footprint.RowPitch; - for (uint32_t j = 0; j < swap_texture_size.first; ++j) { - pixel_dest[0] = pixel_source[2]; - pixel_dest[1] = pixel_source[1]; - pixel_dest[2] = pixel_source[0]; - pixel_dest[3] = pixel_source[3]; - pixel_dest += 4; - pixel_source += 4; - } - } - return raw_image; -} - bool D3D12CommandProcessor::SetupContext() { if (!CommandProcessor::SetupContext()) { XELOGE("Failed to initialize base command processor context"); return false; } - auto& provider = GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); - auto direct_queue = provider.GetDirectQueue(); + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); + ID3D12CommandQueue* direct_queue = provider.GetDirectQueue(); fence_completion_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (fence_completion_event_ == nullptr) { @@ -1222,120 +1173,255 @@ bool D3D12CommandProcessor::SetupContext() { D3D12_HEAP_FLAGS heap_flag_create_not_zeroed = provider.GetHeapFlagCreateNotZeroed(); - // Create gamma ramp resources. The PWL gamma ramp is 16-bit, but 6 bits are - // hardwired to zero, so DXGI_FORMAT_R10G10B10A2_UNORM can be used for it too. - // https://www.x.org/docs/AMD/old/42590_m76_rrg_1.01o.pdf - dirty_gamma_ramp_normal_ = true; + // Create gamma ramp resources. + dirty_gamma_ramp_table_ = true; dirty_gamma_ramp_pwl_ = true; - D3D12_RESOURCE_DESC gamma_ramp_desc; - gamma_ramp_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE1D; - gamma_ramp_desc.Alignment = 0; - gamma_ramp_desc.Width = 256; - gamma_ramp_desc.Height = 1; - gamma_ramp_desc.DepthOrArraySize = 1; - // Normal gamma is 256x1, PWL gamma is 128x1. - gamma_ramp_desc.MipLevels = 2; - gamma_ramp_desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; - gamma_ramp_desc.SampleDesc.Count = 1; - gamma_ramp_desc.SampleDesc.Quality = 0; - gamma_ramp_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - gamma_ramp_desc.Flags = D3D12_RESOURCE_FLAG_NONE; + D3D12_RESOURCE_DESC gamma_ramp_buffer_desc; + ui::d3d12::util::FillBufferResourceDesc( + gamma_ramp_buffer_desc, (256 + 128 * 3) * 4, D3D12_RESOURCE_FLAG_NONE); // The first action will be uploading. - gamma_ramp_texture_state_ = D3D12_RESOURCE_STATE_COPY_DEST; + gamma_ramp_buffer_state_ = D3D12_RESOURCE_STATE_COPY_DEST; if (FAILED(device->CreateCommittedResource( &ui::d3d12::util::kHeapPropertiesDefault, heap_flag_create_not_zeroed, - &gamma_ramp_desc, gamma_ramp_texture_state_, nullptr, - IID_PPV_ARGS(&gamma_ramp_texture_)))) { - XELOGE("Failed to create the gamma ramp texture"); + &gamma_ramp_buffer_desc, gamma_ramp_buffer_state_, nullptr, + IID_PPV_ARGS(&gamma_ramp_buffer_)))) { + XELOGE("Failed to create the gamma ramp buffer"); return false; } - // Get the layout for the upload buffer. - gamma_ramp_desc.DepthOrArraySize = kQueueFrames; - UINT64 gamma_ramp_upload_size; - device->GetCopyableFootprints(&gamma_ramp_desc, 0, kQueueFrames * 2, 0, - gamma_ramp_footprints_, nullptr, nullptr, - &gamma_ramp_upload_size); - // Create the upload buffer for the gamma ramp. - ui::d3d12::util::FillBufferResourceDesc( - gamma_ramp_desc, gamma_ramp_upload_size, D3D12_RESOURCE_FLAG_NONE); + // The upload buffer is frame-buffered. + gamma_ramp_buffer_desc.Width *= kQueueFrames; if (FAILED(device->CreateCommittedResource( &ui::d3d12::util::kHeapPropertiesUpload, heap_flag_create_not_zeroed, - &gamma_ramp_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, - IID_PPV_ARGS(&gamma_ramp_upload_)))) { + &gamma_ramp_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, + IID_PPV_ARGS(&gamma_ramp_upload_buffer_)))) { XELOGE("Failed to create the gamma ramp upload buffer"); return false; } - if (FAILED(gamma_ramp_upload_->Map( - 0, nullptr, reinterpret_cast(&gamma_ramp_upload_mapping_)))) { + if (FAILED(gamma_ramp_upload_buffer_->Map( + 0, nullptr, + reinterpret_cast(&gamma_ramp_upload_buffer_mapping_)))) { XELOGE("Failed to map the gamma ramp upload buffer"); - gamma_ramp_upload_mapping_ = nullptr; + gamma_ramp_upload_buffer_mapping_ = nullptr; return false; } - D3D12_RESOURCE_DESC swap_texture_desc; - swap_texture_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - swap_texture_desc.Alignment = 0; - auto swap_texture_size = GetSwapTextureSize(); - swap_texture_desc.Width = swap_texture_size.first; - swap_texture_desc.Height = swap_texture_size.second; - swap_texture_desc.DepthOrArraySize = 1; - swap_texture_desc.MipLevels = 1; - swap_texture_desc.Format = ui::d3d12::D3D12Context::kSwapChainFormat; - swap_texture_desc.SampleDesc.Count = 1; - swap_texture_desc.SampleDesc.Quality = 0; - swap_texture_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - swap_texture_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; - // Can be sampled at any time, switch to render target when needed, then back. - if (FAILED(device->CreateCommittedResource( - &ui::d3d12::util::kHeapPropertiesDefault, heap_flag_create_not_zeroed, - &swap_texture_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, - nullptr, IID_PPV_ARGS(&swap_texture_)))) { - XELOGE("Failed to create the command processor front buffer"); + // Initialize compute pipelines for output with gamma ramp. + D3D12_ROOT_PARAMETER + apply_gamma_root_parameters[UINT(ApplyGammaRootParameter::kCount)]; + { + D3D12_ROOT_PARAMETER& apply_gamma_root_parameter_constants = + apply_gamma_root_parameters[UINT(ApplyGammaRootParameter::kConstants)]; + apply_gamma_root_parameter_constants.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + apply_gamma_root_parameter_constants.Constants.ShaderRegister = 0; + apply_gamma_root_parameter_constants.Constants.RegisterSpace = 0; + apply_gamma_root_parameter_constants.Constants.Num32BitValues = + sizeof(ApplyGammaConstants) / sizeof(uint32_t); + apply_gamma_root_parameter_constants.ShaderVisibility = + D3D12_SHADER_VISIBILITY_ALL; + } + D3D12_DESCRIPTOR_RANGE apply_gamma_root_descriptor_range_dest; + apply_gamma_root_descriptor_range_dest.RangeType = + D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + apply_gamma_root_descriptor_range_dest.NumDescriptors = 1; + apply_gamma_root_descriptor_range_dest.BaseShaderRegister = 0; + apply_gamma_root_descriptor_range_dest.RegisterSpace = 0; + apply_gamma_root_descriptor_range_dest.OffsetInDescriptorsFromTableStart = 0; + { + D3D12_ROOT_PARAMETER& apply_gamma_root_parameter_dest = + apply_gamma_root_parameters[UINT( + ApplyGammaRootParameter::kDestination)]; + apply_gamma_root_parameter_dest.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + apply_gamma_root_parameter_dest.DescriptorTable.NumDescriptorRanges = 1; + apply_gamma_root_parameter_dest.DescriptorTable.pDescriptorRanges = + &apply_gamma_root_descriptor_range_dest; + apply_gamma_root_parameter_dest.ShaderVisibility = + D3D12_SHADER_VISIBILITY_ALL; + } + D3D12_DESCRIPTOR_RANGE apply_gamma_root_descriptor_range_source; + apply_gamma_root_descriptor_range_source.RangeType = + D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + apply_gamma_root_descriptor_range_source.NumDescriptors = 1; + apply_gamma_root_descriptor_range_source.BaseShaderRegister = 0; + apply_gamma_root_descriptor_range_source.RegisterSpace = 0; + apply_gamma_root_descriptor_range_source.OffsetInDescriptorsFromTableStart = + 0; + { + D3D12_ROOT_PARAMETER& apply_gamma_root_parameter_source = + apply_gamma_root_parameters[UINT(ApplyGammaRootParameter::kSource)]; + apply_gamma_root_parameter_source.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + apply_gamma_root_parameter_source.DescriptorTable.NumDescriptorRanges = 1; + apply_gamma_root_parameter_source.DescriptorTable.pDescriptorRanges = + &apply_gamma_root_descriptor_range_source; + apply_gamma_root_parameter_source.ShaderVisibility = + D3D12_SHADER_VISIBILITY_ALL; + } + D3D12_DESCRIPTOR_RANGE apply_gamma_root_descriptor_range_ramp; + apply_gamma_root_descriptor_range_ramp.RangeType = + D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + apply_gamma_root_descriptor_range_ramp.NumDescriptors = 1; + apply_gamma_root_descriptor_range_ramp.BaseShaderRegister = 1; + apply_gamma_root_descriptor_range_ramp.RegisterSpace = 0; + apply_gamma_root_descriptor_range_ramp.OffsetInDescriptorsFromTableStart = 0; + { + D3D12_ROOT_PARAMETER& apply_gamma_root_parameter_gamma_ramp = + apply_gamma_root_parameters[UINT(ApplyGammaRootParameter::kRamp)]; + apply_gamma_root_parameter_gamma_ramp.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + apply_gamma_root_parameter_gamma_ramp.DescriptorTable.NumDescriptorRanges = + 1; + apply_gamma_root_parameter_gamma_ramp.DescriptorTable.pDescriptorRanges = + &apply_gamma_root_descriptor_range_ramp; + apply_gamma_root_parameter_gamma_ramp.ShaderVisibility = + D3D12_SHADER_VISIBILITY_ALL; + } + D3D12_ROOT_SIGNATURE_DESC apply_gamma_root_signature_desc; + apply_gamma_root_signature_desc.NumParameters = + UINT(ApplyGammaRootParameter::kCount); + apply_gamma_root_signature_desc.pParameters = apply_gamma_root_parameters; + apply_gamma_root_signature_desc.NumStaticSamplers = 0; + apply_gamma_root_signature_desc.pStaticSamplers = nullptr; + apply_gamma_root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; + *(apply_gamma_root_signature_.ReleaseAndGetAddressOf()) = + ui::d3d12::util::CreateRootSignature(provider, + apply_gamma_root_signature_desc); + if (!apply_gamma_root_signature_) { + XELOGE("Failed to create the gamma ramp application root signature"); return false; } - device->GetCopyableFootprints(&swap_texture_desc, 0, 1, 0, - &swap_texture_copy_footprint_, nullptr, nullptr, - &swap_texture_copy_size_); - D3D12_DESCRIPTOR_HEAP_DESC swap_descriptor_heap_desc; - swap_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - swap_descriptor_heap_desc.NumDescriptors = 1; - swap_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - swap_descriptor_heap_desc.NodeMask = 0; - if (FAILED(device->CreateDescriptorHeap( - &swap_descriptor_heap_desc, - IID_PPV_ARGS(&swap_texture_rtv_descriptor_heap_)))) { - XELOGE("Failed to create the command processor front buffer RTV heap"); + *(apply_gamma_table_pipeline_.ReleaseAndGetAddressOf()) = + ui::d3d12::util::CreateComputePipeline( + device, shaders::apply_gamma_table_cs, + sizeof(shaders::apply_gamma_table_cs), + apply_gamma_root_signature_.Get()); + if (!apply_gamma_table_pipeline_) { + XELOGE( + "Failed to create the 256-entry table gamma ramp application compute " + "pipeline"); return false; } - swap_texture_rtv_ = - swap_texture_rtv_descriptor_heap_->GetCPUDescriptorHandleForHeapStart(); - D3D12_RENDER_TARGET_VIEW_DESC swap_rtv_desc; - swap_rtv_desc.Format = ui::d3d12::D3D12Context::kSwapChainFormat; - swap_rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - swap_rtv_desc.Texture2D.MipSlice = 0; - swap_rtv_desc.Texture2D.PlaneSlice = 0; - device->CreateRenderTargetView(swap_texture_, &swap_rtv_desc, - swap_texture_rtv_); - swap_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - swap_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - if (FAILED(device->CreateDescriptorHeap( - &swap_descriptor_heap_desc, - IID_PPV_ARGS(&swap_texture_srv_descriptor_heap_)))) { - XELOGE("Failed to create the command processor front buffer SRV heap"); + *(apply_gamma_table_fxaa_luma_pipeline_.ReleaseAndGetAddressOf()) = + ui::d3d12::util::CreateComputePipeline( + device, shaders::apply_gamma_table_fxaa_luma_cs, + sizeof(shaders::apply_gamma_table_fxaa_luma_cs), + apply_gamma_root_signature_.Get()); + if (!apply_gamma_table_fxaa_luma_pipeline_) { + XELOGE( + "Failed to create the 256-entry table gamma ramp application compute " + "pipeline with perceptual luma output"); + return false; + } + *(apply_gamma_pwl_pipeline_.ReleaseAndGetAddressOf()) = + ui::d3d12::util::CreateComputePipeline( + device, shaders::apply_gamma_pwl_cs, + sizeof(shaders::apply_gamma_pwl_cs), + apply_gamma_root_signature_.Get()); + if (!apply_gamma_pwl_pipeline_) { + XELOGE("Failed to create the PWL gamma ramp application compute pipeline"); + return false; + } + *(apply_gamma_pwl_fxaa_luma_pipeline_.ReleaseAndGetAddressOf()) = + ui::d3d12::util::CreateComputePipeline( + device, shaders::apply_gamma_pwl_fxaa_luma_cs, + sizeof(shaders::apply_gamma_pwl_fxaa_luma_cs), + apply_gamma_root_signature_.Get()); + if (!apply_gamma_pwl_fxaa_luma_pipeline_) { + XELOGE( + "Failed to create the PWL gamma ramp application compute pipeline with " + "perceptual luma output"); + return false; + } + + // Initialize compute pipelines for post-processing anti-aliasing. + D3D12_ROOT_PARAMETER fxaa_root_parameters[UINT(FxaaRootParameter::kCount)]; + { + D3D12_ROOT_PARAMETER& fxaa_root_parameter_constants = + fxaa_root_parameters[UINT(ApplyGammaRootParameter::kConstants)]; + fxaa_root_parameter_constants.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + fxaa_root_parameter_constants.Constants.ShaderRegister = 0; + fxaa_root_parameter_constants.Constants.RegisterSpace = 0; + fxaa_root_parameter_constants.Constants.Num32BitValues = + sizeof(FxaaConstants) / sizeof(uint32_t); + fxaa_root_parameter_constants.ShaderVisibility = + D3D12_SHADER_VISIBILITY_ALL; + } + D3D12_DESCRIPTOR_RANGE fxaa_root_descriptor_range_dest; + fxaa_root_descriptor_range_dest.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + fxaa_root_descriptor_range_dest.NumDescriptors = 1; + fxaa_root_descriptor_range_dest.BaseShaderRegister = 0; + fxaa_root_descriptor_range_dest.RegisterSpace = 0; + fxaa_root_descriptor_range_dest.OffsetInDescriptorsFromTableStart = 0; + { + D3D12_ROOT_PARAMETER& fxaa_root_parameter_dest = + fxaa_root_parameters[UINT(FxaaRootParameter::kDestination)]; + fxaa_root_parameter_dest.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + fxaa_root_parameter_dest.DescriptorTable.NumDescriptorRanges = 1; + fxaa_root_parameter_dest.DescriptorTable.pDescriptorRanges = + &fxaa_root_descriptor_range_dest; + fxaa_root_parameter_dest.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + } + D3D12_DESCRIPTOR_RANGE fxaa_root_descriptor_range_source; + fxaa_root_descriptor_range_source.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + fxaa_root_descriptor_range_source.NumDescriptors = 1; + fxaa_root_descriptor_range_source.BaseShaderRegister = 0; + fxaa_root_descriptor_range_source.RegisterSpace = 0; + fxaa_root_descriptor_range_source.OffsetInDescriptorsFromTableStart = 0; + { + D3D12_ROOT_PARAMETER& fxaa_root_parameter_source = + fxaa_root_parameters[UINT(FxaaRootParameter::kSource)]; + fxaa_root_parameter_source.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + fxaa_root_parameter_source.DescriptorTable.NumDescriptorRanges = 1; + fxaa_root_parameter_source.DescriptorTable.pDescriptorRanges = + &fxaa_root_descriptor_range_source; + fxaa_root_parameter_source.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + } + D3D12_STATIC_SAMPLER_DESC fxaa_root_sampler; + fxaa_root_sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + fxaa_root_sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + fxaa_root_sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + fxaa_root_sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + fxaa_root_sampler.MipLODBias = 0.0f; + fxaa_root_sampler.MaxAnisotropy = 1; + fxaa_root_sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + fxaa_root_sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; + fxaa_root_sampler.MinLOD = 0.0f; + fxaa_root_sampler.MaxLOD = 0.0f; + fxaa_root_sampler.ShaderRegister = 0; + fxaa_root_sampler.RegisterSpace = 0; + fxaa_root_sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + D3D12_ROOT_SIGNATURE_DESC fxaa_root_signature_desc; + fxaa_root_signature_desc.NumParameters = UINT(FxaaRootParameter::kCount); + fxaa_root_signature_desc.pParameters = fxaa_root_parameters; + fxaa_root_signature_desc.NumStaticSamplers = 1; + fxaa_root_signature_desc.pStaticSamplers = &fxaa_root_sampler; + fxaa_root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; + *(fxaa_root_signature_.ReleaseAndGetAddressOf()) = + ui::d3d12::util::CreateRootSignature(provider, fxaa_root_signature_desc); + if (!fxaa_root_signature_) { + XELOGE("Failed to create the FXAA root signature"); + return false; + } + *(fxaa_pipeline_.ReleaseAndGetAddressOf()) = + ui::d3d12::util::CreateComputePipeline(device, shaders::fxaa_cs, + sizeof(shaders::fxaa_cs), + fxaa_root_signature_.Get()); + if (!fxaa_pipeline_) { + XELOGE("Failed to create the FXAA compute pipeline"); + return false; + } + *(fxaa_extreme_pipeline_.ReleaseAndGetAddressOf()) = + ui::d3d12::util::CreateComputePipeline(device, shaders::fxaa_extreme_cs, + sizeof(shaders::fxaa_extreme_cs), + fxaa_root_signature_.Get()); + if (!fxaa_pipeline_) { + XELOGE("Failed to create the extreme-quality FXAA compute pipeline"); return false; } - D3D12_SHADER_RESOURCE_VIEW_DESC swap_srv_desc; - swap_srv_desc.Format = ui::d3d12::D3D12Context::kSwapChainFormat; - swap_srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - swap_srv_desc.Shader4ComponentMapping = - D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - swap_srv_desc.Texture2D.MostDetailedMip = 0; - swap_srv_desc.Texture2D.MipLevels = 1; - swap_srv_desc.Texture2D.PlaneSlice = 0; - swap_srv_desc.Texture2D.ResourceMinLODClamp = 0.0f; - device->CreateShaderResourceView( - swap_texture_, &swap_srv_desc, - swap_texture_srv_descriptor_heap_->GetCPUDescriptorHandleForHeapStart()); if (bindless_resources_used_) { // Create the system bindless descriptors once all resources are @@ -1471,11 +1557,11 @@ bool D3D12CommandProcessor::SetupContext() { view_bindless_heap_cpu_start_, uint32_t(SystemBindlessView::kEdramR32G32B32A32UintUAV)), 4); - // kGammaRampNormalSRV. + // kGammaRampTableSRV. WriteGammaRampSRV(false, provider.OffsetViewDescriptor( view_bindless_heap_cpu_start_, - uint32_t(SystemBindlessView::kGammaRampNormalSRV))); + uint32_t(SystemBindlessView::kGammaRampTableSRV))); // kGammaRampPWLSRV. WriteGammaRampSRV(true, provider.OffsetViewDescriptor( @@ -1501,35 +1587,29 @@ void D3D12CommandProcessor::ShutdownContext() { ui::d3d12::util::ReleaseAndNull(scratch_buffer_); scratch_buffer_size_ = 0; - for (auto& buffer_for_deletion : buffers_for_deletion_) { - buffer_for_deletion.first->Release(); + for (const std::pair& resource_for_deletion : + resources_for_deletion_) { + resource_for_deletion.second->Release(); } - buffers_for_deletion_.clear(); + resources_for_deletion_.clear(); - if (swap_texture_srv_descriptor_heap_ != nullptr) { - { - std::lock_guard lock(swap_state_.mutex); - swap_state_.pending = false; - swap_state_.front_buffer_texture = 0; - } - // TODO(Triang3l): Ensure this is synchronized. The display context may not - // exist at this point, so awaiting its fence doesn't always work. - swap_texture_srv_descriptor_heap_->Release(); - swap_texture_srv_descriptor_heap_ = nullptr; - } - ui::d3d12::util::ReleaseAndNull(swap_texture_rtv_descriptor_heap_); - ui::d3d12::util::ReleaseAndNull(swap_texture_); + fxaa_source_texture_submission_ = 0; + fxaa_source_texture_.Reset(); - // Don't need the data anymore, so zero range. - if (gamma_ramp_upload_mapping_ != nullptr) { - D3D12_RANGE gamma_ramp_written_range; - gamma_ramp_written_range.Begin = 0; - gamma_ramp_written_range.End = 0; - gamma_ramp_upload_->Unmap(0, &gamma_ramp_written_range); - gamma_ramp_upload_mapping_ = nullptr; - } - ui::d3d12::util::ReleaseAndNull(gamma_ramp_upload_); - ui::d3d12::util::ReleaseAndNull(gamma_ramp_texture_); + fxaa_extreme_pipeline_.Reset(); + fxaa_pipeline_.Reset(); + fxaa_root_signature_.Reset(); + + apply_gamma_pwl_fxaa_luma_pipeline_.Reset(); + apply_gamma_pwl_pipeline_.Reset(); + apply_gamma_table_fxaa_luma_pipeline_.Reset(); + apply_gamma_table_pipeline_.Reset(); + apply_gamma_root_signature_.Reset(); + + // Unmapping will be done implicitly by the destruction. + gamma_ramp_upload_buffer_mapping_ = nullptr; + gamma_ramp_upload_buffer_.Reset(); + gamma_ramp_buffer_.Reset(); pipeline_cache_.reset(); @@ -1596,6 +1676,8 @@ void D3D12CommandProcessor::ShutdownContext() { fence_completion_event_ = nullptr; } + device_removed_ = false; + CommandProcessor::ShutdownContext(); } @@ -1633,177 +1715,339 @@ void D3D12CommandProcessor::WriteRegister(uint32_t index, uint32_t value) { } else if (index == XE_GPU_REG_DC_LUT_PWL_DATA) { UpdateGammaRampValue(GammaRampType::kPWL, value); } else if (index == XE_GPU_REG_DC_LUT_30_COLOR) { - UpdateGammaRampValue(GammaRampType::kNormal, value); + UpdateGammaRampValue(GammaRampType::kTable, value); } else if (index == XE_GPU_REG_DC_LUT_RW_MODE) { gamma_ramp_rw_subindex_ = 0; } } -void D3D12CommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, - uint32_t frontbuffer_width, - uint32_t frontbuffer_height) { +void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, + uint32_t frontbuffer_width, + uint32_t frontbuffer_height) { // FIXME(Triang3l): frontbuffer_ptr is currently unreliable, in the trace // player it's set to 0, but it's not needed anyway since the fetch constant // contains the address. SCOPE_profile_cpu_f("gpu"); + ui::Presenter* presenter = graphics_system_->presenter(); + if (!presenter) { + return; + } + // In case the swap command is the only one in the frame. - BeginSubmission(true); - - auto device = GetD3D12Context().GetD3D12Provider().GetDevice(); - - // Upload the new gamma ramps, using the upload buffer for the current frame - // (will close the frame after this anyway, so can't write multiple times per - // frame). - uint32_t gamma_ramp_frame = uint32_t(frame_current_ % kQueueFrames); - if (dirty_gamma_ramp_normal_) { - const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& gamma_ramp_footprint = - gamma_ramp_footprints_[gamma_ramp_frame * 2]; - volatile uint32_t* mapping = reinterpret_cast( - gamma_ramp_upload_mapping_ + gamma_ramp_footprint.Offset); - for (uint32_t i = 0; i < 256; ++i) { - uint32_t value = gamma_ramp_.normal[i].value; - // Swap red and blue (535107D4 has settings allowing separate - // configuration). - mapping[i] = ((value & 1023) << 20) | (value & (1023 << 10)) | - ((value >> 20) & 1023); - } - PushTransitionBarrier(gamma_ramp_texture_, gamma_ramp_texture_state_, - D3D12_RESOURCE_STATE_COPY_DEST); - gamma_ramp_texture_state_ = D3D12_RESOURCE_STATE_COPY_DEST; - SubmitBarriers(); - D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; - location_source.pResource = gamma_ramp_upload_; - location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - location_source.PlacedFootprint = gamma_ramp_footprint; - location_dest.pResource = gamma_ramp_texture_; - location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - location_dest.SubresourceIndex = 0; - deferred_command_list_.CopyTexture(location_dest, location_source); - dirty_gamma_ramp_normal_ = false; - } - if (dirty_gamma_ramp_pwl_) { - const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& gamma_ramp_footprint = - gamma_ramp_footprints_[gamma_ramp_frame * 2 + 1]; - volatile uint32_t* mapping = reinterpret_cast( - gamma_ramp_upload_mapping_ + gamma_ramp_footprint.Offset); - for (uint32_t i = 0; i < 128; ++i) { - // TODO(Triang3l): Find a game to test if red and blue need to be swapped. - mapping[i] = (gamma_ramp_.pwl[i].values[0].base >> 6) | - (uint32_t(gamma_ramp_.pwl[i].values[1].base >> 6) << 10) | - (uint32_t(gamma_ramp_.pwl[i].values[2].base >> 6) << 20); - } - PushTransitionBarrier(gamma_ramp_texture_, gamma_ramp_texture_state_, - D3D12_RESOURCE_STATE_COPY_DEST); - gamma_ramp_texture_state_ = D3D12_RESOURCE_STATE_COPY_DEST; - SubmitBarriers(); - D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; - location_source.pResource = gamma_ramp_upload_; - location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - location_source.PlacedFootprint = gamma_ramp_footprint; - location_dest.pResource = gamma_ramp_texture_; - location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - location_dest.SubresourceIndex = 1; - deferred_command_list_.CopyTexture(location_dest, location_source); - dirty_gamma_ramp_pwl_ = false; + if (!BeginSubmission(true)) { + return; } + // Obtain the actual front buffer size to pass to RefreshGuestOutput, + // resolution-scaled if it's a resolve destination, or not otherwise. D3D12_SHADER_RESOURCE_VIEW_DESC swap_texture_srv_desc; xenos::TextureFormat frontbuffer_format; ID3D12Resource* swap_texture_resource = texture_cache_->RequestSwapTexture( swap_texture_srv_desc, frontbuffer_format); - if (swap_texture_resource) { - // This is according to D3D::InitializePresentationParameters from a game - // executable, which initializes the normal gamma ramp for 8_8_8_8 output - // and the PWL gamma ramp for 2_10_10_10. - bool use_pwl_gamma_ramp = - frontbuffer_format == xenos::TextureFormat::k_2_10_10_10 || - frontbuffer_format == xenos::TextureFormat::k_2_10_10_10_AS_16_16_16_16; - - bool descriptors_obtained; - ui::d3d12::util::DescriptorCpuGpuHandlePair descriptor_swap_texture; - ui::d3d12::util::DescriptorCpuGpuHandlePair descriptor_gamma_ramp; - if (bindless_resources_used_) { - descriptors_obtained = - RequestOneUseSingleViewDescriptors(1, &descriptor_swap_texture); - descriptor_gamma_ramp = GetSystemBindlessViewHandlePair( - use_pwl_gamma_ramp ? SystemBindlessView::kGammaRampPWLSRV - : SystemBindlessView::kGammaRampNormalSRV); - } else { - ui::d3d12::util::DescriptorCpuGpuHandlePair descriptors[2]; - descriptors_obtained = RequestOneUseSingleViewDescriptors(2, descriptors); - if (descriptors_obtained) { - descriptor_swap_texture = descriptors[0]; - descriptor_gamma_ramp = descriptors[1]; - WriteGammaRampSRV(use_pwl_gamma_ramp, descriptor_gamma_ramp.first); - } - } - if (descriptors_obtained) { - // Must not call anything that can change the descriptor heap from now on! - - // Create the swap texture descriptor. - device->CreateShaderResourceView(swap_texture_resource, - &swap_texture_srv_desc, - descriptor_swap_texture.first); - - // The swap texture is kept as an SRV because the graphics system may draw - // with it at any time. It's switched to RTV and back when needed. - PushTransitionBarrier(swap_texture_, - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, - D3D12_RESOURCE_STATE_RENDER_TARGET); - PushTransitionBarrier(gamma_ramp_texture_, gamma_ramp_texture_state_, - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - gamma_ramp_texture_state_ = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - SubmitBarriers(); - - auto swap_texture_size = GetSwapTextureSize(); - - // Draw the stretching rectangle. - render_target_cache_->InvalidateCommandListRenderTargets(); - deferred_command_list_.D3DOMSetRenderTargets(1, &swap_texture_rtv_, TRUE, - nullptr); - D3D12_VIEWPORT viewport; - viewport.TopLeftX = 0.0f; - viewport.TopLeftY = 0.0f; - viewport.Width = float(swap_texture_size.first); - viewport.Height = float(swap_texture_size.second); - viewport.MinDepth = 0.0f; - viewport.MaxDepth = 0.0f; - SetViewport(viewport); - D3D12_RECT scissor; - scissor.left = 0; - scissor.top = 0; - scissor.right = swap_texture_size.first; - scissor.bottom = swap_texture_size.second; - SetScissorRect(scissor); - D3D12GraphicsSystem* graphics_system = - static_cast(graphics_system_); - current_cached_pipeline_ = nullptr; - current_external_pipeline_ = nullptr; - current_graphics_root_signature_ = nullptr; - primitive_topology_ = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; - graphics_system->StretchTextureToFrontBuffer( - descriptor_swap_texture.second, &descriptor_gamma_ramp.second, - use_pwl_gamma_ramp ? (1.0f / 128.0f) : (1.0f / 256.0f), - deferred_command_list_); - // Ending the current frame anyway, so no need to reset the current render - // targets when using ROV. - - PushTransitionBarrier(swap_texture_, D3D12_RESOURCE_STATE_RENDER_TARGET, - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - // Don't care about graphics state because the frame is ending anyway. - auto swap_screen_size = GetSwapScreenSize(); - { - std::lock_guard lock(swap_state_.mutex); - swap_state_.width = swap_screen_size.first; - swap_state_.height = swap_screen_size.second; - swap_state_.front_buffer_texture = - reinterpret_cast(swap_texture_srv_descriptor_heap_); - } - } + if (!swap_texture_resource) { + return; } + D3D12_RESOURCE_DESC swap_texture_desc = swap_texture_resource->GetDesc(); + uint32_t resolution_scale_max = + std::max(texture_cache_->GetDrawResolutionScaleX(), + texture_cache_->GetDrawResolutionScaleY()); + presenter->RefreshGuestOutput( + uint32_t(swap_texture_desc.Width), uint32_t(swap_texture_desc.Height), + 1280 * resolution_scale_max, 720 * resolution_scale_max, + [this, &swap_texture_srv_desc, frontbuffer_format, swap_texture_resource, + &swap_texture_desc]( + ui::Presenter::GuestOutputRefreshContext& context) -> bool { + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); + + SwapPostEffect swap_post_effect = GetActualSwapPostEffect(); + bool use_fxaa = swap_post_effect == SwapPostEffect::kFxaa || + swap_post_effect == SwapPostEffect::kFxaaExtreme; + if (use_fxaa) { + // Make sure the texture of the correct size is available for FXAA. + if (fxaa_source_texture_) { + D3D12_RESOURCE_DESC fxaa_source_texture_desc = + fxaa_source_texture_->GetDesc(); + if (fxaa_source_texture_desc.Width != swap_texture_desc.Width || + fxaa_source_texture_desc.Height != swap_texture_desc.Height) { + if (submission_completed_ < fxaa_source_texture_submission_) { + fxaa_source_texture_->AddRef(); + resources_for_deletion_.emplace_back( + fxaa_source_texture_submission_, + fxaa_source_texture_.Get()); + } + fxaa_source_texture_.Reset(); + fxaa_source_texture_submission_ = 0; + } + } + if (!fxaa_source_texture_) { + D3D12_RESOURCE_DESC fxaa_source_texture_desc; + fxaa_source_texture_desc.Dimension = + D3D12_RESOURCE_DIMENSION_TEXTURE2D; + fxaa_source_texture_desc.Alignment = 0; + fxaa_source_texture_desc.Width = swap_texture_desc.Width; + fxaa_source_texture_desc.Height = swap_texture_desc.Height; + fxaa_source_texture_desc.DepthOrArraySize = 1; + fxaa_source_texture_desc.MipLevels = 1; + fxaa_source_texture_desc.Format = kFxaaSourceTextureFormat; + fxaa_source_texture_desc.SampleDesc.Count = 1; + fxaa_source_texture_desc.SampleDesc.Quality = 0; + fxaa_source_texture_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + fxaa_source_texture_desc.Flags = + D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + if (FAILED(device->CreateCommittedResource( + &ui::d3d12::util::kHeapPropertiesDefault, + provider.GetHeapFlagCreateNotZeroed(), + &fxaa_source_texture_desc, + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, nullptr, + IID_PPV_ARGS(&fxaa_source_texture_)))) { + XELOGE("Failed to create the FXAA input texture"); + swap_post_effect = SwapPostEffect::kNone; + use_fxaa = false; + } + } + } + + // This is according to D3D::InitializePresentationParameters from a + // game executable, which initializes the 256-entry table gamma ramp for + // 8_8_8_8 output and the PWL gamma ramp for 2_10_10_10. + bool use_pwl_gamma_ramp = + frontbuffer_format == xenos::TextureFormat::k_2_10_10_10 || + frontbuffer_format == + xenos::TextureFormat::k_2_10_10_10_AS_16_16_16_16; + + context.SetIs8bpc(!use_pwl_gamma_ramp && !use_fxaa); + + // Upload the new gamma ramp, using the upload buffer for the current + // frame (will close the frame after this anyway, so can't write + // multiple times per frame). + if (use_pwl_gamma_ramp ? dirty_gamma_ramp_pwl_ + : dirty_gamma_ramp_table_) { + uint32_t gamma_ramp_offset_bytes = use_pwl_gamma_ramp ? 256 * 4 : 0; + uint32_t gamma_ramp_upload_offset_bytes = + uint32_t(frame_current_ % kQueueFrames) * ((256 + 128 * 3) * 4) + + gamma_ramp_offset_bytes; + uint32_t gamma_ramp_size_bytes = + (use_pwl_gamma_ramp ? 128 * 3 : 256) * 4; + std::memcpy(gamma_ramp_upload_buffer_mapping_ + + gamma_ramp_upload_offset_bytes, + use_pwl_gamma_ramp + ? static_cast(gamma_ramp_.pwl) + : static_cast(gamma_ramp_.table), + gamma_ramp_size_bytes); + PushTransitionBarrier(gamma_ramp_buffer_.Get(), + gamma_ramp_buffer_state_, + D3D12_RESOURCE_STATE_COPY_DEST); + gamma_ramp_buffer_state_ = D3D12_RESOURCE_STATE_COPY_DEST; + SubmitBarriers(); + deferred_command_list_.D3DCopyBufferRegion( + gamma_ramp_buffer_.Get(), gamma_ramp_offset_bytes, + gamma_ramp_upload_buffer_.Get(), gamma_ramp_upload_offset_bytes, + gamma_ramp_size_bytes); + (use_pwl_gamma_ramp ? dirty_gamma_ramp_pwl_ + : dirty_gamma_ramp_table_) = false; + } + + // Destination, source, and if bindful, gamma ramp. + ui::d3d12::util::DescriptorCpuGpuHandlePair apply_gamma_descriptors[3]; + ui::d3d12::util::DescriptorCpuGpuHandlePair + apply_gamma_descriptor_gamma_ramp; + if (!RequestOneUseSingleViewDescriptors( + bindless_resources_used_ ? 2 : 3, apply_gamma_descriptors)) { + return false; + } + // Must not call anything that can change the descriptor heap from now + // on! + if (bindless_resources_used_) { + apply_gamma_descriptor_gamma_ramp = GetSystemBindlessViewHandlePair( + use_pwl_gamma_ramp ? SystemBindlessView::kGammaRampPWLSRV + : SystemBindlessView::kGammaRampTableSRV); + } else { + apply_gamma_descriptor_gamma_ramp = apply_gamma_descriptors[2]; + WriteGammaRampSRV(use_pwl_gamma_ramp, + apply_gamma_descriptor_gamma_ramp.first); + } + + ID3D12Resource* guest_output_resource = + static_cast< + ui::d3d12::D3D12Presenter::D3D12GuestOutputRefreshContext&>( + context) + .resource_uav_capable(); + + if (use_fxaa) { + fxaa_source_texture_submission_ = submission_current_; + } + + ID3D12Resource* apply_gamma_dest = + use_fxaa ? fxaa_source_texture_.Get() : guest_output_resource; + D3D12_RESOURCE_STATES apply_gamma_dest_initial_state = + use_fxaa ? D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE + : ui::d3d12::D3D12Presenter::kGuestOutputInternalState; + static_cast( + context) + .resource_uav_capable(); + PushTransitionBarrier(apply_gamma_dest, apply_gamma_dest_initial_state, + D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + // From now on, even in case of failure, apply_gamma_dest must be + // transitioned back to apply_gamma_dest_initial_state! + D3D12_UNORDERED_ACCESS_VIEW_DESC apply_gamma_dest_uav_desc; + apply_gamma_dest_uav_desc.Format = + use_fxaa ? kFxaaSourceTextureFormat + : ui::d3d12::D3D12Presenter::kGuestOutputFormat; + apply_gamma_dest_uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + apply_gamma_dest_uav_desc.Texture2D.MipSlice = 0; + apply_gamma_dest_uav_desc.Texture2D.PlaneSlice = 0; + device->CreateUnorderedAccessView(apply_gamma_dest, nullptr, + &apply_gamma_dest_uav_desc, + apply_gamma_descriptors[0].first); + + device->CreateShaderResourceView(swap_texture_resource, + &swap_texture_srv_desc, + apply_gamma_descriptors[1].first); + + PushTransitionBarrier(gamma_ramp_buffer_.Get(), + gamma_ramp_buffer_state_, + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + gamma_ramp_buffer_state_ = + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + + deferred_command_list_.D3DSetComputeRootSignature( + apply_gamma_root_signature_.Get()); + ApplyGammaConstants apply_gamma_constants; + apply_gamma_constants.size[0] = uint32_t(swap_texture_desc.Width); + apply_gamma_constants.size[1] = uint32_t(swap_texture_desc.Height); + deferred_command_list_.D3DSetComputeRoot32BitConstants( + UINT(ApplyGammaRootParameter::kConstants), + sizeof(apply_gamma_constants) / sizeof(uint32_t), + &apply_gamma_constants, 0); + deferred_command_list_.D3DSetComputeRootDescriptorTable( + UINT(ApplyGammaRootParameter::kDestination), + apply_gamma_descriptors[0].second); + deferred_command_list_.D3DSetComputeRootDescriptorTable( + UINT(ApplyGammaRootParameter::kSource), + apply_gamma_descriptors[1].second); + deferred_command_list_.D3DSetComputeRootDescriptorTable( + UINT(ApplyGammaRootParameter::kRamp), + apply_gamma_descriptor_gamma_ramp.second); + ID3D12PipelineState* apply_gamma_pipeline; + if (use_pwl_gamma_ramp) { + apply_gamma_pipeline = use_fxaa + ? apply_gamma_pwl_fxaa_luma_pipeline_.Get() + : apply_gamma_pwl_pipeline_.Get(); + } else { + apply_gamma_pipeline = + use_fxaa ? apply_gamma_table_fxaa_luma_pipeline_.Get() + : apply_gamma_table_pipeline_.Get(); + } + SetExternalPipeline(apply_gamma_pipeline); + SubmitBarriers(); + uint32_t group_count_x = (uint32_t(swap_texture_desc.Width) + 7) / 8; + uint32_t group_count_y = (uint32_t(swap_texture_desc.Height) + 7) / 8; + deferred_command_list_.D3DDispatch(group_count_x, group_count_y, 1); + + // Apply FXAA. + if (use_fxaa) { + // Destination and source. + ui::d3d12::util::DescriptorCpuGpuHandlePair fxaa_descriptors[2]; + if (!RequestOneUseSingleViewDescriptors( + uint32_t(xe::countof(fxaa_descriptors)), fxaa_descriptors)) { + // Failed to obtain descriptors for FXAA - just copy after gamma + // ramp application without applying FXAA. + PushTransitionBarrier(apply_gamma_dest, + D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + D3D12_RESOURCE_STATE_COPY_SOURCE); + PushTransitionBarrier( + guest_output_resource, + ui::d3d12::D3D12Presenter::kGuestOutputInternalState, + D3D12_RESOURCE_STATE_COPY_DEST); + SubmitBarriers(); + deferred_command_list_.D3DCopyResource(guest_output_resource, + apply_gamma_dest); + PushTransitionBarrier(apply_gamma_dest, + D3D12_RESOURCE_STATE_COPY_SOURCE, + apply_gamma_dest_initial_state); + PushTransitionBarrier( + guest_output_resource, D3D12_RESOURCE_STATE_COPY_DEST, + ui::d3d12::D3D12Presenter::kGuestOutputInternalState); + return false; + } else { + assert_true(apply_gamma_dest_initial_state == + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + PushTransitionBarrier(apply_gamma_dest, + D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + apply_gamma_dest_initial_state); + PushTransitionBarrier( + guest_output_resource, + ui::d3d12::D3D12Presenter::kGuestOutputInternalState, + D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + // From now on, even in case of failure, guest_output_resource must + // be transitioned back to kGuestOutputInternalState! + deferred_command_list_.D3DSetComputeRootSignature( + fxaa_root_signature_.Get()); + FxaaConstants fxaa_constants; + fxaa_constants.size[0] = uint32_t(swap_texture_desc.Width); + fxaa_constants.size[1] = uint32_t(swap_texture_desc.Height); + fxaa_constants.size_inv[0] = 1.0f / float(fxaa_constants.size[0]); + fxaa_constants.size_inv[1] = 1.0f / float(fxaa_constants.size[1]); + deferred_command_list_.D3DSetComputeRoot32BitConstants( + UINT(FxaaRootParameter::kConstants), + sizeof(fxaa_constants) / sizeof(uint32_t), &fxaa_constants, 0); + D3D12_UNORDERED_ACCESS_VIEW_DESC fxaa_dest_uav_desc; + fxaa_dest_uav_desc.Format = + ui::d3d12::D3D12Presenter::kGuestOutputFormat; + fxaa_dest_uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + fxaa_dest_uav_desc.Texture2D.MipSlice = 0; + fxaa_dest_uav_desc.Texture2D.PlaneSlice = 0; + device->CreateUnorderedAccessView(guest_output_resource, nullptr, + &fxaa_dest_uav_desc, + fxaa_descriptors[0].first); + deferred_command_list_.D3DSetComputeRootDescriptorTable( + UINT(FxaaRootParameter::kDestination), + fxaa_descriptors[0].second); + D3D12_SHADER_RESOURCE_VIEW_DESC fxaa_source_srv_desc; + fxaa_source_srv_desc.Format = kFxaaSourceTextureFormat; + fxaa_source_srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + fxaa_source_srv_desc.Shader4ComponentMapping = + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + fxaa_source_srv_desc.Texture2D.MostDetailedMip = 0; + fxaa_source_srv_desc.Texture2D.MipLevels = 1; + fxaa_source_srv_desc.Texture2D.PlaneSlice = 0; + fxaa_source_srv_desc.Texture2D.ResourceMinLODClamp = 0.0f; + device->CreateShaderResourceView(fxaa_source_texture_.Get(), + &fxaa_source_srv_desc, + fxaa_descriptors[1].first); + deferred_command_list_.D3DSetComputeRootDescriptorTable( + UINT(FxaaRootParameter::kSource), fxaa_descriptors[1].second); + SetExternalPipeline(swap_post_effect == SwapPostEffect::kFxaaExtreme + ? fxaa_extreme_pipeline_.Get() + : fxaa_pipeline_.Get()); + SubmitBarriers(); + deferred_command_list_.D3DDispatch(group_count_x, group_count_y, 1); + PushTransitionBarrier( + guest_output_resource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + ui::d3d12::D3D12Presenter::kGuestOutputInternalState); + } + } else { + assert_true(apply_gamma_dest_initial_state == + ui::d3d12::D3D12Presenter::kGuestOutputInternalState); + PushTransitionBarrier(apply_gamma_dest, + D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + apply_gamma_dest_initial_state); + } + + // Need to submit all the commands before giving the image back to the + // presenter so it can submit its own commands for displaying it to the + // queue. + SubmitBarriers(); + EndSubmission(true); + return true; + }); + + // End the frame even if did not present for any reason (the image refresher + // was not called), to prevent leaking per-frame resources. EndSubmission(true); } @@ -1829,7 +2073,7 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, SCOPE_profile_cpu_f("gpu"); #endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES - ID3D12Device* device = GetD3D12Context().GetD3D12Provider().GetDevice(); + ID3D12Device* device = GetD3D12Provider().GetDevice(); const RegisterFile& regs = *register_file_; xenos::ModeControl edram_mode = regs.Get().edram_mode; @@ -1884,7 +2128,9 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, pixel_shader && pixel_shader->is_valid_memexport_used(); bool memexport_used = memexport_used_vertex || memexport_used_pixel; - BeginSubmission(true); + if (!BeginSubmission(true)) { + return false; + } // Process primitives. PrimitiveProcessor::ProcessingResult primitive_processing_result; @@ -2349,7 +2595,9 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, } void D3D12CommandProcessor::InitializeTrace() { - BeginSubmission(false); + if (!BeginSubmission(false)) { + return; + } bool render_target_cache_submitted = render_target_cache_->InitializeTraceSubmitDownloads(); bool shared_memory_submitted = @@ -2370,7 +2618,9 @@ bool D3D12CommandProcessor::IssueCopy() { #if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES SCOPE_profile_cpu_f("gpu"); #endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES - BeginSubmission(true); + if (!BeginSubmission(true)) { + return false; + } uint32_t written_address, written_length; if (!render_target_cache_->Resolve(*memory_, *shared_memory_, *texture_cache_, written_address, written_length)) { @@ -2416,8 +2666,7 @@ void D3D12CommandProcessor::CheckSubmissionFence(uint64_t await_submission) { // outside of a submission, await explicitly. if (queue_operations_done_since_submission_signal_) { UINT64 fence_value = ++queue_operations_since_submission_fence_last_; - ID3D12CommandQueue* direct_queue = - GetD3D12Context().GetD3D12Provider().GetDirectQueue(); + ID3D12CommandQueue* direct_queue = GetD3D12Provider().GetDirectQueue(); if (SUCCEEDED( direct_queue->Signal(queue_operations_since_submission_fence_, fence_value) && @@ -2486,13 +2735,13 @@ void D3D12CommandProcessor::CheckSubmissionFence(uint64_t await_submission) { view_bindless_one_use_descriptors_.pop_front(); } - // Delete transient buffers marked for deletion. - while (!buffers_for_deletion_.empty()) { - if (buffers_for_deletion_.front().second > submission_completed_) { + // Delete transient resources marked for deletion. + while (!resources_for_deletion_.empty()) { + if (resources_for_deletion_.front().first > submission_completed_) { break; } - buffers_for_deletion_.front().first->Release(); - buffers_for_deletion_.pop_front(); + resources_for_deletion_.front().second->Release(); + resources_for_deletion_.pop_front(); } shared_memory_->CompletedSubmissionUpdated(); @@ -2502,14 +2751,28 @@ void D3D12CommandProcessor::CheckSubmissionFence(uint64_t await_submission) { render_target_cache_->CompletedSubmissionUpdated(); } -void D3D12CommandProcessor::BeginSubmission(bool is_guest_command) { +bool D3D12CommandProcessor::BeginSubmission(bool is_guest_command) { #if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES SCOPE_profile_cpu_f("gpu"); #endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES + if (device_removed_) { + return false; + } + bool is_opening_frame = is_guest_command && !frame_open_; if (submission_open_ && !is_opening_frame) { - return; + return true; + } + + // Check if the device is still available. + ID3D12Device* device = GetD3D12Provider().GetDevice(); + HRESULT device_removed_reason = device->GetDeviceRemovedReason(); + if (FAILED(device_removed_reason)) { + device_removed_ = true; + graphics_system_->OnHostGpuLossFromAnyThread(device_removed_reason != + DXGI_ERROR_DEVICE_REMOVED); + return false; } // Check the fence - needed for all kinds of submissions (to reclaim transient @@ -2610,7 +2873,7 @@ void D3D12CommandProcessor::BeginSubmission(bool is_guest_command) { pix_capture_requested_.exchange(false, std::memory_order_relaxed); if (pix_capturing_) { IDXGraphicsAnalysis* graphics_analysis = - GetD3D12Context().GetD3D12Provider().GetGraphicsAnalysis(); + GetD3D12Provider().GetGraphicsAnalysis(); if (graphics_analysis != nullptr) { graphics_analysis->BeginCapture(); } @@ -2620,10 +2883,12 @@ void D3D12CommandProcessor::BeginSubmission(bool is_guest_command) { texture_cache_->BeginFrame(); } + + return true; } bool D3D12CommandProcessor::EndSubmission(bool is_swap) { - auto& provider = GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); // Make sure there is a command allocator to write commands to. if (submission_open_ && !command_allocator_writable_first_) { @@ -2660,7 +2925,7 @@ bool D3D12CommandProcessor::EndSubmission(bool is_swap) { // destroyed between frames. SubmitBarriers(); - auto direct_queue = provider.GetDirectQueue(); + ID3D12CommandQueue* direct_queue = provider.GetDirectQueue(); // Submit the deferred command list. // Only one deferred command list must be executed in the same @@ -3347,9 +3612,9 @@ void D3D12CommandProcessor::UpdateSystemConstantValues( bool D3D12CommandProcessor::UpdateBindings( const D3D12Shader* vertex_shader, const D3D12Shader* pixel_shader, ID3D12RootSignature* root_signature) { - auto& provider = GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); - auto& regs = *register_file_; + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); + const RegisterFile& regs = *register_file_; #if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES SCOPE_profile_cpu_f("gpu"); @@ -4178,8 +4443,8 @@ ID3D12Resource* D3D12CommandProcessor::RequestReadbackBuffer(uint32_t size) { } size = xe::align(size, kReadbackBufferSizeIncrement); if (size > readback_buffer_size_) { - auto& provider = GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); D3D12_RESOURCE_DESC buffer_desc; ui::d3d12::util::FillBufferResourceDesc(buffer_desc, size, D3D12_RESOURCE_FLAG_NONE); @@ -4201,16 +4466,23 @@ ID3D12Resource* D3D12CommandProcessor::RequestReadbackBuffer(uint32_t size) { void D3D12CommandProcessor::WriteGammaRampSRV( bool is_pwl, D3D12_CPU_DESCRIPTOR_HANDLE handle) const { - auto device = GetD3D12Context().GetD3D12Provider().GetDevice(); + ID3D12Device* device = GetD3D12Provider().GetDevice(); D3D12_SHADER_RESOURCE_VIEW_DESC desc; desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; - desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; + desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - // 256-entry for normal, 128-entry for PWL. - desc.Texture1D.MostDetailedMip = is_pwl ? 1 : 0; - desc.Texture1D.MipLevels = 1; - desc.Texture1D.ResourceMinLODClamp = 0.0f; - device->CreateShaderResourceView(gamma_ramp_texture_, &desc, handle); + desc.Buffer.StructureByteStride = 0; + desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + if (is_pwl) { + desc.Format = DXGI_FORMAT_R16G16_UINT; + desc.Buffer.FirstElement = 256 * 4 / 4; + desc.Buffer.NumElements = 128 * 3; + } else { + desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; + desc.Buffer.FirstElement = 0; + desc.Buffer.NumElements = 256; + } + device->CreateShaderResourceView(gamma_ramp_buffer_.Get(), &desc, handle); } } // namespace d3d12 diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.h b/src/xenia/gpu/d3d12/d3d12_command_processor.h index 3476a9ad8..abdd1fbda 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.h +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -32,8 +32,8 @@ #include "xenia/gpu/dxbc_shader_translator.h" #include "xenia/gpu/xenos.h" #include "xenia/kernel/kernel_state.h" -#include "xenia/ui/d3d12/d3d12_context.h" #include "xenia/ui/d3d12/d3d12_descriptor_heap_pool.h" +#include "xenia/ui/d3d12/d3d12_provider.h" #include "xenia/ui/d3d12/d3d12_upload_buffer_pool.h" #include "xenia/ui/d3d12/d3d12_util.h" @@ -58,8 +58,9 @@ class D3D12CommandProcessor : public CommandProcessor { void RestoreEdramSnapshot(const void* snapshot) override; - ui::d3d12::D3D12Context& GetD3D12Context() const { - return static_cast(*context_); + ui::d3d12::D3D12Provider& GetD3D12Provider() const { + return *static_cast( + graphics_system_->provider()); } // Returns the deferred drawing command list for the currently open @@ -153,7 +154,7 @@ class D3D12CommandProcessor : public CommandProcessor { kEdramR32G32UintUAV, kEdramR32G32B32A32UintUAV, - kGammaRampNormalSRV, + kGammaRampTableSRV, kGammaRampPWLSRV, // Beyond this point, SRVs are accessible to shaders through an unbounded @@ -210,16 +211,14 @@ class D3D12CommandProcessor : public CommandProcessor { // Returns the text to display in the GPU backend name in the window title. std::string GetWindowTitleText() const; - std::unique_ptr Capture(); - protected: bool SetupContext() override; void ShutdownContext() override; void WriteRegister(uint32_t index, uint32_t value) override; - void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, - uint32_t frontbuffer_height) override; + void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, + uint32_t frontbuffer_height) override; void OnPrimaryBufferEnd() override; @@ -321,8 +320,9 @@ class D3D12CommandProcessor : public CommandProcessor { void CheckSubmissionFence(uint64_t await_submission); // If is_guest_command is true, a new full frame - with full cleanup of // resources and, if needed, starting capturing - is opened if pending (as - // opposed to simply resuming after mid-frame synchronization). - void BeginSubmission(bool is_guest_command); + // opposed to simply resuming after mid-frame synchronization). Returns + // whether a submission is open currently and the device is not removed. + bool BeginSubmission(bool is_guest_command); // If is_swap is true, a full frame is closed - with, if needed, cache // clearing and stopping capturing. Returns whether the submission was done // successfully, if it has failed, leaves it open. @@ -380,6 +380,8 @@ class D3D12CommandProcessor : public CommandProcessor { void WriteGammaRampSRV(bool is_pwl, D3D12_CPU_DESCRIPTOR_HANDLE handle) const; + bool device_removed_ = false; + bool cache_clear_requested_ = false; HANDLE fence_completion_event_ = nullptr; @@ -497,42 +499,73 @@ class D3D12CommandProcessor : public CommandProcessor { std::unique_ptr texture_cache_; - // Mip 0 contains the normal gamma ramp (256 entries), mip 1 contains the PWL - // ramp (128 entries). DXGI_FORMAT_R10G10B10A2_UNORM 1D. - ID3D12Resource* gamma_ramp_texture_ = nullptr; - D3D12_RESOURCE_STATES gamma_ramp_texture_state_; + // Bytes 0x0...0x3FF - 256-entry R10G10B10X2 gamma ramp (red and blue must be + // read as swapped - 535107D4 has settings allowing separate configuration). + // Bytes 0x400...0x9FF - 128-entry PWL R16G16 gamma ramp (R - base, G - delta, + // low 6 bits of each are zero, 3 elements per entry). + // https://www.x.org/docs/AMD/old/42590_m76_rrg_1.01o.pdf + Microsoft::WRL::ComPtr gamma_ramp_buffer_; + D3D12_RESOURCE_STATES gamma_ramp_buffer_state_; // Upload buffer for an image that is the same as gamma_ramp_, but with // kQueueFrames array layers. - ID3D12Resource* gamma_ramp_upload_ = nullptr; - uint8_t* gamma_ramp_upload_mapping_ = nullptr; - D3D12_PLACED_SUBRESOURCE_FOOTPRINT gamma_ramp_footprints_[kQueueFrames * 2]; + Microsoft::WRL::ComPtr gamma_ramp_upload_buffer_; + uint8_t* gamma_ramp_upload_buffer_mapping_ = nullptr; - static constexpr uint32_t kSwapTextureWidth = 1280; - static constexpr uint32_t kSwapTextureHeight = 720; - std::pair GetSwapTextureSize() const { - return std::make_pair( - kSwapTextureWidth * texture_cache_->GetDrawResolutionScaleX(), - kSwapTextureHeight * texture_cache_->GetDrawResolutionScaleY()); - } - std::pair GetSwapScreenSize() const { - uint32_t resolution_scale = - std::max(texture_cache_->GetDrawResolutionScaleX(), - texture_cache_->GetDrawResolutionScaleY()); - return std::make_pair(kSwapTextureWidth * resolution_scale, - kSwapTextureHeight * resolution_scale); - } - ID3D12Resource* swap_texture_ = nullptr; - D3D12_PLACED_SUBRESOURCE_FOOTPRINT swap_texture_copy_footprint_; - UINT64 swap_texture_copy_size_; - ID3D12DescriptorHeap* swap_texture_rtv_descriptor_heap_ = nullptr; - D3D12_CPU_DESCRIPTOR_HANDLE swap_texture_rtv_; - ID3D12DescriptorHeap* swap_texture_srv_descriptor_heap_ = nullptr; + struct ApplyGammaConstants { + uint32_t size[2]; + }; + enum class ApplyGammaRootParameter : UINT { + kConstants, + kDestination, + kSource, + kRamp, + + kCount, + }; + Microsoft::WRL::ComPtr apply_gamma_root_signature_; + Microsoft::WRL::ComPtr apply_gamma_table_pipeline_; + Microsoft::WRL::ComPtr + apply_gamma_table_fxaa_luma_pipeline_; + Microsoft::WRL::ComPtr apply_gamma_pwl_pipeline_; + Microsoft::WRL::ComPtr + apply_gamma_pwl_fxaa_luma_pipeline_; + + struct FxaaConstants { + uint32_t size[2]; + float size_inv[2]; + }; + enum class FxaaRootParameter : UINT { + kConstants, + kDestination, + kSource, + + kCount, + }; + Microsoft::WRL::ComPtr fxaa_root_signature_; + Microsoft::WRL::ComPtr fxaa_pipeline_; + Microsoft::WRL::ComPtr fxaa_extreme_pipeline_; + + // PWL gamma ramp can result in values with more precision than 10bpc. Though + // those sub-10bpc bits don't have any noticeable visual effect, so normally + // R10G10B10A2_UNORM is enough. But what's the most important is that for the + // original FXAA shader, the luma needs to be written to the alpha channel. + // For simplicity (to avoid modifying the FXAA shader and adding more texture + // fetches into it), and for the highest quality (preserving all 13 bits that + // may be generated by applying the PWL gamma ramp with an increment of 2^3, + // and also leaving some space for the result of applying fractional weights + // to calculate the luma), using R16G16B16A16_UNORM instead of + // R10G10B10X2_UNORM with a separate alpha texture. + static constexpr DXGI_FORMAT kFxaaSourceTextureFormat = + DXGI_FORMAT_R16G16B16A16_UNORM; + // Kept in NON_PIXEL_SHADER_RESOURCE state. + Microsoft::WRL::ComPtr fxaa_source_texture_; + uint64_t fxaa_source_texture_submission_ = 0; // Unsubmitted barrier batch. std::vector barriers_; // , sorted by the submission number. - std::deque> buffers_for_deletion_; + std::deque> resources_for_deletion_; static constexpr uint32_t kScratchBufferSizeIncrement = 16 * 1024 * 1024; ID3D12Resource* scratch_buffer_ = nullptr; diff --git a/src/xenia/gpu/d3d12/d3d12_graphics_system.cc b/src/xenia/gpu/d3d12/d3d12_graphics_system.cc index 411e95ce5..b4e0d025d 100644 --- a/src/xenia/gpu/d3d12/d3d12_graphics_system.cc +++ b/src/xenia/gpu/d3d12/d3d12_graphics_system.cc @@ -22,13 +22,6 @@ namespace xe { namespace gpu { namespace d3d12 { -// Generated with `xb buildshaders`. -namespace shaders { -#include "xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_tc_vs.h" -#include "xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_gamma_ps.h" -#include "xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_ps.h" -} // namespace shaders - D3D12GraphicsSystem::D3D12GraphicsSystem() {} D3D12GraphicsSystem::~D3D12GraphicsSystem() {} @@ -48,198 +41,11 @@ std::string D3D12GraphicsSystem::name() const { X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, - ui::Window* target_window) { + ui::WindowedAppContext* app_context, + bool is_surface_required) { provider_ = xe::ui::d3d12::D3D12Provider::Create(); - auto d3d12_provider = static_cast(provider()); - auto device = d3d12_provider->GetDevice(); - - auto result = GraphicsSystem::Setup(processor, kernel_state, target_window); - if (result != X_STATUS_SUCCESS) { - return result; - } - - if (target_window) { - display_context_ = reinterpret_cast( - target_window->context()); - } - - // Create the stretch pipeline root signature, with 1 parameter (source - // texture) for raw stretch and 3 parameters (source texture, gamma ramp LUT, - // inverse of the size of the gamma ramp LUT) for gamma-correcting stretch. - // Raw. - D3D12_ROOT_PARAMETER stretch_root_parameters[3]; - stretch_root_parameters[0].ParameterType = - D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - stretch_root_parameters[0].DescriptorTable.NumDescriptorRanges = 1; - D3D12_DESCRIPTOR_RANGE stretch_root_texture_range; - stretch_root_texture_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - stretch_root_texture_range.NumDescriptors = 1; - stretch_root_texture_range.BaseShaderRegister = 0; - stretch_root_texture_range.RegisterSpace = 0; - stretch_root_texture_range.OffsetInDescriptorsFromTableStart = 0; - stretch_root_parameters[0].DescriptorTable.pDescriptorRanges = - &stretch_root_texture_range; - stretch_root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - D3D12_STATIC_SAMPLER_DESC stretch_sampler_desc; - stretch_sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - stretch_sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - stretch_sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - stretch_sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - stretch_sampler_desc.MipLODBias = 0.0f; - stretch_sampler_desc.MaxAnisotropy = 1; - stretch_sampler_desc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; - stretch_sampler_desc.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - stretch_sampler_desc.MinLOD = 0.0f; - stretch_sampler_desc.MaxLOD = 0.0f; - stretch_sampler_desc.ShaderRegister = 0; - stretch_sampler_desc.RegisterSpace = 0; - stretch_sampler_desc.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - D3D12_ROOT_SIGNATURE_DESC stretch_root_desc; - stretch_root_desc.NumParameters = 1; - stretch_root_desc.pParameters = stretch_root_parameters; - stretch_root_desc.NumStaticSamplers = 1; - stretch_root_desc.pStaticSamplers = &stretch_sampler_desc; - stretch_root_desc.Flags = - D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS; - stretch_root_signature_ = - ui::d3d12::util::CreateRootSignature(*d3d12_provider, stretch_root_desc); - if (stretch_root_signature_ == nullptr) { - XELOGE("Failed to create the front buffer stretch root signature"); - return X_STATUS_UNSUCCESSFUL; - } - // Gamma. - stretch_root_parameters[1].ParameterType = - D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - stretch_root_parameters[1].DescriptorTable.NumDescriptorRanges = 1; - D3D12_DESCRIPTOR_RANGE stretch_root_gamma_ramp_range; - stretch_root_gamma_ramp_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - stretch_root_gamma_ramp_range.NumDescriptors = 1; - stretch_root_gamma_ramp_range.BaseShaderRegister = 1; - stretch_root_gamma_ramp_range.RegisterSpace = 0; - stretch_root_gamma_ramp_range.OffsetInDescriptorsFromTableStart = 0; - stretch_root_parameters[1].DescriptorTable.pDescriptorRanges = - &stretch_root_gamma_ramp_range; - stretch_root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - stretch_root_parameters[2].ParameterType = - D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; - stretch_root_parameters[2].Constants.ShaderRegister = 0; - stretch_root_parameters[2].Constants.RegisterSpace = 0; - stretch_root_parameters[2].Constants.Num32BitValues = 1; - stretch_root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - stretch_root_desc.NumParameters = 3; - stretch_root_desc.pParameters = stretch_root_parameters; - stretch_gamma_root_signature_ = - ui::d3d12::util::CreateRootSignature(*d3d12_provider, stretch_root_desc); - if (stretch_gamma_root_signature_ == nullptr) { - XELOGE( - "Failed to create the gamma-correcting front buffer stretch root " - "signature"); - stretch_root_signature_->Release(); - stretch_root_signature_ = nullptr; - return X_STATUS_UNSUCCESSFUL; - } - - // Create the stretch pipelines. - D3D12_GRAPHICS_PIPELINE_STATE_DESC stretch_pipeline_desc = {}; - stretch_pipeline_desc.pRootSignature = stretch_root_signature_; - stretch_pipeline_desc.VS.pShaderBytecode = shaders::fullscreen_tc_vs; - stretch_pipeline_desc.VS.BytecodeLength = sizeof(shaders::fullscreen_tc_vs); - stretch_pipeline_desc.PS.pShaderBytecode = shaders::stretch_ps; - stretch_pipeline_desc.PS.BytecodeLength = sizeof(shaders::stretch_ps); - // The shader will set alpha to 1, don't use output-merger to preserve it. - stretch_pipeline_desc.BlendState.RenderTarget[0].RenderTargetWriteMask = - D3D12_COLOR_WRITE_ENABLE_ALL; - stretch_pipeline_desc.SampleMask = UINT_MAX; - stretch_pipeline_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; - stretch_pipeline_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; - stretch_pipeline_desc.RasterizerState.DepthClipEnable = TRUE; - stretch_pipeline_desc.PrimitiveTopologyType = - D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - stretch_pipeline_desc.NumRenderTargets = 1; - stretch_pipeline_desc.RTVFormats[0] = - ui::d3d12::D3D12Context::kSwapChainFormat; - stretch_pipeline_desc.SampleDesc.Count = 1; - if (FAILED(device->CreateGraphicsPipelineState( - &stretch_pipeline_desc, IID_PPV_ARGS(&stretch_pipeline_)))) { - XELOGE("Failed to create the front buffer stretch pipeline"); - stretch_gamma_root_signature_->Release(); - stretch_gamma_root_signature_ = nullptr; - stretch_root_signature_->Release(); - stretch_root_signature_ = nullptr; - return X_STATUS_UNSUCCESSFUL; - } - stretch_pipeline_desc.pRootSignature = stretch_gamma_root_signature_; - stretch_pipeline_desc.PS.pShaderBytecode = shaders::stretch_gamma_ps; - stretch_pipeline_desc.PS.BytecodeLength = sizeof(shaders::stretch_gamma_ps); - if (FAILED(device->CreateGraphicsPipelineState( - &stretch_pipeline_desc, IID_PPV_ARGS(&stretch_gamma_pipeline_)))) { - XELOGE( - "Failed to create the gamma-correcting front buffer stretch pipeline"); - stretch_pipeline_->Release(); - stretch_pipeline_ = nullptr; - stretch_gamma_root_signature_->Release(); - stretch_gamma_root_signature_ = nullptr; - stretch_root_signature_->Release(); - stretch_root_signature_ = nullptr; - return X_STATUS_UNSUCCESSFUL; - } - - return X_STATUS_SUCCESS; -} - -void D3D12GraphicsSystem::Shutdown() { - ui::d3d12::util::ReleaseAndNull(stretch_gamma_pipeline_); - ui::d3d12::util::ReleaseAndNull(stretch_pipeline_); - ui::d3d12::util::ReleaseAndNull(stretch_gamma_root_signature_); - ui::d3d12::util::ReleaseAndNull(stretch_root_signature_); - - GraphicsSystem::Shutdown(); -} - -std::unique_ptr D3D12GraphicsSystem::Capture() { - auto d3d12_command_processor = - static_cast(command_processor()); - if (!d3d12_command_processor) { - return nullptr; - } - return d3d12_command_processor->Capture(); -} - -void D3D12GraphicsSystem::StretchTextureToFrontBuffer( - D3D12_GPU_DESCRIPTOR_HANDLE handle, - D3D12_GPU_DESCRIPTOR_HANDLE* gamma_ramp_handle, float gamma_ramp_inv_size, - ID3D12GraphicsCommandList* command_list) { - if (gamma_ramp_handle != nullptr) { - command_list->SetPipelineState(stretch_gamma_pipeline_); - command_list->SetGraphicsRootSignature(stretch_gamma_root_signature_); - command_list->SetGraphicsRootDescriptorTable(1, *gamma_ramp_handle); - command_list->SetGraphicsRoot32BitConstants(2, 1, &gamma_ramp_inv_size, 0); - } else { - command_list->SetPipelineState(stretch_pipeline_); - command_list->SetGraphicsRootSignature(stretch_root_signature_); - } - command_list->SetGraphicsRootDescriptorTable(0, handle); - command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - command_list->DrawInstanced(3, 1, 0, 0); -} - -void D3D12GraphicsSystem::StretchTextureToFrontBuffer( - D3D12_GPU_DESCRIPTOR_HANDLE handle, - D3D12_GPU_DESCRIPTOR_HANDLE* gamma_ramp_handle, float gamma_ramp_inv_size, - DeferredCommandList& command_list) { - if (gamma_ramp_handle != nullptr) { - command_list.D3DSetPipelineState(stretch_gamma_pipeline_); - command_list.D3DSetGraphicsRootSignature(stretch_gamma_root_signature_); - command_list.D3DSetGraphicsRootDescriptorTable(1, *gamma_ramp_handle); - command_list.D3DSetGraphicsRoot32BitConstants(2, 1, &gamma_ramp_inv_size, - 0); - } else { - command_list.D3DSetPipelineState(stretch_pipeline_); - command_list.D3DSetGraphicsRootSignature(stretch_root_signature_); - } - command_list.D3DSetGraphicsRootDescriptorTable(0, handle); - command_list.D3DIASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - command_list.D3DDrawInstanced(3, 1, 0, 0); + return GraphicsSystem::Setup(processor, kernel_state, app_context, + is_surface_required); } std::unique_ptr @@ -248,69 +54,6 @@ D3D12GraphicsSystem::CreateCommandProcessor() { new D3D12CommandProcessor(this, kernel_state_)); } -void D3D12GraphicsSystem::Swap(xe::ui::UIEvent* e) { - if (display_context_->WasLost()) { - // We're crashing. Cheese it. - return; - } - - if (!command_processor_) { - return; - } - - auto& swap_state = command_processor_->swap_state(); - ID3D12DescriptorHeap* swap_srv_heap; - { - std::lock_guard lock(swap_state.mutex); - swap_state.pending = false; - swap_srv_heap = reinterpret_cast( - swap_state.front_buffer_texture); - } - if (swap_srv_heap == nullptr) { - // Not ready yet. - return; - } - - uint32_t window_width, window_height; - display_context_->GetSwapChainSize(window_width, window_height); - - int32_t target_x, target_y; - uint32_t target_width, target_height; - draw_util::GetPresentArea(swap_state.width, swap_state.height, window_width, - window_height, target_x, target_y, target_width, - target_height); - // For safety. - target_x = clamp(target_x, int32_t(D3D12_VIEWPORT_BOUNDS_MIN), - int32_t(D3D12_VIEWPORT_BOUNDS_MAX)); - target_y = clamp(target_y, int32_t(D3D12_VIEWPORT_BOUNDS_MIN), - int32_t(D3D12_VIEWPORT_BOUNDS_MAX)); - target_width = std::min( - target_width, uint32_t(int32_t(D3D12_VIEWPORT_BOUNDS_MAX) - target_x)); - target_height = std::min( - target_height, uint32_t(int32_t(D3D12_VIEWPORT_BOUNDS_MAX) - target_y)); - - auto command_list = display_context_->GetSwapCommandList(); - // Assuming the window has already been cleared to the needed letterbox color. - D3D12_VIEWPORT viewport; - viewport.TopLeftX = float(target_x); - viewport.TopLeftY = float(target_y); - viewport.Width = float(target_width); - viewport.Height = float(target_height); - viewport.MinDepth = 0.0f; - viewport.MaxDepth = 0.0f; - command_list->RSSetViewports(1, &viewport); - D3D12_RECT scissor; - scissor.left = 0; - scissor.top = 0; - scissor.right = window_width; - scissor.bottom = window_height; - command_list->RSSetScissorRects(1, &scissor); - command_list->SetDescriptorHeaps(1, &swap_srv_heap); - StretchTextureToFrontBuffer( - swap_srv_heap->GetGPUDescriptorHandleForHeapStart(), nullptr, 0.0f, - command_list); -} - } // namespace d3d12 } // namespace gpu } // namespace xe diff --git a/src/xenia/gpu/d3d12/d3d12_graphics_system.h b/src/xenia/gpu/d3d12/d3d12_graphics_system.h index 00faeb34f..249cc3ffa 100644 --- a/src/xenia/gpu/d3d12/d3d12_graphics_system.h +++ b/src/xenia/gpu/d3d12/d3d12_graphics_system.h @@ -15,7 +15,6 @@ #include "xenia/gpu/command_processor.h" #include "xenia/gpu/d3d12/deferred_command_list.h" #include "xenia/gpu/graphics_system.h" -#include "xenia/ui/d3d12/d3d12_context.h" namespace xe { namespace gpu { @@ -31,37 +30,11 @@ class D3D12GraphicsSystem : public GraphicsSystem { std::string name() const override; X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, - ui::Window* target_window) override; - void Shutdown() override; - - std::unique_ptr Capture() override; - - // Draws a texture covering the entire viewport to the render target currently - // bound on the specified command list (in D3D12Context::kSwapChainFormat). - // This changes the current pipeline, graphics root signature and primitive - // topology. The gamma ramp texture must be 1D if present at all, for linear - // space, pass nullptr as the gamma ramp. - void StretchTextureToFrontBuffer( - D3D12_GPU_DESCRIPTOR_HANDLE handle, - D3D12_GPU_DESCRIPTOR_HANDLE* gamma_ramp_handle, float gamma_ramp_inv_size, - ID3D12GraphicsCommandList* command_list); - void StretchTextureToFrontBuffer( - D3D12_GPU_DESCRIPTOR_HANDLE handle, - D3D12_GPU_DESCRIPTOR_HANDLE* gamma_ramp_handle, float gamma_ramp_inv_size, - DeferredCommandList& command_list); + ui::WindowedAppContext* app_context, + bool is_surface_required) override; protected: std::unique_ptr CreateCommandProcessor() override; - - void Swap(xe::ui::UIEvent* e) override; - - private: - ui::d3d12::D3D12Context* display_context_ = nullptr; - - ID3D12RootSignature* stretch_root_signature_ = nullptr; - ID3D12RootSignature* stretch_gamma_root_signature_ = nullptr; - ID3D12PipelineState* stretch_pipeline_ = nullptr; - ID3D12PipelineState* stretch_gamma_pipeline_ = nullptr; }; } // namespace d3d12 diff --git a/src/xenia/gpu/d3d12/d3d12_primitive_processor.cc b/src/xenia/gpu/d3d12/d3d12_primitive_processor.cc index ec1e576b0..a806546a1 100644 --- a/src/xenia/gpu/d3d12/d3d12_primitive_processor.cc +++ b/src/xenia/gpu/d3d12/d3d12_primitive_processor.cc @@ -33,7 +33,7 @@ bool D3D12PrimitiveProcessor::Initialize() { return false; } frame_index_buffer_pool_ = std::make_unique( - command_processor_.GetD3D12Context().GetD3D12Provider(), + command_processor_.GetD3D12Provider(), std::max(size_t(kMinRequiredConvertedIndexBufferSize), ui::GraphicsUploadBufferPool::kDefaultPageSize)); return true; @@ -90,7 +90,7 @@ bool D3D12PrimitiveProcessor::InitializeBuiltin16BitIndexBuffer( assert_null(builtin_index_buffer_upload_); const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); D3D12_RESOURCE_DESC resource_desc; diff --git a/src/xenia/gpu/d3d12/d3d12_render_target_cache.cc b/src/xenia/gpu/d3d12/d3d12_render_target_cache.cc index d72e37428..61b8bb83f 100644 --- a/src/xenia/gpu/d3d12/d3d12_render_target_cache.cc +++ b/src/xenia/gpu/d3d12/d3d12_render_target_cache.cc @@ -215,7 +215,7 @@ D3D12RenderTargetCache::~D3D12RenderTargetCache() { Shutdown(true); } bool D3D12RenderTargetCache::Initialize() { const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); if (cvars::render_target_path_d3d12 == "rtv") { @@ -1298,7 +1298,7 @@ bool D3D12RenderTargetCache::Update(bool is_rasterization_done, void D3D12RenderTargetCache::WriteEdramRawSRVDescriptor( D3D12_CPU_DESCRIPTOR_HANDLE handle) { const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); device->CopyDescriptorsSimple( 1, handle, @@ -1311,7 +1311,7 @@ void D3D12RenderTargetCache::WriteEdramRawSRVDescriptor( void D3D12RenderTargetCache::WriteEdramRawUAVDescriptor( D3D12_CPU_DESCRIPTOR_HANDLE handle) { const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); device->CopyDescriptorsSimple( 1, handle, @@ -1339,7 +1339,7 @@ void D3D12RenderTargetCache::WriteEdramUintPow2SRVDescriptor( return; } const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); device->CopyDescriptorsSimple( 1, handle, @@ -1366,7 +1366,7 @@ void D3D12RenderTargetCache::WriteEdramUintPow2UAVDescriptor( return; } const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); device->CopyDescriptorsSimple( 1, handle, @@ -1668,8 +1668,9 @@ bool D3D12RenderTargetCache::InitializeTraceSubmitDownloads() { ui::d3d12::util::FillBufferResourceDesc(edram_snapshot_download_buffer_desc, xenos::kEdramSizeBytes, D3D12_RESOURCE_FLAG_NONE); - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); if (FAILED(device->CreateCommittedResource( &ui::d3d12::util::kHeapPropertiesReadback, provider.GetHeapFlagCreateNotZeroed(), @@ -1721,7 +1722,8 @@ void D3D12RenderTargetCache::RestoreEdramSnapshot(const void* snapshot) { // Create the buffer - will be used for copying to either a 32-bit 1280x2048 // render target or the EDRAM buffer. - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); if (!edram_snapshot_restore_pool_) { edram_snapshot_restore_pool_ = std::make_unique( @@ -1966,8 +1968,7 @@ DXGI_FORMAT D3D12RenderTargetCache::GetDepthSRVStencilDXGIFormat( RenderTargetCache::RenderTarget* D3D12RenderTargetCache::CreateRenderTarget( RenderTargetKey key) { - ID3D12Device* device = - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(); + ID3D12Device* device = command_processor_.GetD3D12Provider().GetDevice(); D3D12_RESOURCE_DESC resource_desc; resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; @@ -4345,8 +4346,7 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) { // *************************************************************************** ID3D12PipelineState* const* pipelines; - ID3D12Device* device = - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(); + ID3D12Device* device = command_processor_.GetD3D12Provider().GetDevice(); D3D12_INPUT_ELEMENT_DESC pipeline_input_element_desc; pipeline_input_element_desc.SemanticName = "POSITION"; pipeline_input_element_desc.SemanticIndex = 0; @@ -4516,7 +4516,7 @@ void D3D12RenderTargetCache::PerformTransfersAndResolveClears( assert_true(GetPath() == Path::kHostRenderTargets); const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); uint64_t current_submission = command_processor_.GetCurrentSubmission(); DeferredCommandList& command_list = @@ -6476,8 +6476,8 @@ ID3D12PipelineState* D3D12RenderTargetCache::GetOrCreateDumpPipeline( // Pipeline // *************************************************************************** ID3D12PipelineState* pipeline = ui::d3d12::util::CreateComputePipeline( - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(), - built_shader_.data(), built_shader_size_bytes, + command_processor_.GetD3D12Provider().GetDevice(), built_shader_.data(), + built_shader_size_bytes, key.is_depth ? dump_root_signature_depth_ : dump_root_signature_color_); const char* format_name = key.is_depth @@ -6561,7 +6561,7 @@ void D3D12RenderTargetCache::DumpRenderTargets(uint32_t dump_base, // 32bpp and 64bpp. size_t edram_uav_indices[2] = {SIZE_MAX, SIZE_MAX}; const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); if (!bindless_resources_used_) { if (any_sources_32bpp_64bpp[0]) { edram_uav_indices[0] = current_temporary_descriptors_cpu_.size(); diff --git a/src/xenia/gpu/d3d12/d3d12_shared_memory.cc b/src/xenia/gpu/d3d12/d3d12_shared_memory.cc index fd521da54..e946a3319 100644 --- a/src/xenia/gpu/d3d12/d3d12_shared_memory.cc +++ b/src/xenia/gpu/d3d12/d3d12_shared_memory.cc @@ -43,7 +43,7 @@ bool D3D12SharedMemory::Initialize() { InitializeCommon(); const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); D3D12_RESOURCE_DESC buffer_desc; @@ -215,8 +215,9 @@ void D3D12SharedMemory::CommitUAVWritesAndTransitionBuffer( void D3D12SharedMemory::WriteRawSRVDescriptor( D3D12_CPU_DESCRIPTOR_HANDLE handle) { - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); device->CopyDescriptorsSimple( 1, handle, provider.OffsetViewDescriptor(buffer_descriptor_heap_start_, @@ -226,8 +227,9 @@ void D3D12SharedMemory::WriteRawSRVDescriptor( void D3D12SharedMemory::WriteRawUAVDescriptor( D3D12_CPU_DESCRIPTOR_HANDLE handle) { - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); device->CopyDescriptorsSimple( 1, handle, provider.OffsetViewDescriptor(buffer_descriptor_heap_start_, @@ -252,8 +254,9 @@ void D3D12SharedMemory::WriteUintPow2SRVDescriptor( assert_unhandled_case(element_size_bytes_pow2); return; } - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); device->CopyDescriptorsSimple( 1, handle, provider.OffsetViewDescriptor(buffer_descriptor_heap_start_, @@ -278,8 +281,9 @@ void D3D12SharedMemory::WriteUintPow2UAVDescriptor( assert_unhandled_case(element_size_bytes_pow2); return; } - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); device->CopyDescriptorsSimple( 1, handle, provider.OffsetViewDescriptor(buffer_descriptor_heap_start_, @@ -298,8 +302,9 @@ bool D3D12SharedMemory::InitializeTraceSubmitDownloads() { ui::d3d12::util::FillBufferResourceDesc( download_buffer_desc, download_page_count << page_size_log2(), D3D12_RESOURCE_FLAG_NONE); - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); if (FAILED(device->CreateCommittedResource( &ui::d3d12::util::kHeapPropertiesReadback, provider.GetHeapFlagCreateNotZeroed(), &download_buffer_desc, @@ -365,7 +370,7 @@ bool D3D12SharedMemory::AllocateSparseHostGpuMemoryRange( << host_gpu_memory_sparse_granularity_log2(); const ui::d3d12::D3D12Provider& provider = - command_processor_.GetD3D12Context().GetD3D12Provider(); + command_processor_.GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); ID3D12CommandQueue* direct_queue = provider.GetDirectQueue(); diff --git a/src/xenia/gpu/d3d12/pipeline_cache.cc b/src/xenia/gpu/d3d12/pipeline_cache.cc index 3eb63f172..f8617355a 100644 --- a/src/xenia/gpu/d3d12/pipeline_cache.cc +++ b/src/xenia/gpu/d3d12/pipeline_cache.cc @@ -87,7 +87,8 @@ PipelineCache::PipelineCache(D3D12CommandProcessor& command_processor, register_file_(register_file), render_target_cache_(render_target_cache), bindless_resources_used_(bindless_resources_used) { - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); bool edram_rov_used = render_target_cache.GetPath() == RenderTargetCache::Path::kPixelShaderInterlock; @@ -109,7 +110,8 @@ PipelineCache::PipelineCache(D3D12CommandProcessor& command_processor, PipelineCache::~PipelineCache() { Shutdown(); } bool PipelineCache::Initialize() { - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); // Initialize the command processor thread DXIL objects. dxbc_converter_ = nullptr; @@ -414,7 +416,8 @@ void PipelineCache::InitializeShaderStorage( std::mutex shaders_failed_to_translate_mutex; std::vector shaders_failed_to_translate; auto shader_translation_thread_function = [&]() { - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); StringBuffer ucode_disasm_buffer; DxbcShaderTranslator translator( provider.GetAdapterVendorID(), bindless_resources_used_, @@ -1241,7 +1244,8 @@ bool PipelineCache::TranslateAnalyzedShader( } // Disassemble the shader for dumping. - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); if (cvars::d3d12_dxbc_disasm_dxilconv) { translation.DisassembleDxbcAndDxil(provider, cvars::d3d12_dxbc_disasm, dxbc_converter, dxc_utils, dxc_compiler); @@ -2052,8 +2056,7 @@ ID3D12PipelineState* PipelineCache::CreateD3D12Pipeline( } // Create the D3D12 pipeline state object. - auto device = - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(); + ID3D12Device* device = command_processor_.GetD3D12Provider().GetDevice(); ID3D12PipelineState* state; if (FAILED(device->CreateGraphicsPipelineState(&state_desc, IID_PPV_ARGS(&state)))) { diff --git a/src/xenia/gpu/d3d12/texture_cache.cc b/src/xenia/gpu/d3d12/texture_cache.cc index f2098c9df..3c03131e3 100644 --- a/src/xenia/gpu/d3d12/texture_cache.cc +++ b/src/xenia/gpu/d3d12/texture_cache.cc @@ -869,8 +869,9 @@ TextureCache::TextureCache(D3D12CommandProcessor& command_processor, TextureCache::~TextureCache() { Shutdown(); } bool TextureCache::Initialize() { - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); if (IsDrawResolutionScaled()) { // Buffers not used yet - no need aliasing barriers to change ownership of @@ -1444,7 +1445,8 @@ void TextureCache::WriteActiveTextureBindfulSRV( } } } - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); D3D12_CPU_DESCRIPTOR_HANDLE source_handle; if (descriptor_index != UINT32_MAX) { assert_not_null(texture); @@ -1622,8 +1624,7 @@ void TextureCache::WriteSampler(SamplerParameters parameters, desc.MinLOD = float(parameters.mip_min_level); // Maximum mip level is in the texture resource itself. desc.MaxLOD = FLT_MAX; - auto device = - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(); + ID3D12Device* device = command_processor_.GetD3D12Provider().GetDevice(); device->CreateSampler(&desc, handle); } @@ -1712,8 +1713,9 @@ bool TextureCache::EnsureScaledResolveMemoryCommitted( uint64_t last_scaled = uint64_t(start_unscaled + (length_unscaled - 1)) * draw_resolution_scale_area; - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); // Ensure GPU virtual memory for buffers that may be used to access the range // is allocated - buffers are created. Always creating both buffers for all @@ -1943,8 +1945,8 @@ void TextureCache::CreateCurrentScaledResolveRangeUintPow2SRV( scaled_resolve_2gb_buffers_[buffer_index]; assert_not_null(buffer); ui::d3d12::util::CreateBufferTypedSRV( - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(), - handle, buffer->resource(), + command_processor_.GetD3D12Provider().GetDevice(), handle, + buffer->resource(), ui::d3d12::util::GetUintPow2DXGIFormat(element_size_bytes_pow2), uint32_t(scaled_resolve_current_range_length_scaled_ >> element_size_bytes_pow2), @@ -1961,8 +1963,8 @@ void TextureCache::CreateCurrentScaledResolveRangeUintPow2UAV( scaled_resolve_2gb_buffers_[buffer_index]; assert_not_null(buffer); ui::d3d12::util::CreateBufferTypedUAV( - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(), - handle, buffer->resource(), + command_processor_.GetD3D12Provider().GetDevice(), handle, + buffer->resource(), ui::d3d12::util::GetUintPow2DXGIFormat(element_size_bytes_pow2), uint32_t(scaled_resolve_current_range_length_scaled_ >> element_size_bytes_pow2), @@ -2254,8 +2256,9 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) { // Untiling through a buffer instead of using unordered access because copying // is not done that often. desc.Flags = D3D12_RESOURCE_FLAG_NONE; - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); - auto device = provider.GetDevice(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); // Assuming untiling will be the next operation. D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_COPY_DEST; ID3D12Resource* resource; @@ -2317,9 +2320,9 @@ bool TextureCache::LoadTextureData(Texture* texture) { return true; } - auto& command_list = command_processor_.GetDeferredCommandList(); - auto device = - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(); + DeferredCommandList& command_list = + command_processor_.GetDeferredCommandList(); + ID3D12Device* device = command_processor_.GetD3D12Provider().GetDevice(); // Get the pipeline. LoadMode load_mode = GetLoadMode(texture->key); @@ -2875,8 +2878,7 @@ uint32_t TextureCache::FindOrCreateTextureDescriptor(Texture& texture, host_swizzle | D3D12_SHADER_COMPONENT_MAPPING_ALWAYS_SET_BIT_AVOIDING_ZEROMEM_MISTAKES; - auto device = - command_processor_.GetD3D12Context().GetD3D12Provider().GetDevice(); + ID3D12Device* device = command_processor_.GetD3D12Provider().GetDevice(); uint32_t descriptor_index; if (bindless_resources_used_) { descriptor_index = @@ -2928,7 +2930,8 @@ uint32_t TextureCache::FindOrCreateTextureDescriptor(Texture& texture, D3D12_CPU_DESCRIPTOR_HANDLE TextureCache::GetTextureDescriptorCPUHandle( uint32_t descriptor_index) const { - auto& provider = command_processor_.GetD3D12Context().GetD3D12Provider(); + const ui::d3d12::D3D12Provider& provider = + command_processor_.GetD3D12Provider(); if (bindless_resources_used_) { return provider.OffsetViewDescriptor( command_processor_.GetViewBindlessHeapCPUStart(), descriptor_index); diff --git a/src/xenia/gpu/draw_util.cc b/src/xenia/gpu/draw_util.cc index 6cb28c9a4..83ea6601d 100644 --- a/src/xenia/gpu/draw_util.cc +++ b/src/xenia/gpu/draw_util.cc @@ -23,6 +23,7 @@ #include "xenia/gpu/texture_info.h" #include "xenia/gpu/texture_util.h" #include "xenia/gpu/xenos.h" +#include "xenia/ui/graphics_util.h" // Very prominent in 545407F2. DEFINE_bool( @@ -33,85 +34,10 @@ DEFINE_bool( "for certain games to display the scene graphics).", "GPU"); -DEFINE_bool( - present_rescale, true, - "Whether to rescale the image, instead of maintaining the original pixel " - "size, when presenting to the window. When this is disabled, other " - "positioning options are ignored.", - "GPU"); -DEFINE_bool( - present_letterbox, true, - "Maintain aspect ratio when stretching by displaying bars around the image " - "when there's no more overscan area to crop out.", - "GPU"); -// https://github.com/MonoGame/MonoGame/issues/4697#issuecomment-217779403 -// Using the value from DirectXTK (5% cropped out from each side, thus 90%), -// which is not exactly the Xbox One title-safe area, but close, and within the -// action-safe area: -// https://github.com/microsoft/DirectXTK/blob/1e80a465c6960b457ef9ab6716672c1443a45024/Src/SimpleMath.cpp#L144 -// XNA TitleSafeArea is 80%, but it's very conservative, designed for CRT, and -// is the title-safe area rather than the action-safe area. -// 90% is also exactly the fraction of 16:9 height in 16:10. -DEFINE_int32( - present_safe_area_x, 90, - "Percentage of the image width that can be kept when presenting to " - "maintain aspect ratio without letterboxing or stretching.", - "GPU"); -DEFINE_int32( - present_safe_area_y, 90, - "Percentage of the image height that can be kept when presenting to " - "maintain aspect ratio without letterboxing or stretching.", - "GPU"); - namespace xe { namespace gpu { namespace draw_util { -int32_t FloatToD3D11Fixed16p8(float f32) { - // https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#3.2.4.1%20FLOAT%20-%3E%20Fixed%20Point%20Integer - // Early exit tests. - // n == NaN || n.unbiasedExponent < -f-1 -> 0 . 0 - if (!(std::abs(f32) >= 1.0f / 512.0f)) { - return 0; - } - // n >= (2^(i-1)-2^-f) -> 2^(i-1)-1 . 2^f-1 - if (f32 >= 32768.0f - 1.0f / 256.0f) { - return (1 << 23) - 1; - } - // n <= -2^(i-1) -> -2^(i-1) . 0 - if (f32 <= -32768.0f) { - return -32768 * 256; - } - uint32_t f32_bits = *reinterpret_cast(&f32); - // Copy float32 mantissa bits [22:0] into corresponding bits [22:0] of a - // result buffer that has at least 24 bits total storage (before reaching - // rounding step further below). This includes one bit for the hidden 1. - // Set bit [23] (float32 hidden bit). - // Clear bits [31:24]. - union { - int32_t s; - uint32_t u; - } result; - result.u = (f32_bits & ((1 << 23) - 1)) | (1 << 23); - // If the sign bit is set in the float32 number (negative), then take the 2's - // component of the entire set of bits. - if ((f32_bits >> 31) != 0) { - result.s = -result.s; - } - // Final calculation: extraBits = (mantissa - f) - n.unbiasedExponent - // (guaranteed to be >= 0). - int32_t exponent = int32_t((f32_bits >> 23) & 255) - 127; - uint32_t extra_bits = uint32_t(15 - exponent); - if (extra_bits) { - // Round the 32-bit value to a decimal that is extraBits to the left of - // the LSB end, using nearest-even. - result.u += (1 << (extra_bits - 1)) - 1 + ((result.u >> extra_bits) & 1); - // Shift right by extraBits (sign extending). - result.s >>= extra_bits; - } - return result.s; -} - bool IsRasterizationPotentiallyDone(const RegisterFile& regs, bool primitive_polygonal) { // TODO(Triang3l): Investigate ModeControl::kIgnore better, with respect to @@ -746,7 +672,7 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory, regs.Get().pix_center ? 0.0f : 0.5f; int32_t vertices_fixed[6]; for (size_t i = 0; i < xe::countof(vertices_fixed); ++i) { - vertices_fixed[i] = FloatToD3D11Fixed16p8( + vertices_fixed[i] = ui::FloatToD3D11Fixed16p8( xenos::GpuSwap(vertices_guest[i], fetch.endian) + half_pixel_offset); } // Inclusive. @@ -1151,87 +1077,6 @@ ResolveCopyShaderIndex ResolveInfo::GetCopyShader( return shader; } -void GetPresentArea(uint32_t source_width, uint32_t source_height, - uint32_t window_width, uint32_t window_height, - int32_t& target_x_out, int32_t& target_y_out, - uint32_t& target_width_out, uint32_t& target_height_out) { - if (!cvars::present_rescale) { - target_x_out = (int32_t(window_width) - int32_t(source_width)) / 2; - target_y_out = (int32_t(window_height) - int32_t(source_height)) / 2; - target_width_out = source_width; - target_height_out = source_height; - return; - } - // Prevent division by zero. - if (!source_width || !source_height) { - target_x_out = 0; - target_y_out = 0; - target_width_out = 0; - target_height_out = 0; - return; - } - if (uint64_t(window_width) * source_height > - uint64_t(source_width) * window_height) { - // The window is wider that the source - crop along Y, then letterbox or - // stretch along X. - uint32_t present_safe_area; - if (cvars::present_safe_area_y > 0 && cvars::present_safe_area_y < 100) { - present_safe_area = uint32_t(cvars::present_safe_area_y); - } else { - present_safe_area = 100; - } - uint32_t target_height = - uint32_t(uint64_t(window_width) * source_height / source_width); - bool letterbox = false; - if (target_height * present_safe_area > window_height * 100) { - // Don't crop out more than the safe area margin - letterbox or stretch. - target_height = window_height * 100 / present_safe_area; - letterbox = true; - } - if (letterbox && cvars::present_letterbox) { - uint32_t target_width = - uint32_t(uint64_t(source_width) * window_height * 100 / - (source_height * present_safe_area)); - target_x_out = (int32_t(window_width) - int32_t(target_width)) / 2; - target_width_out = target_width; - } else { - target_x_out = 0; - target_width_out = window_width; - } - target_y_out = (int32_t(window_height) - int32_t(target_height)) / 2; - target_height_out = target_height; - } else { - // The window is taller than the source - crop along X, then letterbox or - // stretch along Y. - uint32_t present_safe_area; - if (cvars::present_safe_area_x > 0 && cvars::present_safe_area_x < 100) { - present_safe_area = uint32_t(cvars::present_safe_area_x); - } else { - present_safe_area = 100; - } - uint32_t target_width = - uint32_t(uint64_t(window_height) * source_width / source_height); - bool letterbox = false; - if (target_width * present_safe_area > window_width * 100) { - // Don't crop out more than the safe area margin - letterbox or stretch. - target_width = window_width * 100 / present_safe_area; - letterbox = true; - } - if (letterbox && cvars::present_letterbox) { - uint32_t target_height = - uint32_t(uint64_t(source_height) * window_width * 100 / - (source_width * present_safe_area)); - target_y_out = (int32_t(window_height) - int32_t(target_height)) / 2; - target_height_out = target_height; - } else { - target_y_out = 0; - target_height_out = window_height; - } - target_x_out = (int32_t(window_width) - int32_t(target_width)) / 2; - target_width_out = target_width; - } -} - } // namespace draw_util } // namespace gpu } // namespace xe diff --git a/src/xenia/gpu/draw_util.h b/src/xenia/gpu/draw_util.h index 24f58b23a..39d430676 100644 --- a/src/xenia/gpu/draw_util.h +++ b/src/xenia/gpu/draw_util.h @@ -25,15 +25,6 @@ namespace xe { namespace gpu { namespace draw_util { -// For estimating coverage extents from vertices. This may give results that are -// different than what the host GPU will actually draw (this is the reference -// conversion with 1/2 ULP accuracy, but Direct3D 11 permits 0.6 ULP tolerance -// in floating point to fixed point conversion), but is enough to tie-break -// vertices at pixel centers (due to the half-pixel offset applied to integer -// coordinates incorrectly, for instance) with some error tolerance near 0.5, -// for use with the top-left rasterization rule later. -int32_t FloatToD3D11Fixed16p8(float f32); - // Polygonal primitive types (not including points and lines) are rasterized as // triangles, have front and back faces, and also support face culling and fill // modes (polymode_front_ptype, polymode_back_ptype). Other primitive types are diff --git a/src/xenia/gpu/graphics_system.cc b/src/xenia/gpu/graphics_system.cc index 6ffab9150..b5470fd0a 100644 --- a/src/xenia/gpu/graphics_system.cc +++ b/src/xenia/gpu/graphics_system.cc @@ -2,13 +2,19 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/gpu/graphics_system.h" +#include +#include +#include +#include +#include + #include "xenia/base/byte_stream.h" #include "xenia/base/clock.h" #include "xenia/base/logging.h" @@ -48,70 +54,40 @@ GraphicsSystem::~GraphicsSystem() = default; X_STATUS GraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, - ui::Window* target_window) { + ui::WindowedAppContext* app_context, + [[maybe_unused]] bool is_surface_required) { memory_ = processor->memory(); processor_ = processor; kernel_state_ = kernel_state; - target_window_ = target_window; + app_context_ = app_context; - // Initialize display and rendering context. - // This must happen on the UI thread. - std::unique_ptr processor_context = nullptr; if (provider_) { - // Setup the context the command processor will do all its drawing in. - bool contexts_initialized = true; - processor_context = provider()->CreateOffscreenContext(); - if (processor_context) { - if (target_window_) { - if (!target_window_->app_context().CallInUIThreadSynchronous([&]() { - // Create the context used for presentation. - assert_null(target_window->context()); - target_window_->set_context( - provider_->CreateContext(target_window_)); - })) { - contexts_initialized = false; - } - } + // Safe if either the UI thread call or the presenter creation fails. + if (app_context_) { + app_context_->CallInUIThreadSynchronous([this]() { + presenter_ = provider_->CreatePresenter( + [this](bool is_responsible, bool statically_from_ui_thread) { + OnHostGpuLossFromAnyThread(is_responsible); + }); + }); } else { - contexts_initialized = false; - } - if (!contexts_initialized) { - xe::FatalError( - "Unable to initialize graphics context. Xenia requires Vulkan " - "support.\n" - "\n" - "Ensure you have the latest drivers for your GPU and " - "that it supports Vulkan.\n" - "\n" - "See https://xenia.jp/faq/ for more information and a list of " - "supported GPUs."); - return X_STATUS_UNSUCCESSFUL; + // May be needed for offscreen use, such as capturing the guest output + // image. + presenter_ = provider_->CreatePresenter( + [this](bool is_responsible, bool statically_from_ui_thread) { + OnHostGpuLossFromAnyThread(is_responsible); + }); } } // Create command processor. This will spin up a thread to process all // incoming ringbuffer packets. command_processor_ = CreateCommandProcessor(); - if (!command_processor_->Initialize(std::move(processor_context))) { + if (!command_processor_->Initialize()) { XELOGE("Unable to initialize command processor"); return X_STATUS_UNSUCCESSFUL; } - if (target_window) { - command_processor_->set_swap_request_handler( - [this]() { target_window_->Invalidate(); }); - - // Watch for paint requests to do our swap. - target_window->on_painting.AddListener( - [this](xe::ui::UIEvent* e) { Swap(e); }); - - // Watch for context lost events. - target_window->on_context_lost.AddListener( - [this](xe::ui::UIEvent* e) { Reset(); }); - } else { - command_processor_->set_swap_request_handler([]() {}); - } - // Let the processor know we want register access callbacks. memory_->AddVirtualMappedRange( 0x7FC80000, 0xFFFF0000, 0x0000FFFF, this, @@ -152,6 +128,7 @@ void GraphicsSystem::Shutdown() { if (command_processor_) { EndTracing(); command_processor_->Shutdown(); + command_processor_.reset(); } if (vsync_worker_thread_) { @@ -159,13 +136,35 @@ void GraphicsSystem::Shutdown() { vsync_worker_thread_->Wait(0, 0, 0, nullptr); vsync_worker_thread_.reset(); } + + if (presenter_) { + if (app_context_) { + app_context_->CallInUIThreadSynchronous([this]() { presenter_.reset(); }); + } + // If there's no app context (thus the presenter is owned by the thread that + // initialized the GraphicsSystem) or can't be queueing UI thread calls + // anymore, shutdown anyway. + presenter_.reset(); + } + + provider_.reset(); } -void GraphicsSystem::Reset() { - // TODO(DrChat): Reset the system. - XELOGI("Context lost; Reset invoked"); - Shutdown(); - +void GraphicsSystem::OnHostGpuLossFromAnyThread( + [[maybe_unused]] bool is_responsible) { + // TODO(Triang3l): Somehow gain exclusive ownership of the Provider (may be + // used by the command processor, the presenter, and possibly anything else, + // it's considered free-threaded, except for lifetime management which will be + // involved in this case) and reset it so a new host GPU API device is + // created. Then ask the command processor to reset itself in its thread, and + // ask the UI thread to reset the Presenter (the UI thread manages its + // lifetime - but if there's no WindowedAppContext, either don't reset it as + // in this case there's no user who needs uninterrupted gameplay, or somehow + // protect it with a mutex so any thread can be considered a UI thread and + // reset). + if (host_gpu_loss_reported_.test_and_set(std::memory_order_relaxed)) { + return; + } xe::FatalError("Graphics device lost (probably due to an internal error)"); } diff --git a/src/xenia/gpu/graphics_system.h b/src/xenia/gpu/graphics_system.h index 3fd7132e1..0434a5619 100644 --- a/src/xenia/gpu/graphics_system.h +++ b/src/xenia/gpu/graphics_system.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -11,7 +11,10 @@ #define XENIA_GPU_GRAPHICS_SYSTEM_H_ #include +#include +#include #include +#include #include #include @@ -19,7 +22,9 @@ #include "xenia/gpu/register_file.h" #include "xenia/kernel/xthread.h" #include "xenia/memory.h" -#include "xenia/ui/window.h" +#include "xenia/ui/graphics_provider.h" +#include "xenia/ui/presenter.h" +#include "xenia/ui/windowed_app_context.h" #include "xenia/xbox.h" namespace xe { @@ -41,14 +46,17 @@ class GraphicsSystem { cpu::Processor* processor() const { return processor_; } kernel::KernelState* kernel_state() const { return kernel_state_; } ui::GraphicsProvider* provider() const { return provider_.get(); } + ui::Presenter* presenter() const { return presenter_.get(); } virtual X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, - ui::Window* target_window); + ui::WindowedAppContext* app_context, + bool is_surface_required); virtual void Shutdown(); - virtual void Reset(); - virtual std::unique_ptr Capture() { return nullptr; } + // May be called from any thread any number of times, even during recovery + // from a device loss. + void OnHostGpuLossFromAnyThread(bool is_responsible); RegisterFile* register_file() { return ®ister_file_; } CommandProcessor* command_processor() const { @@ -91,12 +99,11 @@ class GraphicsSystem { void WriteRegister(uint32_t addr, uint32_t value); void MarkVblank(); - virtual void Swap(xe::ui::UIEvent* e) = 0; Memory* memory_ = nullptr; cpu::Processor* processor_ = nullptr; kernel::KernelState* kernel_state_ = nullptr; - ui::Window* target_window_ = nullptr; + ui::WindowedAppContext* app_context_ = nullptr; std::unique_ptr provider_; uint32_t interrupt_callback_ = 0; @@ -109,6 +116,11 @@ class GraphicsSystem { std::unique_ptr command_processor_; bool paused_ = false; + + private: + std::unique_ptr presenter_; + + std::atomic_flag host_gpu_loss_reported_; }; } // namespace gpu diff --git a/src/xenia/gpu/null/null_command_processor.cc b/src/xenia/gpu/null/null_command_processor.cc index 5d1e7a9b0..e706d5bbf 100644 --- a/src/xenia/gpu/null/null_command_processor.cc +++ b/src/xenia/gpu/null/null_command_processor.cc @@ -31,9 +31,9 @@ void NullCommandProcessor::ShutdownContext() { return CommandProcessor::ShutdownContext(); } -void NullCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, - uint32_t frontbuffer_width, - uint32_t frontbuffer_height) {} +void NullCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, + uint32_t frontbuffer_width, + uint32_t frontbuffer_height) {} Shader* NullCommandProcessor::LoadShader(xenos::ShaderType shader_type, uint32_t guest_address, diff --git a/src/xenia/gpu/null/null_command_processor.h b/src/xenia/gpu/null/null_command_processor.h index 0285b1ed2..11c829018 100644 --- a/src/xenia/gpu/null/null_command_processor.h +++ b/src/xenia/gpu/null/null_command_processor.h @@ -33,8 +33,8 @@ class NullCommandProcessor : public CommandProcessor { bool SetupContext() override; void ShutdownContext() override; - void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, - uint32_t frontbuffer_height) override; + void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, + uint32_t frontbuffer_height) override; Shader* LoadShader(xenos::ShaderType shader_type, uint32_t guest_address, const uint32_t* host_address, diff --git a/src/xenia/gpu/null/null_graphics_system.cc b/src/xenia/gpu/null/null_graphics_system.cc index c6df3ff09..57d5e958f 100644 --- a/src/xenia/gpu/null/null_graphics_system.cc +++ b/src/xenia/gpu/null/null_graphics_system.cc @@ -23,31 +23,20 @@ NullGraphicsSystem::~NullGraphicsSystem() {} X_STATUS NullGraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, - ui::Window* target_window) { + ui::WindowedAppContext* app_context, + bool is_surface_required) { // This is a null graphics system, but we still setup vulkan because UI needs // it through us :| - provider_ = xe::ui::vulkan::VulkanProvider::Create(); - - return GraphicsSystem::Setup(processor, kernel_state, target_window); + provider_ = xe::ui::vulkan::VulkanProvider::Create(is_surface_required); + return GraphicsSystem::Setup(processor, kernel_state, app_context, + is_surface_required); } -void NullGraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); } - std::unique_ptr NullGraphicsSystem::CreateCommandProcessor() { return std::unique_ptr( new NullCommandProcessor(this, kernel_state_)); } -void NullGraphicsSystem::Swap(xe::ui::UIEvent* e) { - if (!command_processor_) { - return; - } - - auto& swap_state = command_processor_->swap_state(); - std::lock_guard lock(swap_state.mutex); - swap_state.pending = false; -} - } // namespace null } // namespace gpu } // namespace xe \ No newline at end of file diff --git a/src/xenia/gpu/null/null_graphics_system.h b/src/xenia/gpu/null/null_graphics_system.h index 640a0424f..d5b8d32b9 100644 --- a/src/xenia/gpu/null/null_graphics_system.h +++ b/src/xenia/gpu/null/null_graphics_system.h @@ -29,13 +29,11 @@ class NullGraphicsSystem : public GraphicsSystem { std::string name() const override { return "null"; } X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, - ui::Window* target_window) override; - void Shutdown() override; + ui::WindowedAppContext* app_context, + bool is_surface_required) override; private: std::unique_ptr CreateCommandProcessor() override; - - void Swap(xe::ui::UIEvent* e) override; }; } // namespace null diff --git a/src/xenia/gpu/premake5.lua b/src/xenia/gpu/premake5.lua index 27e817f44..bfdb70789 100644 --- a/src/xenia/gpu/premake5.lua +++ b/src/xenia/gpu/premake5.lua @@ -39,6 +39,7 @@ project("xenia-gpu-shader-compiler") "spirv-tools", "xenia-base", "xenia-gpu", + "xenia-ui", "xenia-ui-spirv", }) defines({ diff --git a/src/xenia/gpu/shaders/apply_gamma_pwl.cs.hlsl b/src/xenia/gpu/shaders/apply_gamma_pwl.cs.hlsl new file mode 100644 index 000000000..8ba176f73 --- /dev/null +++ b/src/xenia/gpu/shaders/apply_gamma_pwl.cs.hlsl @@ -0,0 +1,2 @@ +float XeApplyGammaGetAlpha(float3 color) { return 1.0f; } +#include "apply_gamma_pwl.hlsli" diff --git a/src/xenia/gpu/shaders/apply_gamma_pwl.hlsli b/src/xenia/gpu/shaders/apply_gamma_pwl.hlsli new file mode 100644 index 000000000..a77c85460 --- /dev/null +++ b/src/xenia/gpu/shaders/apply_gamma_pwl.hlsli @@ -0,0 +1,41 @@ +// float XeApplyGammaGetAlpha(float3 color) needs to be specified externally. + +cbuffer XeApplyGammaRampConstants : register(b0) { + uint2 xe_apply_gamma_size; +}; +RWTexture2D xe_apply_gamma_dest : register(u0); +Texture2D xe_apply_gamma_source : register(t0); +Buffer xe_apply_gamma_ramp : register(t1); + +float XeApplyPWLGamma(uint input, uint component) { + // TODO(Triang3l): If this is ever used for gamma other than 128 entries for a + // 10bpc front buffer, handle the increment from DC_LUTA/B_CONTROL. Currently + // assuming it's 2^3 = 8, or 1024 / 128. + // output = base + (multiplier * delta) / increment + // https://developer.amd.com/wordpress/media/2012/10/RRG-216M56-03oOEM.pdf + // The lower 6 bits of the base and the delta are 0 (though enforcing that in + // the shader is not necessary). + // The `(multiplier * delta) / increment` part may result in a nonzero value + // in the lower 6 bits of the result, however, so doing `* (1.0f / 64.0f)` + // instead of `>> 6` to preserve them (if the render target is 16bpc rather + // than 10bpc, for instance). + uint2 ramp_value = xe_apply_gamma_ramp[(input >> 3u) * 3u + component]; + return saturate((float(ramp_value.x) + + float((input & 7u) * ramp_value.y) * (1.0f / 8.0f)) * + (1.0f / (64.0f * 1023.0f))); +} + +[numthreads(8, 8, 1)] +void main(uint3 xe_thread_id : SV_DispatchThreadID) { + [branch] if (any(xe_thread_id.xy >= xe_apply_gamma_size)) { + return; + } + // UNORM conversion according to the Direct3D 10+ rules. + uint3 input = uint3(xe_apply_gamma_source[xe_thread_id.xy] * 1023.0f + 0.5f); + // The ramp is BGR, not RGB. + float3 output = float3(XeApplyPWLGamma(input.r, 2u), + XeApplyPWLGamma(input.g, 1u), + XeApplyPWLGamma(input.b, 0u)); + xe_apply_gamma_dest[xe_thread_id.xy] = + float4(output, XeApplyGammaGetAlpha(output)); +} diff --git a/src/xenia/gpu/shaders/apply_gamma_pwl_fxaa_luma.cs.hlsl b/src/xenia/gpu/shaders/apply_gamma_pwl_fxaa_luma.cs.hlsl new file mode 100644 index 000000000..3a6bb4f9b --- /dev/null +++ b/src/xenia/gpu/shaders/apply_gamma_pwl_fxaa_luma.cs.hlsl @@ -0,0 +1,5 @@ +// Perceptual luminance for FXAA. +float XeApplyGammaGetAlpha(float3 color) { + return dot(color, float3(0.299, 0.587, 0.114)); +} +#include "apply_gamma_pwl.hlsli" diff --git a/src/xenia/gpu/shaders/apply_gamma_table.cs.hlsl b/src/xenia/gpu/shaders/apply_gamma_table.cs.hlsl new file mode 100644 index 000000000..94b43ddd8 --- /dev/null +++ b/src/xenia/gpu/shaders/apply_gamma_table.cs.hlsl @@ -0,0 +1,2 @@ +float XeApplyGammaGetAlpha(float3 color) { return 1.0f; } +#include "apply_gamma_table.hlsli" diff --git a/src/xenia/gpu/shaders/apply_gamma_table.hlsli b/src/xenia/gpu/shaders/apply_gamma_table.hlsli new file mode 100644 index 000000000..7f43a9567 --- /dev/null +++ b/src/xenia/gpu/shaders/apply_gamma_table.hlsli @@ -0,0 +1,23 @@ +// float XeApplyGammaGetAlpha(float3 color) needs to be specified externally. + +cbuffer XeApplyGammaRampConstants : register(b0) { + uint2 xe_apply_gamma_size; +}; +RWTexture2D xe_apply_gamma_dest : register(u0); +Texture2D xe_apply_gamma_source : register(t0); +Buffer xe_apply_gamma_ramp : register(t1); + +[numthreads(8, 8, 1)] +void main(uint3 xe_thread_id : SV_DispatchThreadID) { + [branch] if (any(xe_thread_id.xy >= xe_apply_gamma_size)) { + return; + } + // UNORM conversion according to the Direct3D 10+ rules. + uint3 input = uint3(xe_apply_gamma_source[xe_thread_id.xy] * 255.0f + 0.5f); + // The ramp is BGR, not RGB. + float3 output = float3(xe_apply_gamma_ramp[input.r].b, + xe_apply_gamma_ramp[input.g].g, + xe_apply_gamma_ramp[input.b].r); + xe_apply_gamma_dest[xe_thread_id.xy] = + float4(output, XeApplyGammaGetAlpha(output)); +} diff --git a/src/xenia/gpu/shaders/apply_gamma_table_fxaa_luma.cs.hlsl b/src/xenia/gpu/shaders/apply_gamma_table_fxaa_luma.cs.hlsl new file mode 100644 index 000000000..16b2593f2 --- /dev/null +++ b/src/xenia/gpu/shaders/apply_gamma_table_fxaa_luma.cs.hlsl @@ -0,0 +1,5 @@ +// Perceptual luminance for FXAA. +float XeApplyGammaGetAlpha(float3 color) { + return dot(color, float3(0.299, 0.587, 0.114)); +} +#include "apply_gamma_table.hlsli" diff --git a/src/xenia/gpu/shaders/bytecode/.clang-format b/src/xenia/gpu/shaders/bytecode/.clang-format index e3845288a..9d159247d 100644 --- a/src/xenia/gpu/shaders/bytecode/.clang-format +++ b/src/xenia/gpu/shaders/bytecode/.clang-format @@ -1 +1,2 @@ DisableFormat: true +SortIncludes: false diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_quad_hs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_quad_hs.h index ecae25810..1ff1d3d38 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_quad_hs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_quad_hs.h @@ -84,10 +84,10 @@ ret const BYTE adaptive_quad_hs[] = { - 68, 88, 66, 67, 94, 229, - 104, 122, 195, 92, 144, 36, - 252, 149, 235, 210, 131, 205, - 139, 42, 1, 0, 0, 0, + 68, 88, 66, 67, 91, 114, + 196, 93, 35, 195, 210, 49, + 154, 70, 168, 244, 8, 132, + 25, 246, 1, 0, 0, 0, 176, 4, 0, 0, 6, 0, 0, 0, 56, 0, 0, 0, 164, 0, 0, 0, 220, 0, @@ -98,7 +98,7 @@ const BYTE adaptive_quad_hs[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 1, 5, 83, 72, - 0, 5, 4, 0, 60, 0, + 0, 5, 0, 0, 60, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_triangle_hs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_triangle_hs.h index 682d79b83..4d9950a38 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_triangle_hs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/adaptive_triangle_hs.h @@ -73,10 +73,10 @@ ret const BYTE adaptive_triangle_hs[] = { - 68, 88, 66, 67, 86, 168, - 189, 119, 111, 17, 238, 147, - 225, 129, 174, 39, 170, 24, - 24, 225, 1, 0, 0, 0, + 68, 88, 66, 67, 10, 165, + 117, 176, 19, 12, 130, 108, + 171, 104, 195, 161, 52, 251, + 99, 193, 1, 0, 0, 0, 224, 3, 0, 0, 6, 0, 0, 0, 56, 0, 0, 0, 164, 0, 0, 0, 220, 0, @@ -87,7 +87,7 @@ const BYTE adaptive_triangle_hs[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 1, 5, 83, 72, - 0, 5, 4, 0, 60, 0, + 0, 5, 0, 0, 60, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_cs.h new file mode 100644 index 000000000..727d15384 --- /dev/null +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_cs.h @@ -0,0 +1,409 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeApplyGammaRampConstants +// { +// +// uint2 xe_apply_gamma_size; // Offset: 0 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_apply_gamma_source texture float3 2d T0 t0 1 +// xe_apply_gamma_ramp texture uint2 buf T1 t1 1 +// xe_apply_gamma_dest UAV unorm4 2d U0 u0 1 +// XeApplyGammaRampConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Input +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Output +cs_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_resource_buffer (uint,uint,uint,uint) T1[1:1], space=0 +dcl_uav_typed_texture2d (unorm,unorm,unorm,unorm) U0[0:0], space=0 +dcl_input vThreadID.xy +dcl_temps 3 +dcl_thread_group 8, 8, 1 +uge r0.xy, vThreadID.xyxx, CB0[0][0].xyxx +or r0.x, r0.y, r0.x +if_nz r0.x + ret +endif +mov r0.xy, vThreadID.xyxx +mov r0.zw, l(0,0,0,0) +ld r0.xyz, r0.xyzw, T0[0].xyzw +mad r0.xyz, r0.xyzx, l(1023.000000, 1023.000000, 1023.000000, 0.000000), l(0.500000, 0.500000, 0.500000, 0.000000) +ftou r0.xyz, r0.xyzx +ushr r1.xyz, r0.xyzx, l(3, 3, 3, 0) +imul null, r0.w, r1.z, l(3) +imad r1.xy, r1.xyxx, l(3, 3, 0, 0), l(2, 1, 0, 0) +ld r1.xz, r1.xxxx, T1[1].xzyw +utof r1.x, r1.x +and r0.xyz, r0.xyzx, l(7, 7, 7, 0) +imul null, r0.x, r1.z, r0.x +utof r0.x, r0.x +mad r0.x, r0.x, l(0.125000), r1.x +mul r0.x, r0.x, l(0.000015) +min r2.x, r0.x, l(1.000000) +ld r1.xy, r1.yyyy, T1[1].xyzw +utof r0.x, r1.x +imul null, r0.y, r0.y, r1.y +utof r0.y, r0.y +mad r0.x, r0.y, l(0.125000), r0.x +mul r0.x, r0.x, l(0.000015) +min r2.y, r0.x, l(1.000000) +ld r0.xy, r0.wwww, T1[1].xyzw +imul null, r0.y, r0.y, r0.z +utof r0.xy, r0.xyxx +mad r0.x, r0.y, l(0.125000), r0.x +mul r0.x, r0.x, l(0.000015) +min r2.z, r0.x, l(1.000000) +mov r2.w, l(1.000000) +store_uav_typed U0[0].xyzw, vThreadID.xyyy, r2.xyzw +ret +// Approximately 37 instruction slots used +#endif + +const BYTE apply_gamma_pwl_cs[] = +{ + 68, 88, 66, 67, 180, 180, + 222, 28, 4, 138, 188, 113, + 52, 97, 214, 88, 116, 106, + 105, 240, 1, 0, 0, 0, + 128, 7, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 24, 2, 0, 0, 40, 2, + 0, 0, 56, 2, 0, 0, + 228, 6, 0, 0, 82, 68, + 69, 70, 220, 1, 0, 0, + 1, 0, 0, 0, 52, 1, + 0, 0, 4, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 83, 67, 0, 5, 0, 0, + 180, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 220, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 242, 0, + 0, 0, 2, 0, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 255, 255, 255, 255, + 1, 0, 0, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 6, 1, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 1, 0, 0, 0, + 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 26, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 120, 101, + 95, 97, 112, 112, 108, 121, + 95, 103, 97, 109, 109, 97, + 95, 115, 111, 117, 114, 99, + 101, 0, 120, 101, 95, 97, + 112, 112, 108, 121, 95, 103, + 97, 109, 109, 97, 95, 114, + 97, 109, 112, 0, 120, 101, + 95, 97, 112, 112, 108, 121, + 95, 103, 97, 109, 109, 97, + 95, 100, 101, 115, 116, 0, + 88, 101, 65, 112, 112, 108, + 121, 71, 97, 109, 109, 97, + 82, 97, 109, 112, 67, 111, + 110, 115, 116, 97, 110, 116, + 115, 0, 26, 1, 0, 0, + 1, 0, 0, 0, 76, 1, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 116, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 144, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 120, 101, 95, 97, 112, 112, + 108, 121, 95, 103, 97, 109, + 109, 97, 95, 115, 105, 122, + 101, 0, 117, 105, 110, 116, + 50, 0, 171, 171, 1, 0, + 19, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 136, 1, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 8, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 79, 83, 71, 78, 8, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 83, 72, + 69, 88, 164, 4, 0, 0, + 81, 0, 5, 0, 41, 1, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 88, 8, 0, 7, + 70, 126, 48, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 68, 68, + 0, 0, 0, 0, 0, 0, + 156, 24, 0, 7, 70, 238, + 49, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 17, 17, 0, 0, + 0, 0, 0, 0, 95, 0, + 0, 2, 50, 0, 2, 0, + 104, 0, 0, 2, 3, 0, + 0, 0, 155, 0, 0, 4, + 8, 0, 0, 0, 8, 0, + 0, 0, 1, 0, 0, 0, + 80, 0, 0, 8, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 2, 0, 70, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 60, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 0, 0, 0, 0, + 62, 0, 0, 1, 21, 0, + 0, 1, 54, 0, 0, 4, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 2, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 15, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 192, + 127, 68, 0, 192, 127, 68, + 0, 192, 127, 68, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 0, 28, 0, + 0, 5, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 85, 0, 0, 10, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 38, 0, + 0, 8, 0, 208, 0, 0, + 130, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 3, 0, 0, 0, + 35, 0, 0, 15, 50, 0, + 16, 0, 1, 0, 0, 0, + 70, 0, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 82, 0, + 16, 0, 1, 0, 0, 0, + 6, 0, 16, 0, 1, 0, + 0, 0, 134, 125, 32, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 86, 0, 0, 5, + 18, 0, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 1, 0, 0, 0, 1, 0, + 0, 10, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 7, 0, + 0, 0, 7, 0, 0, 0, + 7, 0, 0, 0, 0, 0, + 0, 0, 38, 0, 0, 8, + 0, 208, 0, 0, 18, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 86, 0, + 0, 5, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 62, 10, 0, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 8, 32, 128, 55, 51, 0, + 0, 7, 18, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 45, 0, 0, 8, + 50, 0, 16, 0, 1, 0, + 0, 0, 86, 5, 16, 0, + 1, 0, 0, 0, 70, 126, + 32, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 86, 0, + 0, 5, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 1, 0, 0, 0, + 38, 0, 0, 8, 0, 208, + 0, 0, 34, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 1, 0, + 0, 0, 86, 0, 0, 5, + 34, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 18, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 62, 10, 0, 16, 0, + 0, 0, 0, 0, 56, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 8, 32, + 128, 55, 51, 0, 0, 7, + 34, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 45, 0, 0, 8, 50, 0, + 16, 0, 0, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 38, 0, 0, 8, + 0, 208, 0, 0, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 86, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 62, 10, 0, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 8, 32, 128, 55, 51, 0, + 0, 7, 66, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 54, 0, 0, 5, + 130, 0, 16, 0, 2, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 164, 0, + 0, 7, 242, 224, 33, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 70, 5, 2, 0, + 70, 14, 16, 0, 2, 0, + 0, 0, 62, 0, 0, 1, + 83, 84, 65, 84, 148, 0, + 0, 0, 37, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 10, 0, 0, 0, 5, 0, + 0, 0, 4, 0, 0, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0 +}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_fxaa_luma_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_fxaa_luma_cs.h new file mode 100644 index 000000000..ed86b4d4a --- /dev/null +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_fxaa_luma_cs.h @@ -0,0 +1,413 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeApplyGammaRampConstants +// { +// +// uint2 xe_apply_gamma_size; // Offset: 0 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_apply_gamma_source texture float3 2d T0 t0 1 +// xe_apply_gamma_ramp texture uint2 buf T1 t1 1 +// xe_apply_gamma_dest UAV unorm4 2d U0 u0 1 +// XeApplyGammaRampConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Input +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Output +cs_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_resource_buffer (uint,uint,uint,uint) T1[1:1], space=0 +dcl_uav_typed_texture2d (unorm,unorm,unorm,unorm) U0[0:0], space=0 +dcl_input vThreadID.xy +dcl_temps 3 +dcl_thread_group 8, 8, 1 +uge r0.xy, vThreadID.xyxx, CB0[0][0].xyxx +or r0.x, r0.y, r0.x +if_nz r0.x + ret +endif +mov r0.xy, vThreadID.xyxx +mov r0.zw, l(0,0,0,0) +ld r0.xyz, r0.xyzw, T0[0].xyzw +mad r0.xyz, r0.xyzx, l(1023.000000, 1023.000000, 1023.000000, 0.000000), l(0.500000, 0.500000, 0.500000, 0.000000) +ftou r0.xyz, r0.xyzx +ushr r1.xyz, r0.xyzx, l(3, 3, 3, 0) +imul null, r0.w, r1.z, l(3) +imad r1.xy, r1.xyxx, l(3, 3, 0, 0), l(2, 1, 0, 0) +ld r1.xz, r1.xxxx, T1[1].xzyw +utof r1.x, r1.x +and r0.xyz, r0.xyzx, l(7, 7, 7, 0) +imul null, r0.x, r1.z, r0.x +utof r0.x, r0.x +mad r0.x, r0.x, l(0.125000), r1.x +mul r0.x, r0.x, l(0.000015) +min r2.x, r0.x, l(1.000000) +ld r1.xy, r1.yyyy, T1[1].xyzw +utof r0.x, r1.x +imul null, r0.y, r0.y, r1.y +utof r0.y, r0.y +mad r0.x, r0.y, l(0.125000), r0.x +mul r0.x, r0.x, l(0.000015) +min r2.y, r0.x, l(1.000000) +ld r0.xy, r0.wwww, T1[1].xyzw +imul null, r0.y, r0.y, r0.z +utof r0.xy, r0.xyxx +mad r0.x, r0.y, l(0.125000), r0.x +mul r0.x, r0.x, l(0.000015) +min r2.z, r0.x, l(1.000000) +dp3 r2.w, r2.xyzx, l(0.299000, 0.587000, 0.114000, 0.000000) +store_uav_typed U0[0].xyzw, vThreadID.xyyy, r2.xyzw +ret +// Approximately 37 instruction slots used +#endif + +const BYTE apply_gamma_pwl_fxaa_luma_cs[] = +{ + 68, 88, 66, 67, 165, 122, + 242, 36, 160, 218, 193, 67, + 37, 43, 138, 45, 109, 219, + 226, 109, 1, 0, 0, 0, + 148, 7, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 24, 2, 0, 0, 40, 2, + 0, 0, 56, 2, 0, 0, + 248, 6, 0, 0, 82, 68, + 69, 70, 220, 1, 0, 0, + 1, 0, 0, 0, 52, 1, + 0, 0, 4, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 83, 67, 0, 5, 0, 0, + 180, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 220, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 242, 0, + 0, 0, 2, 0, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 255, 255, 255, 255, + 1, 0, 0, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 6, 1, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 1, 0, 0, 0, + 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 26, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 120, 101, + 95, 97, 112, 112, 108, 121, + 95, 103, 97, 109, 109, 97, + 95, 115, 111, 117, 114, 99, + 101, 0, 120, 101, 95, 97, + 112, 112, 108, 121, 95, 103, + 97, 109, 109, 97, 95, 114, + 97, 109, 112, 0, 120, 101, + 95, 97, 112, 112, 108, 121, + 95, 103, 97, 109, 109, 97, + 95, 100, 101, 115, 116, 0, + 88, 101, 65, 112, 112, 108, + 121, 71, 97, 109, 109, 97, + 82, 97, 109, 112, 67, 111, + 110, 115, 116, 97, 110, 116, + 115, 0, 26, 1, 0, 0, + 1, 0, 0, 0, 76, 1, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 116, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 144, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 120, 101, 95, 97, 112, 112, + 108, 121, 95, 103, 97, 109, + 109, 97, 95, 115, 105, 122, + 101, 0, 117, 105, 110, 116, + 50, 0, 171, 171, 1, 0, + 19, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 136, 1, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 8, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 79, 83, 71, 78, 8, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 83, 72, + 69, 88, 184, 4, 0, 0, + 81, 0, 5, 0, 46, 1, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 88, 8, 0, 7, + 70, 126, 48, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 68, 68, + 0, 0, 0, 0, 0, 0, + 156, 24, 0, 7, 70, 238, + 49, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 17, 17, 0, 0, + 0, 0, 0, 0, 95, 0, + 0, 2, 50, 0, 2, 0, + 104, 0, 0, 2, 3, 0, + 0, 0, 155, 0, 0, 4, + 8, 0, 0, 0, 8, 0, + 0, 0, 1, 0, 0, 0, + 80, 0, 0, 8, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 2, 0, 70, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 60, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 0, 0, 0, 0, + 62, 0, 0, 1, 21, 0, + 0, 1, 54, 0, 0, 4, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 2, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 15, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 192, + 127, 68, 0, 192, 127, 68, + 0, 192, 127, 68, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 0, 28, 0, + 0, 5, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 85, 0, 0, 10, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 38, 0, + 0, 8, 0, 208, 0, 0, + 130, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 3, 0, 0, 0, + 35, 0, 0, 15, 50, 0, + 16, 0, 1, 0, 0, 0, + 70, 0, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 82, 0, + 16, 0, 1, 0, 0, 0, + 6, 0, 16, 0, 1, 0, + 0, 0, 134, 125, 32, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 86, 0, 0, 5, + 18, 0, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 1, 0, 0, 0, 1, 0, + 0, 10, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 7, 0, + 0, 0, 7, 0, 0, 0, + 7, 0, 0, 0, 0, 0, + 0, 0, 38, 0, 0, 8, + 0, 208, 0, 0, 18, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 86, 0, + 0, 5, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 62, 10, 0, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 8, 32, 128, 55, 51, 0, + 0, 7, 18, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 45, 0, 0, 8, + 50, 0, 16, 0, 1, 0, + 0, 0, 86, 5, 16, 0, + 1, 0, 0, 0, 70, 126, + 32, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 86, 0, + 0, 5, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 1, 0, 0, 0, + 38, 0, 0, 8, 0, 208, + 0, 0, 34, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 1, 0, + 0, 0, 86, 0, 0, 5, + 34, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 18, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 62, 10, 0, 16, 0, + 0, 0, 0, 0, 56, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 8, 32, + 128, 55, 51, 0, 0, 7, + 34, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 45, 0, 0, 8, 50, 0, + 16, 0, 0, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 38, 0, 0, 8, + 0, 208, 0, 0, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 86, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 62, 10, 0, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 8, 32, 128, 55, 51, 0, + 0, 7, 66, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 16, 0, 0, 10, + 130, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 135, 22, 153, 62, + 162, 69, 22, 63, 213, 120, + 233, 61, 0, 0, 0, 0, + 164, 0, 0, 7, 242, 224, + 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 70, 5, + 2, 0, 70, 14, 16, 0, + 2, 0, 0, 0, 62, 0, + 0, 1, 83, 84, 65, 84, + 148, 0, 0, 0, 37, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 11, 0, 0, 0, + 5, 0, 0, 0, 4, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 6, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0 +}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_cs.h new file mode 100644 index 000000000..a890e8f48 --- /dev/null +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_cs.h @@ -0,0 +1,281 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeApplyGammaRampConstants +// { +// +// uint2 xe_apply_gamma_size; // Offset: 0 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_apply_gamma_source texture float3 2d T0 t0 1 +// xe_apply_gamma_ramp texture float3 buf T1 t1 1 +// xe_apply_gamma_dest UAV unorm4 2d U0 u0 1 +// XeApplyGammaRampConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Input +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Output +cs_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_resource_buffer (float,float,float,float) T1[1:1], space=0 +dcl_uav_typed_texture2d (unorm,unorm,unorm,unorm) U0[0:0], space=0 +dcl_input vThreadID.xy +dcl_temps 2 +dcl_thread_group 8, 8, 1 +uge r0.xy, vThreadID.xyxx, CB0[0][0].xyxx +or r0.x, r0.y, r0.x +if_nz r0.x + ret +endif +mov r0.xy, vThreadID.xyxx +mov r0.zw, l(0,0,0,0) +ld r0.xyz, r0.xyzw, T0[0].xyzw +mad r0.xyz, r0.xyzx, l(255.000000, 255.000000, 255.000000, 0.000000), l(0.500000, 0.500000, 0.500000, 0.000000) +ftou r0.xyz, r0.xyzx +ld r1.x, r0.xxxx, T1[1].zxyw +ld r1.y, r0.yyyy, T1[1].xyzw +ld r1.z, r0.zzzz, T1[1].yzxw +mov r1.w, l(1.000000) +store_uav_typed U0[0].xyzw, vThreadID.xyyy, r1.xyzw +ret +// Approximately 16 instruction slots used +#endif + +const BYTE apply_gamma_table_cs[] = +{ + 68, 88, 66, 67, 20, 63, + 31, 100, 63, 232, 227, 64, + 21, 8, 34, 27, 205, 36, + 202, 71, 1, 0, 0, 0, + 252, 4, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 24, 2, 0, 0, 40, 2, + 0, 0, 56, 2, 0, 0, + 96, 4, 0, 0, 82, 68, + 69, 70, 220, 1, 0, 0, + 1, 0, 0, 0, 52, 1, + 0, 0, 4, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 83, 67, 0, 5, 0, 0, + 180, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 220, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 242, 0, + 0, 0, 2, 0, 0, 0, + 5, 0, 0, 0, 1, 0, + 0, 0, 255, 255, 255, 255, + 1, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 6, 1, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 1, 0, 0, 0, + 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 26, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 120, 101, + 95, 97, 112, 112, 108, 121, + 95, 103, 97, 109, 109, 97, + 95, 115, 111, 117, 114, 99, + 101, 0, 120, 101, 95, 97, + 112, 112, 108, 121, 95, 103, + 97, 109, 109, 97, 95, 114, + 97, 109, 112, 0, 120, 101, + 95, 97, 112, 112, 108, 121, + 95, 103, 97, 109, 109, 97, + 95, 100, 101, 115, 116, 0, + 88, 101, 65, 112, 112, 108, + 121, 71, 97, 109, 109, 97, + 82, 97, 109, 112, 67, 111, + 110, 115, 116, 97, 110, 116, + 115, 0, 26, 1, 0, 0, + 1, 0, 0, 0, 76, 1, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 116, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 144, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 120, 101, 95, 97, 112, 112, + 108, 121, 95, 103, 97, 109, + 109, 97, 95, 115, 105, 122, + 101, 0, 117, 105, 110, 116, + 50, 0, 171, 171, 1, 0, + 19, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 136, 1, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 8, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 79, 83, 71, 78, 8, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 83, 72, + 69, 88, 32, 2, 0, 0, + 81, 0, 5, 0, 136, 0, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 88, 8, 0, 7, + 70, 126, 48, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 85, 85, + 0, 0, 0, 0, 0, 0, + 156, 24, 0, 7, 70, 238, + 49, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 17, 17, 0, 0, + 0, 0, 0, 0, 95, 0, + 0, 2, 50, 0, 2, 0, + 104, 0, 0, 2, 2, 0, + 0, 0, 155, 0, 0, 4, + 8, 0, 0, 0, 8, 0, + 0, 0, 1, 0, 0, 0, + 80, 0, 0, 8, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 2, 0, 70, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 60, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 0, 0, 0, 0, + 62, 0, 0, 1, 21, 0, + 0, 1, 54, 0, 0, 4, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 2, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 15, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 127, 67, 0, 0, 127, 67, + 0, 0, 127, 67, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 0, 28, 0, + 0, 5, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 18, 0, + 16, 0, 1, 0, 0, 0, + 6, 0, 16, 0, 0, 0, + 0, 0, 38, 125, 32, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 45, 0, 0, 8, + 34, 0, 16, 0, 1, 0, + 0, 0, 86, 5, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 45, 0, + 0, 8, 66, 0, 16, 0, + 1, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 150, 124, 32, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 54, 0, 0, 5, 130, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 164, 0, 0, 7, + 242, 224, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 70, 5, 2, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 62, 0, 0, 1, 83, 84, + 65, 84, 148, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 2, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0 +}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_fxaa_luma_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_fxaa_luma_cs.h new file mode 100644 index 000000000..4373d8498 --- /dev/null +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_table_fxaa_luma_cs.h @@ -0,0 +1,284 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeApplyGammaRampConstants +// { +// +// uint2 xe_apply_gamma_size; // Offset: 0 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_apply_gamma_source texture float3 2d T0 t0 1 +// xe_apply_gamma_ramp texture float3 buf T1 t1 1 +// xe_apply_gamma_dest UAV unorm4 2d U0 u0 1 +// XeApplyGammaRampConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Input +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Output +cs_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_resource_buffer (float,float,float,float) T1[1:1], space=0 +dcl_uav_typed_texture2d (unorm,unorm,unorm,unorm) U0[0:0], space=0 +dcl_input vThreadID.xy +dcl_temps 2 +dcl_thread_group 8, 8, 1 +uge r0.xy, vThreadID.xyxx, CB0[0][0].xyxx +or r0.x, r0.y, r0.x +if_nz r0.x + ret +endif +mov r0.xy, vThreadID.xyxx +mov r0.zw, l(0,0,0,0) +ld r0.xyz, r0.xyzw, T0[0].xyzw +mad r0.xyz, r0.xyzx, l(255.000000, 255.000000, 255.000000, 0.000000), l(0.500000, 0.500000, 0.500000, 0.000000) +ftou r0.xyz, r0.xyzx +ld r1.x, r0.xxxx, T1[1].zxyw +ld r1.y, r0.yyyy, T1[1].xyzw +ld r1.z, r0.zzzz, T1[1].yzxw +dp3 r1.w, r1.xyzx, l(0.299000, 0.587000, 0.114000, 0.000000) +store_uav_typed U0[0].xyzw, vThreadID.xyyy, r1.xyzw +ret +// Approximately 16 instruction slots used +#endif + +const BYTE apply_gamma_table_fxaa_luma_cs[] = +{ + 68, 88, 66, 67, 148, 92, + 39, 196, 202, 33, 41, 82, + 77, 137, 192, 188, 150, 218, + 30, 64, 1, 0, 0, 0, + 16, 5, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 24, 2, 0, 0, 40, 2, + 0, 0, 56, 2, 0, 0, + 116, 4, 0, 0, 82, 68, + 69, 70, 220, 1, 0, 0, + 1, 0, 0, 0, 52, 1, + 0, 0, 4, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 83, 67, 0, 5, 0, 0, + 180, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 220, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 242, 0, + 0, 0, 2, 0, 0, 0, + 5, 0, 0, 0, 1, 0, + 0, 0, 255, 255, 255, 255, + 1, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 6, 1, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 1, 0, 0, 0, + 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 26, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 120, 101, + 95, 97, 112, 112, 108, 121, + 95, 103, 97, 109, 109, 97, + 95, 115, 111, 117, 114, 99, + 101, 0, 120, 101, 95, 97, + 112, 112, 108, 121, 95, 103, + 97, 109, 109, 97, 95, 114, + 97, 109, 112, 0, 120, 101, + 95, 97, 112, 112, 108, 121, + 95, 103, 97, 109, 109, 97, + 95, 100, 101, 115, 116, 0, + 88, 101, 65, 112, 112, 108, + 121, 71, 97, 109, 109, 97, + 82, 97, 109, 112, 67, 111, + 110, 115, 116, 97, 110, 116, + 115, 0, 26, 1, 0, 0, + 1, 0, 0, 0, 76, 1, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 116, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 144, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 120, 101, 95, 97, 112, 112, + 108, 121, 95, 103, 97, 109, + 109, 97, 95, 115, 105, 122, + 101, 0, 117, 105, 110, 116, + 50, 0, 171, 171, 1, 0, + 19, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 136, 1, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 8, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 79, 83, 71, 78, 8, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 83, 72, + 69, 88, 52, 2, 0, 0, + 81, 0, 5, 0, 141, 0, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 88, 8, 0, 7, + 70, 126, 48, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 85, 85, + 0, 0, 0, 0, 0, 0, + 156, 24, 0, 7, 70, 238, + 49, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 17, 17, 0, 0, + 0, 0, 0, 0, 95, 0, + 0, 2, 50, 0, 2, 0, + 104, 0, 0, 2, 2, 0, + 0, 0, 155, 0, 0, 4, + 8, 0, 0, 0, 8, 0, + 0, 0, 1, 0, 0, 0, + 80, 0, 0, 8, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 2, 0, 70, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 60, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 0, 0, 0, 0, + 62, 0, 0, 1, 21, 0, + 0, 1, 54, 0, 0, 4, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 2, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 15, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 127, 67, 0, 0, 127, 67, + 0, 0, 127, 67, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 0, 28, 0, + 0, 5, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 18, 0, + 16, 0, 1, 0, 0, 0, + 6, 0, 16, 0, 0, 0, + 0, 0, 38, 125, 32, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 45, 0, 0, 8, + 34, 0, 16, 0, 1, 0, + 0, 0, 86, 5, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 45, 0, + 0, 8, 66, 0, 16, 0, + 1, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 150, 124, 32, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 16, 0, 0, 10, 130, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 135, 22, 153, 62, 162, 69, + 22, 63, 213, 120, 233, 61, + 0, 0, 0, 0, 164, 0, + 0, 7, 242, 224, 33, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 70, 5, 2, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 62, 0, 0, 1, + 83, 84, 65, 84, 148, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0 +}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/clear_uint2_ps.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/clear_uint2_ps.h index 12d936ccf..8bf1a7d63 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/clear_uint2_ps.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/clear_uint2_ps.h @@ -44,10 +44,10 @@ ret const BYTE clear_uint2_ps[] = { - 68, 88, 66, 67, 37, 23, - 17, 1, 102, 148, 181, 42, - 241, 102, 112, 167, 142, 147, - 73, 7, 1, 0, 0, 0, + 68, 88, 66, 67, 90, 79, + 67, 15, 17, 175, 210, 170, + 189, 222, 209, 228, 62, 31, + 153, 246, 1, 0, 0, 0, 148, 2, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 88, 1, 0, 0, 104, 1, @@ -57,7 +57,7 @@ const BYTE clear_uint2_ps[] = 1, 0, 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 255, 255, 0, 5, 4, 0, + 255, 255, 0, 5, 0, 0, 244, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_quad_hs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_quad_hs.h index cba1c29ab..9969afd5f 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_quad_hs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_quad_hs.h @@ -121,10 +121,10 @@ ret const BYTE continuous_quad_hs[] = { - 68, 88, 66, 67, 109, 174, - 137, 67, 82, 213, 246, 48, - 97, 232, 232, 104, 109, 96, - 97, 35, 1, 0, 0, 0, + 68, 88, 66, 67, 38, 148, + 187, 235, 226, 45, 172, 116, + 78, 244, 151, 91, 91, 131, + 203, 9, 1, 0, 0, 0, 240, 13, 0, 0, 6, 0, 0, 0, 56, 0, 0, 0, 188, 10, 0, 0, 240, 10, @@ -135,7 +135,7 @@ const BYTE continuous_quad_hs[] = 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, 83, 72, - 0, 5, 4, 0, 82, 10, + 0, 5, 0, 0, 82, 10, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_triangle_hs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_triangle_hs.h index afd8a3347..7ebd3f557 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_triangle_hs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/continuous_triangle_hs.h @@ -112,10 +112,10 @@ ret const BYTE continuous_triangle_hs[] = { - 68, 88, 66, 67, 150, 55, - 56, 98, 126, 111, 114, 179, - 197, 7, 10, 15, 70, 32, - 249, 93, 1, 0, 0, 0, + 68, 88, 66, 67, 73, 225, + 250, 199, 94, 238, 81, 135, + 38, 37, 240, 107, 243, 39, + 228, 138, 1, 0, 0, 0, 96, 13, 0, 0, 6, 0, 0, 0, 56, 0, 0, 0, 188, 10, 0, 0, 240, 10, @@ -126,7 +126,7 @@ const BYTE continuous_triangle_hs[] = 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, 83, 72, - 0, 5, 4, 0, 82, 10, + 0, 5, 0, 0, 82, 10, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_quad_hs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_quad_hs.h index 53a0af447..3fb7e283f 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_quad_hs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_quad_hs.h @@ -121,10 +121,10 @@ ret const BYTE discrete_quad_hs[] = { - 68, 88, 66, 67, 174, 141, - 75, 94, 70, 108, 133, 212, - 18, 161, 84, 115, 238, 199, - 1, 25, 1, 0, 0, 0, + 68, 88, 66, 67, 30, 187, + 71, 171, 142, 3, 72, 194, + 131, 236, 131, 25, 126, 147, + 165, 207, 1, 0, 0, 0, 240, 13, 0, 0, 6, 0, 0, 0, 56, 0, 0, 0, 188, 10, 0, 0, 240, 10, @@ -135,7 +135,7 @@ const BYTE discrete_quad_hs[] = 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, 83, 72, - 0, 5, 4, 0, 82, 10, + 0, 5, 0, 0, 82, 10, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_triangle_hs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_triangle_hs.h index d5855480a..4d4118deb 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_triangle_hs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/discrete_triangle_hs.h @@ -112,10 +112,10 @@ ret const BYTE discrete_triangle_hs[] = { - 68, 88, 66, 67, 100, 231, - 203, 227, 24, 0, 204, 209, - 145, 54, 166, 75, 102, 255, - 190, 98, 1, 0, 0, 0, + 68, 88, 66, 67, 59, 166, + 23, 194, 170, 244, 84, 16, + 156, 139, 156, 141, 147, 143, + 102, 19, 1, 0, 0, 0, 96, 13, 0, 0, 6, 0, 0, 0, 56, 0, 0, 0, 188, 10, 0, 0, 240, 10, @@ -126,7 +126,7 @@ const BYTE discrete_triangle_hs[] = 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, 83, 72, - 0, 5, 4, 0, 82, 10, + 0, 5, 0, 0, 82, 10, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/float24_round_ps.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/float24_round_ps.h index ca93f0d29..8a82eb0e7 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/float24_round_ps.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/float24_round_ps.h @@ -76,10 +76,10 @@ ret const BYTE float24_round_ps[] = { - 68, 88, 66, 67, 23, 65, - 158, 4, 125, 87, 131, 240, - 183, 241, 233, 246, 170, 100, - 46, 3, 1, 0, 0, 0, + 68, 88, 66, 67, 229, 54, + 46, 1, 194, 31, 164, 202, + 193, 71, 175, 129, 44, 52, + 218, 154, 1, 0, 0, 0, 8, 7, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 160, 0, 0, 0, 120, 2, @@ -89,7 +89,7 @@ const BYTE float24_round_ps[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 255, 255, 0, 5, 4, 0, + 255, 255, 0, 5, 0, 0, 60, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/float24_truncate_ps.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/float24_truncate_ps.h index 1111cd47f..91eb48136 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/float24_truncate_ps.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/float24_truncate_ps.h @@ -58,10 +58,10 @@ ret const BYTE float24_truncate_ps[] = { - 68, 88, 66, 67, 73, 81, - 190, 30, 130, 230, 10, 4, - 35, 6, 194, 2, 204, 207, - 200, 64, 1, 0, 0, 0, + 68, 88, 66, 67, 234, 72, + 187, 196, 185, 155, 10, 179, + 119, 204, 17, 88, 131, 142, + 107, 205, 1, 0, 0, 0, 140, 4, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 160, 0, 0, 0, 120, 2, @@ -71,7 +71,7 @@ const BYTE float24_truncate_ps[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 255, 255, 0, 5, 4, 0, + 255, 255, 0, 5, 0, 0, 60, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_tc_vs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_tc_vs.h deleted file mode 100644 index c14aebabf..000000000 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_tc_vs.h +++ /dev/null @@ -1,166 +0,0 @@ -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_VertexID 0 x 0 VERTID uint x -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// SV_Position 0 xyzw 1 POS float xyzw -// -vs_5_1 -dcl_globalFlags refactoringAllowed -dcl_input_sgv v0.x, vertex_id -dcl_output o0.xy -dcl_output_siv o1.xyzw, position -dcl_temps 1 -ishl r0.y, v0.x, l(1) -mov r0.x, v0.x -and r0.xy, r0.xyxx, l(2, 2, 0, 0) -utof r0.xy, r0.xyxx -mad o1.xy, r0.xyxx, l(2.000000, -2.000000, 0.000000, 0.000000), l(-1.000000, 1.000000, 0.000000, 0.000000) -mov o1.zw, l(0,0,0,1.000000) -mov o0.xy, r0.xyxx -ret -// Approximately 8 instruction slots used -#endif - -const BYTE fullscreen_tc_vs[] = -{ - 68, 88, 66, 67, 194, 19, - 91, 0, 93, 21, 209, 27, - 252, 153, 187, 171, 84, 80, - 251, 138, 1, 0, 0, 0, - 240, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 160, 0, 0, 0, 212, 0, - 0, 0, 44, 1, 0, 0, - 84, 2, 0, 0, 82, 68, - 69, 70, 100, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 60, 0, 0, 0, 1, 5, - 254, 255, 0, 5, 4, 0, - 60, 0, 0, 0, 19, 19, - 68, 37, 60, 0, 0, 0, - 24, 0, 0, 0, 40, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 77, 105, 99, 114, 111, 115, - 111, 102, 116, 32, 40, 82, - 41, 32, 72, 76, 83, 76, - 32, 83, 104, 97, 100, 101, - 114, 32, 67, 111, 109, 112, - 105, 108, 101, 114, 32, 49, - 48, 46, 49, 0, 73, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 6, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 1, - 0, 0, 83, 86, 95, 86, - 101, 114, 116, 101, 120, 73, - 68, 0, 79, 83, 71, 78, - 80, 0, 0, 0, 2, 0, - 0, 0, 8, 0, 0, 0, - 56, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 3, 12, 0, 0, - 65, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 15, 0, 0, 0, - 84, 69, 88, 67, 79, 79, - 82, 68, 0, 83, 86, 95, - 80, 111, 115, 105, 116, 105, - 111, 110, 0, 171, 171, 171, - 83, 72, 69, 88, 32, 1, - 0, 0, 81, 0, 1, 0, - 72, 0, 0, 0, 106, 8, - 0, 1, 96, 0, 0, 4, - 18, 16, 16, 0, 0, 0, - 0, 0, 6, 0, 0, 0, - 101, 0, 0, 3, 50, 32, - 16, 0, 0, 0, 0, 0, - 103, 0, 0, 4, 242, 32, - 16, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 104, 0, - 0, 2, 1, 0, 0, 0, - 41, 0, 0, 7, 34, 0, - 16, 0, 0, 0, 0, 0, - 10, 16, 16, 0, 0, 0, - 0, 0, 1, 64, 0, 0, - 1, 0, 0, 0, 54, 0, - 0, 5, 18, 0, 16, 0, - 0, 0, 0, 0, 10, 16, - 16, 0, 0, 0, 0, 0, - 1, 0, 0, 10, 50, 0, - 16, 0, 0, 0, 0, 0, - 70, 0, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 2, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 86, 0, - 0, 5, 50, 0, 16, 0, - 0, 0, 0, 0, 70, 0, - 16, 0, 0, 0, 0, 0, - 50, 0, 0, 15, 50, 32, - 16, 0, 1, 0, 0, 0, - 70, 0, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 0, 64, 0, 0, - 0, 192, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 0, 0, 128, 191, - 0, 0, 128, 63, 0, 0, - 0, 0, 0, 0, 0, 0, - 54, 0, 0, 8, 194, 32, - 16, 0, 1, 0, 0, 0, - 2, 64, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 128, 63, 54, 0, 0, 5, - 50, 32, 16, 0, 0, 0, - 0, 0, 70, 0, 16, 0, - 0, 0, 0, 0, 62, 0, - 0, 1, 83, 84, 65, 84, - 148, 0, 0, 0, 8, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0 -}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_vs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_vs.h index f12247ce4..5a388c0a8 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_vs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fullscreen_vs.h @@ -34,10 +34,10 @@ ret const BYTE fullscreen_vs[] = { - 68, 88, 66, 67, 11, 43, - 171, 43, 233, 188, 20, 107, - 115, 148, 0, 215, 116, 165, - 194, 26, 1, 0, 0, 0, + 68, 88, 66, 67, 111, 71, + 234, 43, 238, 70, 168, 114, + 17, 145, 139, 145, 152, 124, + 190, 180, 1, 0, 0, 0, 172, 2, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 160, 0, 0, 0, 212, 0, @@ -47,7 +47,7 @@ const BYTE fullscreen_vs[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 254, 255, 0, 5, 4, 0, + 254, 255, 0, 5, 0, 0, 60, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_cs.h new file mode 100644 index 000000000..8ca505c28 --- /dev/null +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_cs.h @@ -0,0 +1,1270 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeApplyGammaRampConstants +// { +// +// uint2 xe_fxaa_size; // Offset: 0 Size: 8 +// float2 xe_fxaa_size_inv; // Offset: 8 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_sampler_linear_clamp sampler NA NA S0 s0 1 +// xe_fxaa_source texture float4 2d T0 t0 1 +// xe_fxaa_dest UAV unorm4 2d U0 u0 1 +// XeApplyGammaRampConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Input +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Output +cs_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_sampler S0[0:0], mode_default, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_uav_typed_texture2d (unorm,unorm,unorm,unorm) U0[0:0], space=0 +dcl_input vThreadID.xy +dcl_temps 7 +dcl_thread_group 8, 8, 1 +uge r0.xy, vThreadID.xyxx, CB0[0][0].xyxx +or r0.x, r0.y, r0.x +if_nz r0.x + ret +endif +utof r0.xy, vThreadID.xyxx +add r0.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000) +mul r0.zw, r0.xxxy, CB0[0][0].zzzw +sample_l r1.xyzw, r0.zwzz, T0[0].xyzw, S0[0], l(0.000000) +gather4 r2.xyz, r0.zwzz, T0[0].xyzw, S0[0].w +gather4_aoffimmi(-1,-1,0) r3.xyz, r0.zwzz, T0[0].zxwy, S0[0].w +max r2.w, r1.w, r2.x +min r3.w, r1.w, r2.x +max r2.w, r2.w, r2.z +min r3.w, r2.z, r3.w +max r4.x, r3.y, r3.x +min r4.y, r3.y, r3.x +max r2.w, r2.w, r4.x +min r3.w, r3.w, r4.y +mul r4.x, r2.w, l(0.166000) +add r2.w, r2.w, -r3.w +max r3.w, r4.x, l(0.083300) +lt r3.w, r2.w, r3.w +if_z r3.w +endif +if_z r3.w + sample_l_aoffimmi(1,-1,0) r3.w, r0.zwzz, T0[0].xyzw, S0[0], l(0.000000) + sample_l_aoffimmi(-1,1,0) r4.x, r0.zwzz, T0[0].wxyz, S0[0], l(0.000000) + add r4.yz, r2.xxzx, r3.xxyx + div r2.w, l(1.000000, 1.000000, 1.000000, 1.000000), r2.w + add r4.w, r4.z, r4.y + mad r4.yz, r1.wwww, l(0.000000, -2.000000, -2.000000, 0.000000), r4.yyzy + add r5.x, r2.y, r3.w + add r3.w, r3.w, r3.z + mad r5.y, r2.z, l(-2.000000), r5.x + mad r3.w, r3.x, l(-2.000000), r3.w + add r3.z, r3.z, r4.x + add r2.y, r2.y, r4.x + mad r4.x, |r4.y|, l(2.000000), |r5.y| + mad r3.w, |r4.z|, l(2.000000), |r3.w| + mad r4.y, r3.y, l(-2.000000), r3.z + mad r2.y, r2.x, l(-2.000000), r2.y + add r4.x, r4.x, |r4.y| + add r2.y, r3.w, |r2.y| + add r3.z, r5.x, r3.z + ge r2.y, r4.x, r2.y + mad r3.z, r4.w, l(2.000000), r3.z + if_z r2.y + mov r3.x, r3.y + mov r2.x, r2.z + endif + if_nz r2.y + mov r2.z, CB0[0][0].w + else + mov r2.z, CB0[0][0].z + endif + mad r3.y, r3.z, l(0.083333), -r1.w + add r3.z, -r1.w, r3.x + add r3.w, -r1.w, r2.x + ge r4.x, |r3.z|, |r3.w| + max r3.z, |r3.w|, |r3.z| + if_nz r4.x + mov r2.z, -r2.z + endif + mul_sat r2.w, r2.w, |r3.y| + and r3.y, r2.y, CB0[0][0].z + movc r3.w, r2.y, l(0), CB0[0][0].w + if_z r2.y + mad r4.y, r2.z, l(0.500000), r0.z + else + mov r4.y, r0.z + endif + if_nz r2.y + mad r4.z, r2.z, l(0.500000), r0.w + else + mov r4.z, r0.w + endif + add r5.xy, -r3.ywyy, r4.yzyy + add r4.yw, r3.yyyw, r4.yyyz + mad r4.z, r2.w, l(-2.000000), l(3.000000) + sample_l r5.z, r5.xyxx, T0[0].xywz, S0[0], l(0.000000) + mul r2.w, r2.w, r2.w + sample_l r5.w, r4.ywyy, T0[0].xyzw, S0[0], l(0.000000) + if_z r4.x + add r3.x, r1.w, r2.x + else + add r3.x, r1.w, r3.x + endif + mul r2.x, r3.z, l(0.250000) + mad r3.z, -r3.x, l(0.500000), r1.w + mul r2.w, r2.w, r4.z + lt r3.z, r3.z, l(0.000000) + mad r6.x, -r3.x, l(0.500000), r5.z + mad r6.y, -r3.x, l(0.500000), r5.w + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(1.500000), r5.x + mad r5.y, -r3.w, l(1.500000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(1.500000), r4.y + mad r4.w, r3.w, l(1.500000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(2.000000), r5.x + mad r5.y, -r3.w, l(2.000000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(2.000000), r4.y + mad r4.w, r3.w, l(2.000000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(4.000000), r5.x + mad r5.y, -r3.w, l(4.000000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(4.000000), r4.y + mad r4.w, r3.w, l(4.000000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(12.000000), r5.x + mad r5.y, -r3.w, l(12.000000), r5.y + endif + if_nz r4.z + mad r4.y, r3.y, l(12.000000), r4.y + mad r4.w, r3.w, l(12.000000), r4.w + endif + endif + endif + endif + if_z r2.y + mad r2.x, r0.y, CB0[0][0].w, -r5.y + mad r0.x, -r0.y, CB0[0][0].w, r4.w + else + mad r2.x, r0.x, CB0[0][0].z, -r5.x + mad r0.x, -r0.x, CB0[0][0].z, r4.y + endif + lt r3.xy, r6.xyxx, l(0.000000, 0.000000, 0.000000, 0.000000) + add r0.y, r2.x, r0.x + ine r3.xy, r3.zzzz, r3.xyxx + div r0.y, l(1.000000, 1.000000, 1.000000, 1.000000), r0.y + lt r3.z, r2.x, r0.x + min r0.x, r0.x, r2.x + movc r2.x, r3.z, r3.x, r3.y + mul r2.w, r2.w, r2.w + mad r0.x, r0.x, -r0.y, l(0.500000) + mul r0.y, r2.w, l(0.750000) + and r0.x, r0.x, r2.x + max r0.x, r0.y, r0.x + if_z r2.y + mad r0.z, r0.x, r2.z, r0.z + endif + if_nz r2.y + mad r0.w, r0.x, r2.z, r0.w + endif + sample_l r1.xyz, r0.zwzz, T0[0].xyzw, S0[0], l(0.000000) +endif +mov r1.w, l(1.000000) +store_uav_typed U0[0].xyzw, vThreadID.xyyy, r1.xyzw +ret +// Approximately 205 instruction slots used +#endif + +const BYTE fxaa_cs[] = +{ + 68, 88, 66, 67, 146, 66, + 17, 144, 231, 237, 183, 236, + 231, 78, 100, 194, 80, 51, + 78, 217, 1, 0, 0, 0, + 184, 23, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 108, 2, 0, 0, 124, 2, + 0, 0, 140, 2, 0, 0, + 28, 23, 0, 0, 82, 68, + 69, 70, 48, 2, 0, 0, + 1, 0, 0, 0, 44, 1, + 0, 0, 4, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 83, 67, 0, 5, 0, 0, + 8, 2, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 220, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 244, 0, + 0, 0, 2, 0, 0, 0, + 5, 0, 0, 0, 4, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 1, 0, + 0, 0, 12, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 1, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 1, 0, 0, 0, + 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 16, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 120, 101, + 95, 115, 97, 109, 112, 108, + 101, 114, 95, 108, 105, 110, + 101, 97, 114, 95, 99, 108, + 97, 109, 112, 0, 120, 101, + 95, 102, 120, 97, 97, 95, + 115, 111, 117, 114, 99, 101, + 0, 120, 101, 95, 102, 120, + 97, 97, 95, 100, 101, 115, + 116, 0, 88, 101, 65, 112, + 112, 108, 121, 71, 97, 109, + 109, 97, 82, 97, 109, 112, + 67, 111, 110, 115, 116, 97, + 110, 116, 115, 0, 171, 171, + 16, 1, 0, 0, 2, 0, + 0, 0, 68, 1, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 148, 1, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 2, 0, 0, 0, 168, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 204, 1, + 0, 0, 8, 0, 0, 0, + 8, 0, 0, 0, 2, 0, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 120, 101, 95, 102, + 120, 97, 97, 95, 115, 105, + 122, 101, 0, 117, 105, 110, + 116, 50, 0, 171, 1, 0, + 19, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 161, 1, 0, 0, 120, 101, + 95, 102, 120, 97, 97, 95, + 115, 105, 122, 101, 95, 105, + 110, 118, 0, 102, 108, 111, + 97, 116, 50, 0, 1, 0, + 3, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 221, 1, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 8, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 79, 83, 71, 78, 8, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 83, 72, + 69, 88, 136, 20, 0, 0, + 81, 0, 5, 0, 34, 5, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 90, 0, + 0, 6, 70, 110, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 156, 24, 0, 7, + 70, 238, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 17, + 0, 0, 0, 0, 0, 0, + 95, 0, 0, 2, 50, 0, + 2, 0, 104, 0, 0, 2, + 7, 0, 0, 0, 155, 0, + 0, 4, 8, 0, 0, 0, + 8, 0, 0, 0, 1, 0, + 0, 0, 80, 0, 0, 8, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 2, 0, + 70, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 60, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 0, 0, + 0, 0, 62, 0, 0, 1, + 21, 0, 0, 1, 86, 0, + 0, 4, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 2, 0, 0, 0, 0, 10, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 63, + 0, 0, 0, 63, 0, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 9, 194, 0, + 16, 0, 0, 0, 0, 0, + 6, 4, 16, 0, 0, 0, + 0, 0, 166, 142, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 72, 0, 0, 13, 242, 0, + 16, 0, 1, 0, 0, 0, + 230, 10, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 109, 0, + 0, 11, 114, 0, 16, 0, + 2, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 58, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 109, 0, 0, 140, 1, 254, + 1, 0, 114, 0, 16, 0, + 3, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 38, 119, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 58, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 10, 0, 16, 0, 2, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 2, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 3, 0, 0, 0, + 42, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 52, 0, + 0, 7, 18, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 10, 0, 16, 0, 3, 0, + 0, 0, 51, 0, 0, 7, + 34, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 0, 3, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 56, 0, 0, 7, + 18, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 231, 251, 41, 62, + 0, 0, 0, 8, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 49, 153, 170, 61, + 49, 0, 0, 7, 130, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 31, 0, + 0, 3, 58, 0, 16, 0, + 3, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 0, 3, + 58, 0, 16, 0, 3, 0, + 0, 0, 72, 0, 0, 142, + 1, 226, 1, 0, 130, 0, + 16, 0, 3, 0, 0, 0, + 230, 10, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 72, 0, + 0, 142, 1, 62, 0, 0, + 18, 0, 16, 0, 4, 0, + 0, 0, 230, 10, 16, 0, + 0, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 98, 0, + 16, 0, 4, 0, 0, 0, + 6, 2, 16, 0, 2, 0, + 0, 0, 6, 1, 16, 0, + 3, 0, 0, 0, 14, 0, + 0, 10, 130, 0, 16, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 128, 63, 0, 0, 128, 63, + 58, 0, 16, 0, 2, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 4, 0, + 0, 0, 42, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 12, 98, 0, + 16, 0, 4, 0, 0, 0, + 246, 15, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 192, 0, 0, 0, 192, + 0, 0, 0, 0, 86, 6, + 16, 0, 4, 0, 0, 0, + 0, 0, 0, 7, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 0, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 42, 0, 16, 0, 3, 0, + 0, 0, 50, 0, 0, 9, + 34, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 10, 0, 16, 0, 5, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 3, 0, + 0, 0, 10, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 58, 0, 16, 0, 3, 0, + 0, 0, 0, 0, 0, 7, + 66, 0, 16, 0, 3, 0, + 0, 0, 42, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 0, 0, 0, 7, 34, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 11, 18, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 128, 129, 0, 0, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 26, 0, 16, 128, 129, 0, + 0, 0, 5, 0, 0, 0, + 50, 0, 0, 11, 130, 0, + 16, 0, 3, 0, 0, 0, + 42, 0, 16, 128, 129, 0, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 58, 0, 16, 128, + 129, 0, 0, 0, 3, 0, + 0, 0, 50, 0, 0, 9, + 34, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 42, 0, 16, 0, 3, 0, + 0, 0, 50, 0, 0, 9, + 34, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 26, 0, 16, 0, 2, 0, + 0, 0, 0, 0, 0, 8, + 18, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 128, 129, 0, 0, 0, + 4, 0, 0, 0, 0, 0, + 0, 8, 34, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 26, 0, 16, 128, 129, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 7, 66, 0, + 16, 0, 3, 0, 0, 0, + 10, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 3, 0, 0, 0, 29, 0, + 0, 7, 34, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 66, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 42, 0, 16, 0, 3, 0, + 0, 0, 31, 0, 0, 3, + 26, 0, 16, 0, 2, 0, + 0, 0, 54, 0, 0, 5, + 18, 0, 16, 0, 3, 0, + 0, 0, 26, 0, 16, 0, + 3, 0, 0, 0, 54, 0, + 0, 5, 18, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 2, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 26, 0, 16, 0, + 2, 0, 0, 0, 54, 0, + 0, 7, 66, 0, 16, 0, + 2, 0, 0, 0, 58, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 18, 0, 0, 1, + 54, 0, 0, 7, 66, 0, + 16, 0, 2, 0, 0, 0, + 42, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 50, 0, 0, 10, + 34, 0, 16, 0, 3, 0, + 0, 0, 42, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 171, 170, 170, 61, + 58, 0, 16, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 8, 66, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 10, 0, 16, 0, 3, 0, + 0, 0, 0, 0, 0, 8, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 29, 0, + 0, 9, 18, 0, 16, 0, + 4, 0, 0, 0, 42, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 58, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 52, 0, + 0, 9, 66, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 42, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 31, 0, + 4, 3, 10, 0, 16, 0, + 4, 0, 0, 0, 54, 0, + 0, 6, 66, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 128, 65, 0, 0, 0, + 2, 0, 0, 0, 21, 0, + 0, 1, 56, 32, 0, 8, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 26, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 9, 34, 0, 16, 0, + 3, 0, 0, 0, 26, 0, + 16, 0, 2, 0, 0, 0, + 42, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 55, 0, + 0, 11, 130, 0, 16, 0, + 3, 0, 0, 0, 26, 0, + 16, 0, 2, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 58, 128, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 31, 0, 0, 3, 26, 0, + 16, 0, 2, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 4, 0, 0, 0, + 42, 0, 16, 0, 2, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 42, 0, + 16, 0, 0, 0, 0, 0, + 18, 0, 0, 1, 54, 0, + 0, 5, 34, 0, 16, 0, + 4, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 26, 0, 16, 0, + 2, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 4, 0, 0, 0, 42, 0, + 16, 0, 2, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 58, 0, 16, 0, + 0, 0, 0, 0, 18, 0, + 0, 1, 54, 0, 0, 5, + 66, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 0, 0, 0, 8, + 50, 0, 16, 0, 5, 0, + 0, 0, 214, 5, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 150, 5, 16, 0, + 4, 0, 0, 0, 0, 0, + 0, 7, 162, 0, 16, 0, + 4, 0, 0, 0, 86, 13, + 16, 0, 3, 0, 0, 0, + 86, 9, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 9, + 66, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 1, 64, 0, 0, 0, 0, + 64, 64, 72, 0, 0, 13, + 66, 0, 16, 0, 5, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 70, 123, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 72, 0, + 0, 13, 130, 0, 16, 0, + 5, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 0, 0, 0, 7, + 18, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 10, 0, + 16, 0, 2, 0, 0, 0, + 18, 0, 0, 1, 0, 0, + 0, 7, 18, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 10, 0, 16, 0, 3, 0, + 0, 0, 21, 0, 0, 1, + 56, 0, 0, 7, 18, 0, + 16, 0, 2, 0, 0, 0, + 42, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 62, 50, 0, + 0, 10, 66, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 58, 0, 16, 0, 1, 0, + 0, 0, 56, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 4, 0, 0, 0, + 49, 0, 0, 7, 66, 0, + 16, 0, 3, 0, 0, 0, + 42, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 10, 18, 0, 16, 0, + 6, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 42, 0, 16, 0, 5, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 58, 0, + 16, 0, 5, 0, 0, 0, + 49, 0, 0, 8, 82, 0, + 16, 0, 4, 0, 0, 0, + 6, 1, 16, 128, 129, 0, + 0, 0, 6, 0, 0, 0, + 6, 0, 16, 0, 2, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 192, 63, 10, 0, + 16, 0, 5, 0, 0, 0, + 50, 0, 0, 10, 34, 0, + 16, 0, 5, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 192, 63, 26, 0, 16, 0, + 5, 0, 0, 0, 21, 0, + 0, 1, 60, 0, 0, 7, + 66, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 4, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 192, 63, 26, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 192, 63, 58, 0, + 16, 0, 4, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 5, 0, 0, 0, 31, 0, + 4, 3, 10, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 18, 0, 16, 0, + 6, 0, 0, 0, 70, 0, + 16, 0, 5, 0, 0, 0, + 54, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 72, 0, 0, 13, 34, 0, + 16, 0, 6, 0, 0, 0, + 214, 5, 16, 0, 4, 0, + 0, 0, 198, 121, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 10, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 6, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 26, 0, 16, 0, 6, 0, + 0, 0, 21, 0, 0, 1, + 49, 0, 0, 8, 82, 0, + 16, 0, 4, 0, 0, 0, + 6, 1, 16, 128, 129, 0, + 0, 0, 6, 0, 0, 0, + 6, 0, 16, 0, 2, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 64, 10, 0, + 16, 0, 5, 0, 0, 0, + 50, 0, 0, 10, 34, 0, + 16, 0, 5, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 26, 0, 16, 0, + 5, 0, 0, 0, 21, 0, + 0, 1, 60, 0, 0, 7, + 66, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 4, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 64, 26, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 64, 58, 0, + 16, 0, 4, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 5, 0, 0, 0, 31, 0, + 4, 3, 10, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 18, 0, 16, 0, + 6, 0, 0, 0, 70, 0, + 16, 0, 5, 0, 0, 0, + 54, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 72, 0, 0, 13, 34, 0, + 16, 0, 6, 0, 0, 0, + 214, 5, 16, 0, 4, 0, + 0, 0, 198, 121, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 10, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 6, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 26, 0, 16, 0, 6, 0, + 0, 0, 21, 0, 0, 1, + 49, 0, 0, 8, 82, 0, + 16, 0, 4, 0, 0, 0, + 6, 1, 16, 128, 129, 0, + 0, 0, 6, 0, 0, 0, + 6, 0, 16, 0, 2, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 64, 10, 0, + 16, 0, 5, 0, 0, 0, + 50, 0, 0, 10, 34, 0, + 16, 0, 5, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 64, 26, 0, 16, 0, + 5, 0, 0, 0, 21, 0, + 0, 1, 60, 0, 0, 7, + 66, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 4, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 64, 26, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 64, 58, 0, + 16, 0, 4, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 5, 0, 0, 0, 31, 0, + 4, 3, 10, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 18, 0, 16, 0, + 6, 0, 0, 0, 70, 0, + 16, 0, 5, 0, 0, 0, + 54, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 72, 0, 0, 13, 34, 0, + 16, 0, 6, 0, 0, 0, + 214, 5, 16, 0, 4, 0, + 0, 0, 198, 121, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 10, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 6, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 26, 0, 16, 0, 6, 0, + 0, 0, 21, 0, 0, 1, + 49, 0, 0, 8, 82, 0, + 16, 0, 4, 0, 0, 0, + 6, 1, 16, 128, 129, 0, + 0, 0, 6, 0, 0, 0, + 6, 0, 16, 0, 2, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 64, 65, 10, 0, + 16, 0, 5, 0, 0, 0, + 50, 0, 0, 10, 34, 0, + 16, 0, 5, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 64, 65, 26, 0, 16, 0, + 5, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 9, + 34, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 64, 65, + 26, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 64, 65, + 58, 0, 16, 0, 4, 0, + 0, 0, 21, 0, 0, 1, + 21, 0, 0, 1, 21, 0, + 0, 1, 21, 0, 0, 1, + 31, 0, 0, 3, 26, 0, + 16, 0, 2, 0, 0, 0, + 50, 0, 0, 12, 18, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 58, 128, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 5, 0, 0, 0, + 50, 0, 0, 12, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 58, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 4, 0, 0, 0, + 18, 0, 0, 1, 50, 0, + 0, 12, 18, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 42, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 5, 0, 0, 0, 50, 0, + 0, 12, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 42, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 49, 0, 0, 10, + 50, 0, 16, 0, 3, 0, + 0, 0, 70, 0, 16, 0, + 6, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 34, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 39, 0, + 0, 7, 50, 0, 16, 0, + 3, 0, 0, 0, 166, 10, + 16, 0, 3, 0, 0, 0, + 70, 0, 16, 0, 3, 0, + 0, 0, 14, 0, 0, 10, + 34, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 128, 63, 0, 0, + 128, 63, 0, 0, 128, 63, + 0, 0, 128, 63, 26, 0, + 16, 0, 0, 0, 0, 0, + 49, 0, 0, 7, 66, 0, + 16, 0, 3, 0, 0, 0, + 10, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 51, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 2, 0, + 0, 0, 55, 0, 0, 9, + 18, 0, 16, 0, 2, 0, + 0, 0, 42, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 0, 3, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 56, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 56, 0, + 0, 7, 34, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 64, 63, 1, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 2, 0, 0, 0, + 52, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 31, 0, + 0, 3, 26, 0, 16, 0, + 2, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 2, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 26, 0, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 72, 0, 0, 13, 114, 0, + 16, 0, 1, 0, 0, 0, + 230, 10, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 54, 0, 0, 5, + 130, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 164, 0, + 0, 7, 242, 224, 33, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 70, 5, 2, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 62, 0, 0, 1, + 83, 84, 65, 84, 148, 0, + 0, 0, 205, 0, 0, 0, + 7, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 94, 0, 0, 0, 1, 0, + 0, 0, 7, 0, 0, 0, + 7, 0, 0, 0, 35, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 12, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 2, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0 +}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_extreme_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_extreme_cs.h new file mode 100644 index 000000000..026ac19fc --- /dev/null +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/fxaa_extreme_cs.h @@ -0,0 +1,1956 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeApplyGammaRampConstants +// { +// +// uint2 xe_fxaa_size; // Offset: 0 Size: 8 +// float2 xe_fxaa_size_inv; // Offset: 8 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_sampler_linear_clamp sampler NA NA S0 s0 1 +// xe_fxaa_source texture float4 2d T0 t0 1 +// xe_fxaa_dest UAV unorm4 2d U0 u0 1 +// XeApplyGammaRampConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Input +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// no Output +cs_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_sampler S0[0:0], mode_default, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_uav_typed_texture2d (unorm,unorm,unorm,unorm) U0[0:0], space=0 +dcl_input vThreadID.xy +dcl_temps 7 +dcl_thread_group 8, 8, 1 +uge r0.xy, vThreadID.xyxx, CB0[0][0].xyxx +or r0.x, r0.y, r0.x +if_nz r0.x + ret +endif +utof r0.xy, vThreadID.xyxx +add r0.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000) +mul r0.zw, r0.xxxy, CB0[0][0].zzzw +sample_l r1.xyzw, r0.zwzz, T0[0].xyzw, S0[0], l(0.000000) +gather4 r2.xyz, r0.zwzz, T0[0].xyzw, S0[0].w +gather4_aoffimmi(-1,-1,0) r3.xyz, r0.zwzz, T0[0].zxwy, S0[0].w +max r2.w, r1.w, r2.x +min r3.w, r1.w, r2.x +max r2.w, r2.w, r2.z +min r3.w, r2.z, r3.w +max r4.x, r3.y, r3.x +min r4.y, r3.y, r3.x +max r2.w, r2.w, r4.x +min r3.w, r3.w, r4.y +mul r4.x, r2.w, l(0.063000) +add r2.w, r2.w, -r3.w +max r3.w, r4.x, l(0.031200) +lt r3.w, r2.w, r3.w +if_z r3.w +endif +if_z r3.w + sample_l_aoffimmi(1,-1,0) r3.w, r0.zwzz, T0[0].xyzw, S0[0], l(0.000000) + sample_l_aoffimmi(-1,1,0) r4.x, r0.zwzz, T0[0].wxyz, S0[0], l(0.000000) + add r4.yz, r2.xxzx, r3.xxyx + div r2.w, l(1.000000, 1.000000, 1.000000, 1.000000), r2.w + add r4.w, r4.z, r4.y + mad r4.yz, r1.wwww, l(0.000000, -2.000000, -2.000000, 0.000000), r4.yyzy + add r5.x, r2.y, r3.w + add r3.w, r3.w, r3.z + mad r5.y, r2.z, l(-2.000000), r5.x + mad r3.w, r3.x, l(-2.000000), r3.w + add r3.z, r3.z, r4.x + add r2.y, r2.y, r4.x + mad r4.x, |r4.y|, l(2.000000), |r5.y| + mad r3.w, |r4.z|, l(2.000000), |r3.w| + mad r4.y, r3.y, l(-2.000000), r3.z + mad r2.y, r2.x, l(-2.000000), r2.y + add r4.x, r4.x, |r4.y| + add r2.y, r3.w, |r2.y| + add r3.z, r5.x, r3.z + ge r2.y, r4.x, r2.y + mad r3.z, r4.w, l(2.000000), r3.z + if_z r2.y + mov r3.x, r3.y + mov r2.x, r2.z + endif + if_nz r2.y + mov r2.z, CB0[0][0].w + else + mov r2.z, CB0[0][0].z + endif + mad r3.y, r3.z, l(0.083333), -r1.w + add r3.z, -r1.w, r3.x + add r3.w, -r1.w, r2.x + ge r4.x, |r3.z|, |r3.w| + max r3.z, |r3.w|, |r3.z| + if_nz r4.x + mov r2.z, -r2.z + endif + mul_sat r2.w, r2.w, |r3.y| + and r3.y, r2.y, CB0[0][0].z + movc r3.w, r2.y, l(0), CB0[0][0].w + if_z r2.y + mad r4.y, r2.z, l(0.500000), r0.z + else + mov r4.y, r0.z + endif + if_nz r2.y + mad r4.z, r2.z, l(0.500000), r0.w + else + mov r4.z, r0.w + endif + add r5.xy, -r3.ywyy, r4.yzyy + add r4.yw, r3.yyyw, r4.yyyz + mad r4.z, r2.w, l(-2.000000), l(3.000000) + sample_l r5.z, r5.xyxx, T0[0].xywz, S0[0], l(0.000000) + mul r2.w, r2.w, r2.w + sample_l r5.w, r4.ywyy, T0[0].xyzw, S0[0], l(0.000000) + if_z r4.x + add r3.x, r1.w, r2.x + else + add r3.x, r1.w, r3.x + endif + mul r2.x, r3.z, l(0.250000) + mad r3.z, -r3.x, l(0.500000), r1.w + mul r2.w, r2.w, r4.z + lt r3.z, r3.z, l(0.000000) + mad r6.x, -r3.x, l(0.500000), r5.z + mad r6.y, -r3.x, l(0.500000), r5.w + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + add r5.xy, -r3.ywyy, r5.xyxx + endif + or r5.z, r4.z, r4.x + if_nz r4.z + add r4.yw, r3.yyyw, r4.yyyw + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + add r5.xy, -r3.ywyy, r5.xyxx + endif + or r5.z, r4.z, r4.x + if_nz r4.z + add r4.yw, r3.yyyw, r4.yyyw + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + add r5.xy, -r3.ywyy, r5.xyxx + endif + or r5.z, r4.z, r4.x + if_nz r4.z + add r4.yw, r3.yyyw, r4.yyyw + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + add r5.xy, -r3.ywyy, r5.xyxx + endif + or r5.z, r4.z, r4.x + if_nz r4.z + add r4.yw, r3.yyyw, r4.yyyw + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(1.500000), r5.x + mad r5.y, -r3.w, l(1.500000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(1.500000), r4.y + mad r4.w, r3.w, l(1.500000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(2.000000), r5.x + mad r5.y, -r3.w, l(2.000000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(2.000000), r4.y + mad r4.w, r3.w, l(2.000000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(2.000000), r5.x + mad r5.y, -r3.w, l(2.000000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(2.000000), r4.y + mad r4.w, r3.w, l(2.000000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(2.000000), r5.x + mad r5.y, -r3.w, l(2.000000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(2.000000), r4.y + mad r4.w, r3.w, l(2.000000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(2.000000), r5.x + mad r5.y, -r3.w, l(2.000000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(2.000000), r4.y + mad r4.w, r3.w, l(2.000000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(4.000000), r5.x + mad r5.y, -r3.w, l(4.000000), r5.y + endif + or r5.z, r4.z, r4.x + if_nz r4.z + mad r4.y, r3.y, l(4.000000), r4.y + mad r4.w, r3.w, l(4.000000), r4.w + endif + if_nz r5.z + if_nz r4.x + sample_l r6.x, r5.xyxx, T0[0].wxyz, S0[0], l(0.000000) + endif + if_nz r4.z + sample_l r6.y, r4.ywyy, T0[0].xwyz, S0[0], l(0.000000) + endif + if_nz r4.x + mad r6.x, -r3.x, l(0.500000), r6.x + endif + if_nz r4.z + mad r6.y, -r3.x, l(0.500000), r6.y + endif + lt r4.xz, |r6.xxyx|, r2.xxxx + if_nz r4.x + mad r5.x, -r3.y, l(8.000000), r5.x + mad r5.y, -r3.w, l(8.000000), r5.y + endif + if_nz r4.z + mad r4.y, r3.y, l(8.000000), r4.y + mad r4.w, r3.w, l(8.000000), r4.w + endif + endif + endif + endif + endif + endif + endif + endif + endif + endif + endif + if_z r2.y + mad r2.x, r0.y, CB0[0][0].w, -r5.y + mad r0.x, -r0.y, CB0[0][0].w, r4.w + else + mad r2.x, r0.x, CB0[0][0].z, -r5.x + mad r0.x, -r0.x, CB0[0][0].z, r4.y + endif + lt r3.xy, r6.xyxx, l(0.000000, 0.000000, 0.000000, 0.000000) + add r0.y, r2.x, r0.x + ine r3.xy, r3.zzzz, r3.xyxx + div r0.y, l(1.000000, 1.000000, 1.000000, 1.000000), r0.y + lt r3.z, r2.x, r0.x + min r0.x, r0.x, r2.x + movc r2.x, r3.z, r3.x, r3.y + mul r2.w, r2.w, r2.w + mad r0.x, r0.x, -r0.y, l(0.500000) + and r0.x, r0.x, r2.x + max r0.x, r2.w, r0.x + if_z r2.y + mad r0.z, r0.x, r2.z, r0.z + endif + if_nz r2.y + mad r0.w, r0.x, r2.z, r0.w + endif + sample_l r1.xyz, r0.zwzz, T0[0].xyzw, S0[0], l(0.000000) +endif +mov r1.w, l(1.000000) +store_uav_typed U0[0].xyzw, vThreadID.xyyy, r1.xyzw +ret +// Approximately 364 instruction slots used +#endif + +const BYTE fxaa_extreme_cs[] = +{ + 68, 88, 66, 67, 37, 138, + 209, 45, 7, 149, 158, 200, + 128, 20, 212, 142, 87, 247, + 202, 95, 1, 0, 0, 0, + 16, 36, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 108, 2, 0, 0, 124, 2, + 0, 0, 140, 2, 0, 0, + 116, 35, 0, 0, 82, 68, + 69, 70, 48, 2, 0, 0, + 1, 0, 0, 0, 44, 1, + 0, 0, 4, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 83, 67, 0, 5, 0, 0, + 8, 2, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 220, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 244, 0, + 0, 0, 2, 0, 0, 0, + 5, 0, 0, 0, 4, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 1, 0, + 0, 0, 12, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 1, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 1, 0, 0, 0, + 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 16, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 120, 101, + 95, 115, 97, 109, 112, 108, + 101, 114, 95, 108, 105, 110, + 101, 97, 114, 95, 99, 108, + 97, 109, 112, 0, 120, 101, + 95, 102, 120, 97, 97, 95, + 115, 111, 117, 114, 99, 101, + 0, 120, 101, 95, 102, 120, + 97, 97, 95, 100, 101, 115, + 116, 0, 88, 101, 65, 112, + 112, 108, 121, 71, 97, 109, + 109, 97, 82, 97, 109, 112, + 67, 111, 110, 115, 116, 97, + 110, 116, 115, 0, 171, 171, + 16, 1, 0, 0, 2, 0, + 0, 0, 68, 1, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 148, 1, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 2, 0, 0, 0, 168, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 204, 1, + 0, 0, 8, 0, 0, 0, + 8, 0, 0, 0, 2, 0, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 120, 101, 95, 102, + 120, 97, 97, 95, 115, 105, + 122, 101, 0, 117, 105, 110, + 116, 50, 0, 171, 1, 0, + 19, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 161, 1, 0, 0, 120, 101, + 95, 102, 120, 97, 97, 95, + 115, 105, 122, 101, 95, 105, + 110, 118, 0, 102, 108, 111, + 97, 116, 50, 0, 1, 0, + 3, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 221, 1, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 8, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 79, 83, 71, 78, 8, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 83, 72, + 69, 88, 224, 32, 0, 0, + 81, 0, 5, 0, 56, 8, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 90, 0, + 0, 6, 70, 110, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 156, 24, 0, 7, + 70, 238, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 17, + 0, 0, 0, 0, 0, 0, + 95, 0, 0, 2, 50, 0, + 2, 0, 104, 0, 0, 2, + 7, 0, 0, 0, 155, 0, + 0, 4, 8, 0, 0, 0, + 8, 0, 0, 0, 1, 0, + 0, 0, 80, 0, 0, 8, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 2, 0, + 70, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 60, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 0, 0, + 0, 0, 62, 0, 0, 1, + 21, 0, 0, 1, 86, 0, + 0, 4, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 2, 0, 0, 0, 0, 10, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 63, + 0, 0, 0, 63, 0, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 9, 194, 0, + 16, 0, 0, 0, 0, 0, + 6, 4, 16, 0, 0, 0, + 0, 0, 166, 142, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 72, 0, 0, 13, 242, 0, + 16, 0, 1, 0, 0, 0, + 230, 10, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 109, 0, + 0, 11, 114, 0, 16, 0, + 2, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 58, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 109, 0, 0, 140, 1, 254, + 1, 0, 114, 0, 16, 0, + 3, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 38, 119, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 58, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 10, 0, 16, 0, 2, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 2, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 3, 0, 0, 0, + 42, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 52, 0, + 0, 7, 18, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 10, 0, 16, 0, 3, 0, + 0, 0, 51, 0, 0, 7, + 34, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 0, 3, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 56, 0, 0, 7, + 18, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 37, 6, 129, 61, + 0, 0, 0, 8, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 36, 151, 255, 60, + 49, 0, 0, 7, 130, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 31, 0, + 0, 3, 58, 0, 16, 0, + 3, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 0, 3, + 58, 0, 16, 0, 3, 0, + 0, 0, 72, 0, 0, 142, + 1, 226, 1, 0, 130, 0, + 16, 0, 3, 0, 0, 0, + 230, 10, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 72, 0, + 0, 142, 1, 62, 0, 0, + 18, 0, 16, 0, 4, 0, + 0, 0, 230, 10, 16, 0, + 0, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 98, 0, + 16, 0, 4, 0, 0, 0, + 6, 2, 16, 0, 2, 0, + 0, 0, 6, 1, 16, 0, + 3, 0, 0, 0, 14, 0, + 0, 10, 130, 0, 16, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 128, 63, 0, 0, 128, 63, + 58, 0, 16, 0, 2, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 4, 0, + 0, 0, 42, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 12, 98, 0, + 16, 0, 4, 0, 0, 0, + 246, 15, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 192, 0, 0, 0, 192, + 0, 0, 0, 0, 86, 6, + 16, 0, 4, 0, 0, 0, + 0, 0, 0, 7, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 0, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 42, 0, 16, 0, 3, 0, + 0, 0, 50, 0, 0, 9, + 34, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 10, 0, 16, 0, 5, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 3, 0, + 0, 0, 10, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 58, 0, 16, 0, 3, 0, + 0, 0, 0, 0, 0, 7, + 66, 0, 16, 0, 3, 0, + 0, 0, 42, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 0, 0, 0, 7, 34, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 11, 18, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 128, 129, 0, 0, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 26, 0, 16, 128, 129, 0, + 0, 0, 5, 0, 0, 0, + 50, 0, 0, 11, 130, 0, + 16, 0, 3, 0, 0, 0, + 42, 0, 16, 128, 129, 0, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 58, 0, 16, 128, + 129, 0, 0, 0, 3, 0, + 0, 0, 50, 0, 0, 9, + 34, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 42, 0, 16, 0, 3, 0, + 0, 0, 50, 0, 0, 9, + 34, 0, 16, 0, 2, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 26, 0, 16, 0, 2, 0, + 0, 0, 0, 0, 0, 8, + 18, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 128, 129, 0, 0, 0, + 4, 0, 0, 0, 0, 0, + 0, 8, 34, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 26, 0, 16, 128, 129, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 7, 66, 0, + 16, 0, 3, 0, 0, 0, + 10, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 3, 0, 0, 0, 29, 0, + 0, 7, 34, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 66, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 42, 0, 16, 0, 3, 0, + 0, 0, 31, 0, 0, 3, + 26, 0, 16, 0, 2, 0, + 0, 0, 54, 0, 0, 5, + 18, 0, 16, 0, 3, 0, + 0, 0, 26, 0, 16, 0, + 3, 0, 0, 0, 54, 0, + 0, 5, 18, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 2, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 26, 0, 16, 0, + 2, 0, 0, 0, 54, 0, + 0, 7, 66, 0, 16, 0, + 2, 0, 0, 0, 58, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 18, 0, 0, 1, + 54, 0, 0, 7, 66, 0, + 16, 0, 2, 0, 0, 0, + 42, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 50, 0, 0, 10, + 34, 0, 16, 0, 3, 0, + 0, 0, 42, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 171, 170, 170, 61, + 58, 0, 16, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 8, 66, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 10, 0, 16, 0, 3, 0, + 0, 0, 0, 0, 0, 8, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 29, 0, + 0, 9, 18, 0, 16, 0, + 4, 0, 0, 0, 42, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 58, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 52, 0, + 0, 9, 66, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 42, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 31, 0, + 4, 3, 10, 0, 16, 0, + 4, 0, 0, 0, 54, 0, + 0, 6, 66, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 128, 65, 0, 0, 0, + 2, 0, 0, 0, 21, 0, + 0, 1, 56, 32, 0, 8, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 26, 0, + 16, 128, 129, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 9, 34, 0, 16, 0, + 3, 0, 0, 0, 26, 0, + 16, 0, 2, 0, 0, 0, + 42, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 55, 0, + 0, 11, 130, 0, 16, 0, + 3, 0, 0, 0, 26, 0, + 16, 0, 2, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 58, 128, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 31, 0, 0, 3, 26, 0, + 16, 0, 2, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 4, 0, 0, 0, + 42, 0, 16, 0, 2, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 42, 0, + 16, 0, 0, 0, 0, 0, + 18, 0, 0, 1, 54, 0, + 0, 5, 34, 0, 16, 0, + 4, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 26, 0, 16, 0, + 2, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 4, 0, 0, 0, 42, 0, + 16, 0, 2, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 58, 0, 16, 0, + 0, 0, 0, 0, 18, 0, + 0, 1, 54, 0, 0, 5, + 66, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 0, 0, 0, 8, + 50, 0, 16, 0, 5, 0, + 0, 0, 214, 5, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 150, 5, 16, 0, + 4, 0, 0, 0, 0, 0, + 0, 7, 162, 0, 16, 0, + 4, 0, 0, 0, 86, 13, + 16, 0, 3, 0, 0, 0, + 86, 9, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 9, + 66, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 192, + 1, 64, 0, 0, 0, 0, + 64, 64, 72, 0, 0, 13, + 66, 0, 16, 0, 5, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 70, 123, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 72, 0, + 0, 13, 130, 0, 16, 0, + 5, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 0, 0, 0, 7, + 18, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 10, 0, + 16, 0, 2, 0, 0, 0, + 18, 0, 0, 1, 0, 0, + 0, 7, 18, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 10, 0, 16, 0, 3, 0, + 0, 0, 21, 0, 0, 1, + 56, 0, 0, 7, 18, 0, + 16, 0, 2, 0, 0, 0, + 42, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 62, 50, 0, + 0, 10, 66, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 58, 0, 16, 0, 1, 0, + 0, 0, 56, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 4, 0, 0, 0, + 49, 0, 0, 7, 66, 0, + 16, 0, 3, 0, 0, 0, + 42, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 10, 18, 0, 16, 0, + 6, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 42, 0, 16, 0, 5, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 58, 0, + 16, 0, 5, 0, 0, 0, + 49, 0, 0, 8, 82, 0, + 16, 0, 4, 0, 0, 0, + 6, 1, 16, 128, 129, 0, + 0, 0, 6, 0, 0, 0, + 6, 0, 16, 0, 2, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 0, 0, 0, 8, + 50, 0, 16, 0, 5, 0, + 0, 0, 214, 5, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 21, 0, + 0, 1, 60, 0, 0, 7, + 66, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 4, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 0, 0, 0, 7, 162, 0, + 16, 0, 4, 0, 0, 0, + 86, 13, 16, 0, 3, 0, + 0, 0, 86, 13, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 5, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 18, 0, 16, 0, 6, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 34, 0, 16, 0, + 6, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 198, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 10, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 26, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 8, 82, 0, 16, 0, + 4, 0, 0, 0, 6, 1, + 16, 128, 129, 0, 0, 0, + 6, 0, 0, 0, 6, 0, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 0, 0, 0, 8, 50, 0, + 16, 0, 5, 0, 0, 0, + 214, 5, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 70, 0, 16, 0, 5, 0, + 0, 0, 21, 0, 0, 1, + 60, 0, 0, 7, 66, 0, + 16, 0, 5, 0, 0, 0, + 42, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 0, 0, + 0, 7, 162, 0, 16, 0, + 4, 0, 0, 0, 86, 13, + 16, 0, 3, 0, 0, 0, + 86, 13, 16, 0, 4, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 42, 0, + 16, 0, 5, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 72, 0, 0, 13, 18, 0, + 16, 0, 6, 0, 0, 0, + 70, 0, 16, 0, 5, 0, + 0, 0, 54, 121, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 34, 0, 16, 0, 6, 0, + 0, 0, 214, 5, 16, 0, + 4, 0, 0, 0, 198, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 10, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 10, 18, 0, 16, 0, + 6, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 10, 0, 16, 0, 6, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 34, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 26, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 49, 0, 0, 8, + 82, 0, 16, 0, 4, 0, + 0, 0, 6, 1, 16, 128, + 129, 0, 0, 0, 6, 0, + 0, 0, 6, 0, 16, 0, + 2, 0, 0, 0, 31, 0, + 4, 3, 10, 0, 16, 0, + 4, 0, 0, 0, 0, 0, + 0, 8, 50, 0, 16, 0, + 5, 0, 0, 0, 214, 5, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 70, 0, + 16, 0, 5, 0, 0, 0, + 21, 0, 0, 1, 60, 0, + 0, 7, 66, 0, 16, 0, + 5, 0, 0, 0, 42, 0, + 16, 0, 4, 0, 0, 0, + 10, 0, 16, 0, 4, 0, + 0, 0, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 0, 0, 0, 7, + 162, 0, 16, 0, 4, 0, + 0, 0, 86, 13, 16, 0, + 3, 0, 0, 0, 86, 13, + 16, 0, 4, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 5, 0, 0, 0, 31, 0, + 4, 3, 10, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 18, 0, 16, 0, + 6, 0, 0, 0, 70, 0, + 16, 0, 5, 0, 0, 0, + 54, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 72, 0, 0, 13, 34, 0, + 16, 0, 6, 0, 0, 0, + 214, 5, 16, 0, 4, 0, + 0, 0, 198, 121, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 10, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 6, 0, 0, 0, 10, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 26, 0, 16, 0, 6, 0, + 0, 0, 21, 0, 0, 1, + 49, 0, 0, 8, 82, 0, + 16, 0, 4, 0, 0, 0, + 6, 1, 16, 128, 129, 0, + 0, 0, 6, 0, 0, 0, + 6, 0, 16, 0, 2, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 0, 0, 0, 8, + 50, 0, 16, 0, 5, 0, + 0, 0, 214, 5, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 21, 0, + 0, 1, 60, 0, 0, 7, + 66, 0, 16, 0, 5, 0, + 0, 0, 42, 0, 16, 0, + 4, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 0, 0, 0, 7, 162, 0, + 16, 0, 4, 0, 0, 0, + 86, 13, 16, 0, 3, 0, + 0, 0, 86, 13, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 5, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 18, 0, 16, 0, 6, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 34, 0, 16, 0, + 6, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 198, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 10, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 26, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 8, 82, 0, 16, 0, + 4, 0, 0, 0, 6, 1, + 16, 128, 129, 0, 0, 0, + 6, 0, 0, 0, 6, 0, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 192, 63, 10, 0, 16, 0, + 5, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 192, 63, + 26, 0, 16, 0, 5, 0, + 0, 0, 21, 0, 0, 1, + 60, 0, 0, 7, 66, 0, + 16, 0, 5, 0, 0, 0, + 42, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 34, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 192, 63, 26, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 192, 63, 58, 0, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 5, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 18, 0, 16, 0, 6, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 34, 0, 16, 0, + 6, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 198, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 10, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 26, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 8, 82, 0, 16, 0, + 4, 0, 0, 0, 6, 1, + 16, 128, 129, 0, 0, 0, + 6, 0, 0, 0, 6, 0, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 10, 0, 16, 0, + 5, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 26, 0, 16, 0, 5, 0, + 0, 0, 21, 0, 0, 1, + 60, 0, 0, 7, 66, 0, + 16, 0, 5, 0, 0, 0, + 42, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 34, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 26, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 58, 0, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 5, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 18, 0, 16, 0, 6, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 34, 0, 16, 0, + 6, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 198, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 10, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 26, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 8, 82, 0, 16, 0, + 4, 0, 0, 0, 6, 1, + 16, 128, 129, 0, 0, 0, + 6, 0, 0, 0, 6, 0, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 10, 0, 16, 0, + 5, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 26, 0, 16, 0, 5, 0, + 0, 0, 21, 0, 0, 1, + 60, 0, 0, 7, 66, 0, + 16, 0, 5, 0, 0, 0, + 42, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 34, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 26, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 58, 0, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 5, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 18, 0, 16, 0, 6, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 34, 0, 16, 0, + 6, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 198, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 10, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 26, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 8, 82, 0, 16, 0, + 4, 0, 0, 0, 6, 1, + 16, 128, 129, 0, 0, 0, + 6, 0, 0, 0, 6, 0, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 10, 0, 16, 0, + 5, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 26, 0, 16, 0, 5, 0, + 0, 0, 21, 0, 0, 1, + 60, 0, 0, 7, 66, 0, + 16, 0, 5, 0, 0, 0, + 42, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 34, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 26, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 58, 0, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 5, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 18, 0, 16, 0, 6, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 34, 0, 16, 0, + 6, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 198, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 10, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 26, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 8, 82, 0, 16, 0, + 4, 0, 0, 0, 6, 1, + 16, 128, 129, 0, 0, 0, + 6, 0, 0, 0, 6, 0, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 10, 0, 16, 0, + 5, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 26, 0, 16, 0, 5, 0, + 0, 0, 21, 0, 0, 1, + 60, 0, 0, 7, 66, 0, + 16, 0, 5, 0, 0, 0, + 42, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 34, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 26, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 58, 0, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 5, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 18, 0, 16, 0, 6, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 34, 0, 16, 0, + 6, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 198, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 10, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 26, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 8, 82, 0, 16, 0, + 4, 0, 0, 0, 6, 1, + 16, 128, 129, 0, 0, 0, + 6, 0, 0, 0, 6, 0, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 64, 10, 0, 16, 0, + 5, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 64, + 26, 0, 16, 0, 5, 0, + 0, 0, 21, 0, 0, 1, + 60, 0, 0, 7, 66, 0, + 16, 0, 5, 0, 0, 0, + 42, 0, 16, 0, 4, 0, + 0, 0, 10, 0, 16, 0, + 4, 0, 0, 0, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 34, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 64, 26, 0, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 64, 58, 0, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 5, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 4, 0, + 0, 0, 72, 0, 0, 13, + 18, 0, 16, 0, 6, 0, + 0, 0, 70, 0, 16, 0, + 5, 0, 0, 0, 54, 121, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 4, 0, 0, 0, 72, 0, + 0, 13, 34, 0, 16, 0, + 6, 0, 0, 0, 214, 5, + 16, 0, 4, 0, 0, 0, + 198, 121, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 63, 10, 0, 16, 0, + 6, 0, 0, 0, 21, 0, + 0, 1, 31, 0, 4, 3, + 42, 0, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 6, 0, + 0, 0, 10, 0, 16, 128, + 65, 0, 0, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 63, 26, 0, + 16, 0, 6, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 8, 82, 0, 16, 0, + 4, 0, 0, 0, 6, 1, + 16, 128, 129, 0, 0, 0, + 6, 0, 0, 0, 6, 0, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 10, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 10, 18, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 65, 10, 0, 16, 0, + 5, 0, 0, 0, 50, 0, + 0, 10, 34, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 65, + 26, 0, 16, 0, 5, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 42, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 65, 26, 0, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 65, 58, 0, + 16, 0, 4, 0, 0, 0, + 21, 0, 0, 1, 21, 0, + 0, 1, 21, 0, 0, 1, + 21, 0, 0, 1, 21, 0, + 0, 1, 21, 0, 0, 1, + 21, 0, 0, 1, 21, 0, + 0, 1, 21, 0, 0, 1, + 21, 0, 0, 1, 21, 0, + 0, 1, 31, 0, 0, 3, + 26, 0, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 12, + 18, 0, 16, 0, 2, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 58, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 26, 0, 16, 128, + 65, 0, 0, 0, 5, 0, + 0, 0, 50, 0, 0, 12, + 18, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 58, 128, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 18, 0, 0, 1, + 50, 0, 0, 12, 18, 0, + 16, 0, 2, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 42, 128, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 5, 0, 0, 0, + 50, 0, 0, 12, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 42, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 4, 0, 0, 0, + 21, 0, 0, 1, 49, 0, + 0, 10, 50, 0, 16, 0, + 3, 0, 0, 0, 70, 0, + 16, 0, 6, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, + 34, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 39, 0, 0, 7, 50, 0, + 16, 0, 3, 0, 0, 0, + 166, 10, 16, 0, 3, 0, + 0, 0, 70, 0, 16, 0, + 3, 0, 0, 0, 14, 0, + 0, 10, 34, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 128, 63, 0, 0, 128, 63, + 26, 0, 16, 0, 0, 0, + 0, 0, 49, 0, 0, 7, + 66, 0, 16, 0, 3, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 51, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 55, 0, + 0, 9, 18, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 3, 0, 0, 0, + 10, 0, 16, 0, 3, 0, + 0, 0, 26, 0, 16, 0, + 3, 0, 0, 0, 56, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 10, + 18, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 1, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 2, 0, 0, 0, 52, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 31, 0, 0, 3, + 26, 0, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 66, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 2, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 31, 0, 4, 3, 26, 0, + 16, 0, 2, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 21, 0, 0, 1, 72, 0, + 0, 13, 114, 0, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 21, 0, 0, 1, + 54, 0, 0, 5, 130, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 164, 0, 0, 7, + 242, 224, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 70, 5, 2, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 62, 0, 0, 1, 83, 84, + 65, 84, 148, 0, 0, 0, + 108, 1, 0, 0, 7, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 134, 0, + 0, 0, 1, 0, 0, 0, + 14, 0, 0, 0, 7, 0, + 0, 0, 84, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 26, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0 +}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_1xmsaa_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_1xmsaa_cs.h index b4022a12a..3e43288af 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_1xmsaa_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_1xmsaa_cs.h @@ -100,10 +100,10 @@ ret const BYTE host_depth_store_1xmsaa_cs[] = { - 68, 88, 66, 67, 40, 219, - 237, 83, 207, 54, 217, 154, - 183, 72, 124, 224, 242, 47, - 153, 1, 1, 0, 0, 0, + 68, 88, 66, 67, 240, 197, + 52, 115, 102, 12, 206, 220, + 26, 47, 204, 31, 163, 187, + 103, 82, 1, 0, 0, 0, 196, 9, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 172, 2, 0, 0, 188, 2, @@ -113,7 +113,7 @@ const BYTE host_depth_store_1xmsaa_cs[] = 2, 0, 0, 0, 92, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 70, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_2xmsaa_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_2xmsaa_cs.h index 21f6ce603..16d7d51b2 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_2xmsaa_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_2xmsaa_cs.h @@ -113,10 +113,10 @@ ret const BYTE host_depth_store_2xmsaa_cs[] = { - 68, 88, 66, 67, 238, 111, - 30, 240, 147, 73, 122, 32, - 177, 28, 116, 35, 239, 195, - 44, 205, 1, 0, 0, 0, + 68, 88, 66, 67, 220, 153, + 96, 168, 160, 176, 100, 61, + 221, 246, 187, 57, 87, 53, + 74, 27, 1, 0, 0, 0, 104, 11, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 172, 2, 0, 0, 188, 2, @@ -126,7 +126,7 @@ const BYTE host_depth_store_2xmsaa_cs[] = 2, 0, 0, 0, 92, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 70, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_4xmsaa_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_4xmsaa_cs.h index 5ba206a86..0ff471cc5 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_4xmsaa_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/host_depth_store_4xmsaa_cs.h @@ -102,10 +102,10 @@ ret const BYTE host_depth_store_4xmsaa_cs[] = { - 68, 88, 66, 67, 2, 59, - 173, 180, 27, 122, 26, 189, - 176, 122, 66, 167, 148, 57, - 133, 176, 1, 0, 0, 0, + 68, 88, 66, 67, 255, 93, + 213, 231, 110, 151, 46, 65, + 140, 74, 54, 254, 196, 135, + 241, 89, 1, 0, 0, 0, 72, 10, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 172, 2, 0, 0, 188, 2, @@ -115,7 +115,7 @@ const BYTE host_depth_store_4xmsaa_cs[] = 2, 0, 0, 0, 92, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 70, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/passthrough_position_xy_vs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/passthrough_position_xy_vs.h index 6e58bc909..a5f7f58b8 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/passthrough_position_xy_vs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/passthrough_position_xy_vs.h @@ -29,10 +29,10 @@ ret const BYTE passthrough_position_xy_vs[] = { - 68, 88, 66, 67, 228, 252, - 150, 103, 204, 200, 35, 231, - 157, 180, 182, 79, 123, 204, - 157, 78, 1, 0, 0, 0, + 68, 88, 66, 67, 92, 220, + 0, 199, 190, 67, 183, 153, + 171, 192, 4, 39, 67, 55, + 215, 255, 1, 0, 0, 0, 12, 2, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 160, 0, 0, 0, 212, 0, @@ -42,7 +42,7 @@ const BYTE passthrough_position_xy_vs[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 254, 255, 0, 5, 4, 0, + 254, 255, 0, 5, 0, 0, 60, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_point_list_gs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_point_list_gs.h index 05adda89b..a8a356706 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_point_list_gs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_point_list_gs.h @@ -273,10 +273,10 @@ ret const BYTE primitive_point_list_gs[] = { - 68, 88, 66, 67, 22, 253, - 167, 182, 60, 53, 75, 37, - 178, 38, 230, 182, 97, 141, - 160, 120, 1, 0, 0, 0, + 68, 88, 66, 67, 6, 119, + 40, 99, 120, 121, 153, 224, + 111, 77, 187, 15, 15, 245, + 37, 128, 1, 0, 0, 0, 136, 29, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 184, 10, 0, 0, 248, 12, @@ -286,7 +286,7 @@ const BYTE primitive_point_list_gs[] = 1, 0, 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 71, 0, 5, 4, 0, + 83, 71, 0, 5, 0, 0, 82, 10, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_quad_list_gs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_quad_list_gs.h index ac77eae81..e349a4f47 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_quad_list_gs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_quad_list_gs.h @@ -194,10 +194,10 @@ ret const BYTE primitive_quad_list_gs[] = { - 68, 88, 66, 67, 60, 29, - 113, 120, 166, 105, 127, 75, - 174, 160, 2, 184, 182, 91, - 66, 12, 1, 0, 0, 0, + 68, 88, 66, 67, 26, 143, + 179, 72, 238, 147, 43, 130, + 37, 11, 116, 191, 138, 68, + 255, 76, 1, 0, 0, 0, 36, 16, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 160, 0, 0, 0, 224, 2, @@ -207,7 +207,7 @@ const BYTE primitive_quad_list_gs[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 71, 0, 5, 4, 0, + 83, 71, 0, 5, 0, 0, 60, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_rectangle_list_gs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_rectangle_list_gs.h index 6ce8c711d..4c078e6ed 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_rectangle_list_gs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/primitive_rectangle_list_gs.h @@ -424,10 +424,10 @@ ret const BYTE primitive_rectangle_list_gs[] = { - 68, 88, 66, 67, 8, 81, - 101, 117, 123, 39, 249, 189, - 184, 94, 218, 115, 216, 148, - 125, 96, 1, 0, 0, 0, + 68, 88, 66, 67, 84, 207, + 1, 11, 213, 109, 28, 213, + 94, 110, 135, 167, 112, 243, + 154, 30, 1, 0, 0, 0, 68, 40, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 160, 0, 0, 0, 224, 2, @@ -437,7 +437,7 @@ const BYTE primitive_rectangle_list_gs[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 71, 0, 5, 4, 0, + 83, 71, 0, 5, 0, 0, 60, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_cs.h index 960fc26a4..cc4b6ddd1 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_cs.h @@ -80,10 +80,10 @@ ret const BYTE resolve_clear_32bpp_cs[] = { - 68, 88, 66, 67, 212, 33, - 57, 169, 71, 99, 28, 75, - 202, 138, 254, 170, 245, 39, - 113, 44, 1, 0, 0, 0, + 68, 88, 66, 67, 226, 247, + 125, 160, 57, 195, 118, 226, + 52, 138, 250, 148, 103, 90, + 91, 213, 1, 0, 0, 0, 120, 7, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 64, 2, 0, 0, 80, 2, @@ -93,7 +93,7 @@ const BYTE resolve_clear_32bpp_cs[] = 1, 0, 0, 0, 176, 0, 0, 0, 2, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 220, 1, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_scaled_cs.h index 03fdb042d..2afb65467 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_32bpp_scaled_cs.h @@ -100,10 +100,10 @@ ret const BYTE resolve_clear_32bpp_scaled_cs[] = { - 68, 88, 66, 67, 151, 173, - 156, 244, 183, 241, 226, 31, - 214, 214, 54, 114, 10, 217, - 153, 194, 1, 0, 0, 0, + 68, 88, 66, 67, 205, 174, + 123, 5, 98, 198, 38, 143, + 98, 109, 190, 150, 214, 170, + 216, 92, 1, 0, 0, 0, 104, 9, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 228, 2, 0, 0, 244, 2, @@ -113,7 +113,7 @@ const BYTE resolve_clear_32bpp_scaled_cs[] = 2, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 128, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_cs.h index 25c2c75c5..c50250e97 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_cs.h @@ -79,10 +79,10 @@ ret const BYTE resolve_clear_64bpp_cs[] = { - 68, 88, 66, 67, 11, 134, - 106, 112, 85, 206, 144, 113, - 121, 69, 237, 199, 49, 129, - 4, 62, 1, 0, 0, 0, + 68, 88, 66, 67, 16, 5, + 27, 66, 247, 175, 170, 221, + 90, 253, 80, 46, 104, 119, + 87, 139, 1, 0, 0, 0, 148, 7, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 64, 2, 0, 0, 80, 2, @@ -92,7 +92,7 @@ const BYTE resolve_clear_64bpp_cs[] = 1, 0, 0, 0, 176, 0, 0, 0, 2, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 220, 1, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_scaled_cs.h index af43268c5..8db3ee557 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_clear_64bpp_scaled_cs.h @@ -98,10 +98,10 @@ ret const BYTE resolve_clear_64bpp_scaled_cs[] = { - 68, 88, 66, 67, 226, 249, - 231, 177, 11, 141, 42, 153, - 99, 45, 131, 104, 152, 194, - 26, 176, 1, 0, 0, 0, + 68, 88, 66, 67, 246, 14, + 132, 223, 204, 44, 6, 28, + 86, 181, 216, 251, 242, 62, + 34, 31, 1, 0, 0, 0, 112, 9, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 228, 2, 0, 0, 244, 2, @@ -111,7 +111,7 @@ const BYTE resolve_clear_64bpp_scaled_cs[] = 2, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 128, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_cs.h index c188bb822..4205ee54f 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_cs.h @@ -214,10 +214,10 @@ ret const BYTE resolve_fast_32bpp_1x2xmsaa_cs[] = { - 68, 88, 66, 67, 155, 144, - 108, 102, 32, 219, 74, 112, - 191, 128, 193, 179, 111, 166, - 110, 225, 1, 0, 0, 0, + 68, 88, 66, 67, 239, 168, + 103, 4, 189, 160, 141, 83, + 16, 65, 33, 184, 201, 9, + 185, 83, 1, 0, 0, 0, 156, 24, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -227,7 +227,7 @@ const BYTE resolve_fast_32bpp_1x2xmsaa_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_scaled_cs.h index 146f231b0..227c58ca3 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_1x2xmsaa_scaled_cs.h @@ -278,10 +278,10 @@ ret const BYTE resolve_fast_32bpp_1x2xmsaa_scaled_cs[] = { - 68, 88, 66, 67, 145, 228, - 65, 128, 86, 250, 142, 70, - 190, 88, 12, 69, 154, 111, - 98, 110, 1, 0, 0, 0, + 68, 88, 66, 67, 33, 171, + 235, 222, 88, 49, 17, 196, + 69, 254, 64, 92, 43, 122, + 25, 15, 1, 0, 0, 0, 40, 31, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -291,7 +291,7 @@ const BYTE resolve_fast_32bpp_1x2xmsaa_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_cs.h index b78967304..1de49b65a 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_cs.h @@ -225,10 +225,10 @@ ret const BYTE resolve_fast_32bpp_4xmsaa_cs[] = { - 68, 88, 66, 67, 83, 123, - 16, 172, 13, 23, 165, 181, - 111, 176, 230, 199, 227, 113, - 202, 160, 1, 0, 0, 0, + 68, 88, 66, 67, 170, 85, + 205, 145, 228, 207, 40, 6, + 92, 178, 101, 253, 108, 144, + 148, 218, 1, 0, 0, 0, 196, 25, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -238,7 +238,7 @@ const BYTE resolve_fast_32bpp_4xmsaa_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_scaled_cs.h index dd885ae7a..48e3dda10 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_32bpp_4xmsaa_scaled_cs.h @@ -290,10 +290,10 @@ ret const BYTE resolve_fast_32bpp_4xmsaa_scaled_cs[] = { - 68, 88, 66, 67, 31, 111, - 219, 218, 202, 212, 228, 164, - 35, 4, 215, 208, 98, 172, - 195, 246, 1, 0, 0, 0, + 68, 88, 66, 67, 228, 200, + 92, 46, 175, 37, 210, 169, + 116, 188, 100, 167, 120, 186, + 226, 184, 1, 0, 0, 0, 100, 32, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -303,7 +303,7 @@ const BYTE resolve_fast_32bpp_4xmsaa_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_cs.h index 06bd84852..827dbd661 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_cs.h @@ -203,10 +203,10 @@ ret const BYTE resolve_fast_64bpp_1x2xmsaa_cs[] = { - 68, 88, 66, 67, 148, 119, - 138, 27, 157, 3, 164, 49, - 44, 31, 102, 96, 70, 236, - 100, 146, 1, 0, 0, 0, + 68, 88, 66, 67, 6, 88, + 245, 144, 16, 3, 175, 59, + 111, 160, 2, 183, 105, 216, + 39, 35, 1, 0, 0, 0, 16, 23, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -216,7 +216,7 @@ const BYTE resolve_fast_64bpp_1x2xmsaa_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_scaled_cs.h index dca0ec999..d957d07a4 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_1x2xmsaa_scaled_cs.h @@ -265,10 +265,10 @@ ret const BYTE resolve_fast_64bpp_1x2xmsaa_scaled_cs[] = { - 68, 88, 66, 67, 146, 74, - 159, 181, 129, 209, 232, 153, - 8, 178, 69, 215, 5, 127, - 187, 119, 1, 0, 0, 0, + 68, 88, 66, 67, 217, 58, + 225, 5, 83, 76, 76, 76, + 41, 4, 184, 58, 64, 138, + 164, 215, 1, 0, 0, 0, 24, 29, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -278,7 +278,7 @@ const BYTE resolve_fast_64bpp_1x2xmsaa_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_cs.h index 8d109abf3..6d0167148 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_cs.h @@ -204,10 +204,10 @@ ret const BYTE resolve_fast_64bpp_4xmsaa_cs[] = { - 68, 88, 66, 67, 103, 127, - 24, 191, 130, 242, 189, 20, - 38, 18, 213, 75, 35, 175, - 132, 194, 1, 0, 0, 0, + 68, 88, 66, 67, 219, 212, + 228, 165, 45, 17, 135, 148, + 112, 245, 209, 7, 35, 133, + 189, 71, 1, 0, 0, 0, 40, 23, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -217,7 +217,7 @@ const BYTE resolve_fast_64bpp_4xmsaa_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_scaled_cs.h index 8da74d9b4..c3155881e 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_fast_64bpp_4xmsaa_scaled_cs.h @@ -263,10 +263,10 @@ ret const BYTE resolve_fast_64bpp_4xmsaa_scaled_cs[] = { - 68, 88, 66, 67, 175, 243, - 135, 222, 141, 148, 29, 126, - 11, 2, 155, 131, 194, 117, - 96, 179, 1, 0, 0, 0, + 68, 88, 66, 67, 128, 163, + 164, 187, 116, 154, 133, 40, + 143, 38, 115, 1, 152, 118, + 125, 200, 1, 0, 0, 0, 36, 29, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -276,7 +276,7 @@ const BYTE resolve_fast_64bpp_4xmsaa_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_cs.h index 8c891d214..3dcc2a0fc 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_cs.h @@ -797,10 +797,10 @@ ret const BYTE resolve_full_128bpp_cs[] = { - 68, 88, 66, 67, 138, 187, - 29, 190, 119, 248, 103, 130, - 145, 38, 138, 150, 199, 99, - 88, 186, 1, 0, 0, 0, + 68, 88, 66, 67, 21, 9, + 61, 18, 132, 97, 176, 57, + 19, 100, 60, 248, 43, 42, + 194, 237, 1, 0, 0, 0, 156, 86, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -810,7 +810,7 @@ const BYTE resolve_full_128bpp_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_scaled_cs.h index 55de6d907..33a40c184 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_128bpp_scaled_cs.h @@ -858,10 +858,10 @@ ret const BYTE resolve_full_128bpp_scaled_cs[] = { - 68, 88, 66, 67, 252, 54, - 64, 236, 181, 3, 56, 102, - 51, 248, 62, 205, 28, 164, - 215, 11, 1, 0, 0, 0, + 68, 88, 66, 67, 0, 197, + 108, 112, 31, 221, 155, 150, + 192, 148, 203, 210, 205, 214, + 112, 66, 1, 0, 0, 0, 220, 91, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -871,7 +871,7 @@ const BYTE resolve_full_128bpp_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_cs.h index 6d6a4feb6..cfeca33cb 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_cs.h @@ -1350,10 +1350,10 @@ ret const BYTE resolve_full_16bpp_cs[] = { - 68, 88, 66, 67, 85, 89, - 112, 166, 132, 95, 159, 158, - 148, 255, 182, 126, 49, 65, - 238, 68, 1, 0, 0, 0, + 68, 88, 66, 67, 122, 248, + 161, 239, 38, 34, 59, 122, + 64, 201, 159, 30, 45, 236, + 145, 6, 1, 0, 0, 0, 112, 158, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -1363,7 +1363,7 @@ const BYTE resolve_full_16bpp_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_scaled_cs.h index e1252bd02..189c65905 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_16bpp_scaled_cs.h @@ -1382,10 +1382,10 @@ ret const BYTE resolve_full_16bpp_scaled_cs[] = { - 68, 88, 66, 67, 24, 35, - 27, 103, 137, 85, 99, 31, - 248, 119, 100, 82, 1, 5, - 211, 65, 1, 0, 0, 0, + 68, 88, 66, 67, 17, 196, + 248, 162, 148, 187, 123, 170, + 150, 149, 87, 23, 157, 34, + 89, 51, 1, 0, 0, 0, 188, 160, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -1395,7 +1395,7 @@ const BYTE resolve_full_16bpp_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_cs.h index e5857d3e0..3c923d804 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_cs.h @@ -1354,10 +1354,10 @@ ret const BYTE resolve_full_32bpp_cs[] = { - 68, 88, 66, 67, 191, 202, - 187, 105, 122, 172, 254, 124, - 230, 42, 42, 152, 112, 61, - 118, 194, 1, 0, 0, 0, + 68, 88, 66, 67, 6, 223, + 221, 81, 201, 228, 242, 38, + 30, 228, 108, 198, 29, 216, + 108, 219, 1, 0, 0, 0, 220, 157, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -1367,7 +1367,7 @@ const BYTE resolve_full_32bpp_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_scaled_cs.h index 1d0a0d2fe..89ca2467e 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_32bpp_scaled_cs.h @@ -1397,10 +1397,10 @@ ret const BYTE resolve_full_32bpp_scaled_cs[] = { - 68, 88, 66, 67, 159, 80, - 95, 78, 198, 213, 86, 238, - 93, 9, 250, 90, 57, 206, - 56, 203, 1, 0, 0, 0, + 68, 88, 66, 67, 19, 170, + 1, 90, 249, 15, 80, 164, + 255, 208, 31, 117, 114, 147, + 62, 94, 1, 0, 0, 0, 4, 162, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -1410,7 +1410,7 @@ const BYTE resolve_full_32bpp_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_cs.h index b8e43bbe1..eb712a645 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_cs.h @@ -1275,10 +1275,10 @@ ret const BYTE resolve_full_64bpp_cs[] = { - 68, 88, 66, 67, 123, 61, - 63, 178, 155, 115, 88, 68, - 202, 19, 87, 198, 58, 252, - 174, 110, 1, 0, 0, 0, + 68, 88, 66, 67, 87, 220, + 183, 187, 206, 232, 244, 147, + 255, 195, 21, 134, 69, 222, + 224, 255, 1, 0, 0, 0, 232, 147, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -1288,7 +1288,7 @@ const BYTE resolve_full_64bpp_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_scaled_cs.h index 085b4df2e..3bd227749 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_64bpp_scaled_cs.h @@ -1338,10 +1338,10 @@ ret const BYTE resolve_full_64bpp_scaled_cs[] = { - 68, 88, 66, 67, 192, 10, - 30, 169, 238, 27, 64, 245, - 27, 79, 191, 206, 88, 74, - 18, 223, 1, 0, 0, 0, + 68, 88, 66, 67, 60, 118, + 161, 100, 5, 152, 135, 130, + 31, 41, 169, 237, 170, 21, + 198, 182, 1, 0, 0, 0, 216, 153, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -1351,7 +1351,7 @@ const BYTE resolve_full_64bpp_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_cs.h index d1b5e34c1..b1a8c9c74 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_cs.h @@ -678,10 +678,10 @@ ret const BYTE resolve_full_8bpp_cs[] = { - 68, 88, 66, 67, 253, 28, - 166, 242, 173, 99, 18, 102, - 240, 221, 130, 124, 127, 224, - 31, 30, 1, 0, 0, 0, + 68, 88, 66, 67, 160, 163, + 233, 191, 143, 128, 203, 231, + 48, 167, 197, 167, 225, 27, + 77, 108, 1, 0, 0, 0, 196, 76, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 208, 2, 0, 0, 224, 2, @@ -691,7 +691,7 @@ const BYTE resolve_full_8bpp_cs[] = 1, 0, 0, 0, 236, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 108, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_scaled_cs.h index 0cc4578ad..5801deccd 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/resolve_full_8bpp_scaled_cs.h @@ -720,10 +720,10 @@ ret const BYTE resolve_full_8bpp_scaled_cs[] = { - 68, 88, 66, 67, 162, 175, - 232, 246, 251, 37, 158, 3, - 20, 94, 73, 161, 62, 53, - 31, 163, 1, 0, 0, 0, + 68, 88, 66, 67, 143, 238, + 173, 2, 246, 240, 28, 191, + 52, 72, 53, 243, 190, 87, + 239, 146, 1, 0, 0, 0, 200, 80, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 56, 3, 0, 0, 72, 3, @@ -733,7 +733,7 @@ const BYTE resolve_full_8bpp_scaled_cs[] = 2, 0, 0, 0, 52, 1, 0, 0, 4, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 212, 2, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_ps.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_ps.h deleted file mode 100644 index 34b882a0d..000000000 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_ps.h +++ /dev/null @@ -1,167 +0,0 @@ -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Resource Bindings: -// -// Name Type Format Dim ID HLSL Bind Count -// ------------------------------ ---------- ------- ----------- ------- -------------- ------ -// xe_sampler_linear_clamp sampler NA NA S0 s0 1 -// xe_texture texture float3 2d T0 t0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_Target 0 xyzw 0 TARGET float xyzw -// -ps_5_1 -dcl_globalFlags refactoringAllowed -dcl_sampler S0[0:0], mode_default, space=0 -dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 -dcl_input_ps linear v0.xy -dcl_output o0.xyzw -dcl_temps 1 -sample_l r0.xyz, v0.xyxx, T0[0].xyzw, S0[0], l(0.000000) -mov o0.xyz, r0.xyzx -mov o0.w, l(1.000000) -ret -// Approximately 4 instruction slots used -#endif - -const BYTE stretch_ps[] = -{ - 68, 88, 66, 67, 42, 105, - 40, 75, 32, 223, 178, 162, - 221, 190, 237, 193, 82, 219, - 83, 57, 1, 0, 0, 0, - 224, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 20, 1, 0, 0, 72, 1, - 0, 0, 124, 1, 0, 0, - 68, 2, 0, 0, 82, 68, - 69, 70, 216, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 60, 0, 0, 0, 1, 5, - 255, 255, 0, 5, 4, 0, - 175, 0, 0, 0, 19, 19, - 68, 37, 60, 0, 0, 0, - 24, 0, 0, 0, 40, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 140, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 164, 0, - 0, 0, 2, 0, 0, 0, - 5, 0, 0, 0, 4, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 1, 0, - 0, 0, 8, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 120, 101, 95, 115, - 97, 109, 112, 108, 101, 114, - 95, 108, 105, 110, 101, 97, - 114, 95, 99, 108, 97, 109, - 112, 0, 120, 101, 95, 116, - 101, 120, 116, 117, 114, 101, - 0, 77, 105, 99, 114, 111, - 115, 111, 102, 116, 32, 40, - 82, 41, 32, 72, 76, 83, - 76, 32, 83, 104, 97, 100, - 101, 114, 32, 67, 111, 109, - 112, 105, 108, 101, 114, 32, - 49, 48, 46, 49, 0, 171, - 73, 83, 71, 78, 44, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 3, 3, 0, 0, 84, 69, - 88, 67, 79, 79, 82, 68, - 0, 171, 171, 171, 79, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 83, 86, 95, 84, - 97, 114, 103, 101, 116, 0, - 171, 171, 83, 72, 69, 88, - 192, 0, 0, 0, 81, 0, - 0, 0, 48, 0, 0, 0, - 106, 8, 0, 1, 90, 0, - 0, 6, 70, 110, 48, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 88, 24, - 0, 7, 70, 126, 48, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 85, 85, 0, 0, 0, 0, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 0, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 1, 0, 0, 0, 72, 0, - 0, 13, 114, 0, 16, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 0, 0, 0, 0, - 70, 126, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 96, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 114, 32, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 130, 32, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 128, 63, - 62, 0, 0, 1, 83, 84, - 65, 84, 148, 0, 0, 0, - 4, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 -}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_adaptive_vs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_adaptive_vs.h index 4c69634b8..a80df6cfc 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_adaptive_vs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_adaptive_vs.h @@ -94,10 +94,10 @@ ret const BYTE tessellation_adaptive_vs[] = { - 68, 88, 66, 67, 223, 69, - 21, 252, 96, 81, 180, 155, - 11, 146, 181, 238, 118, 211, - 188, 154, 1, 0, 0, 0, + 68, 88, 66, 67, 75, 49, + 78, 76, 10, 94, 91, 100, + 187, 17, 145, 80, 196, 255, + 224, 35, 1, 0, 0, 0, 196, 13, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 184, 10, 0, 0, 236, 10, @@ -107,7 +107,7 @@ const BYTE tessellation_adaptive_vs[] = 1, 0, 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 254, 255, 0, 5, 4, 0, + 254, 255, 0, 5, 0, 0, 82, 10, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_indexed_vs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_indexed_vs.h index d5126c625..5362ad6fe 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_indexed_vs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/tessellation_indexed_vs.h @@ -96,10 +96,10 @@ ret const BYTE tessellation_indexed_vs[] = { - 68, 88, 66, 67, 6, 210, - 5, 58, 145, 186, 106, 71, - 191, 85, 65, 89, 187, 76, - 25, 50, 1, 0, 0, 0, + 68, 88, 66, 67, 126, 143, + 61, 16, 42, 166, 147, 123, + 54, 196, 251, 250, 225, 235, + 160, 249, 1, 0, 0, 0, 248, 13, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 184, 10, 0, 0, 236, 10, @@ -109,7 +109,7 @@ const BYTE tessellation_indexed_vs[] = 1, 0, 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 254, 255, 0, 5, 4, 0, + 254, 255, 0, 5, 0, 0, 82, 10, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_128bpb_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_128bpb_cs.h index 73b94fb24..4d3c417b7 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_128bpb_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_128bpb_cs.h @@ -164,10 +164,10 @@ ret const BYTE texture_load_128bpb_cs[] = { - 68, 88, 66, 67, 69, 109, - 54, 62, 246, 252, 13, 179, - 61, 140, 108, 218, 151, 23, - 217, 10, 1, 0, 0, 0, + 68, 88, 66, 67, 202, 63, + 252, 201, 70, 118, 107, 252, + 56, 217, 121, 32, 214, 118, + 48, 42, 1, 0, 0, 0, 92, 19, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -177,7 +177,7 @@ const BYTE texture_load_128bpb_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_128bpb_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_128bpb_scaled_cs.h index 9b102e5b3..ab9c0540c 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_128bpb_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_128bpb_scaled_cs.h @@ -175,10 +175,10 @@ ret const BYTE texture_load_128bpb_scaled_cs[] = { - 68, 88, 66, 67, 152, 21, - 0, 244, 101, 90, 251, 167, - 33, 145, 108, 252, 235, 186, - 133, 5, 1, 0, 0, 0, + 68, 88, 66, 67, 0, 151, + 15, 173, 246, 165, 135, 120, + 79, 233, 110, 0, 24, 192, + 65, 142, 1, 0, 0, 0, 160, 20, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -188,7 +188,7 @@ const BYTE texture_load_128bpb_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_16bpb_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_16bpb_cs.h index e9e64685f..880b2d954 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_16bpb_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_16bpb_cs.h @@ -150,10 +150,10 @@ ret const BYTE texture_load_16bpb_cs[] = { - 68, 88, 66, 67, 174, 93, - 70, 99, 109, 104, 7, 76, - 197, 223, 164, 98, 22, 191, - 35, 145, 1, 0, 0, 0, + 68, 88, 66, 67, 241, 108, + 80, 210, 39, 187, 91, 178, + 204, 21, 81, 58, 61, 11, + 55, 221, 1, 0, 0, 0, 28, 17, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -163,7 +163,7 @@ const BYTE texture_load_16bpb_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_16bpb_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_16bpb_scaled_cs.h index 488d317d3..b7e86471d 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_16bpb_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_16bpb_scaled_cs.h @@ -163,10 +163,10 @@ ret const BYTE texture_load_16bpb_scaled_cs[] = { - 68, 88, 66, 67, 81, 210, - 211, 210, 188, 152, 146, 59, - 242, 122, 231, 17, 215, 172, - 246, 100, 1, 0, 0, 0, + 68, 88, 66, 67, 194, 117, + 247, 194, 178, 87, 120, 28, + 79, 25, 94, 189, 238, 128, + 139, 128, 1, 0, 0, 0, 0, 19, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -176,7 +176,7 @@ const BYTE texture_load_16bpb_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_32bpb_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_32bpb_cs.h index c5a0d2816..db483628e 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_32bpb_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_32bpb_cs.h @@ -161,10 +161,10 @@ ret const BYTE texture_load_32bpb_cs[] = { - 68, 88, 66, 67, 127, 41, - 115, 152, 37, 92, 49, 26, - 80, 31, 185, 102, 249, 41, - 197, 36, 1, 0, 0, 0, + 68, 88, 66, 67, 147, 100, + 112, 89, 242, 160, 225, 236, + 154, 215, 131, 75, 212, 235, + 15, 182, 1, 0, 0, 0, 204, 18, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -174,7 +174,7 @@ const BYTE texture_load_32bpb_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_32bpb_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_32bpb_scaled_cs.h index 2619fa2f4..c4be248cc 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_32bpb_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_32bpb_scaled_cs.h @@ -180,10 +180,10 @@ ret const BYTE texture_load_32bpb_scaled_cs[] = { - 68, 88, 66, 67, 213, 122, - 184, 211, 188, 144, 40, 184, - 157, 227, 165, 48, 220, 115, - 140, 200, 1, 0, 0, 0, + 68, 88, 66, 67, 218, 190, + 24, 130, 153, 203, 104, 119, + 33, 181, 7, 206, 254, 211, + 212, 77, 1, 0, 0, 0, 108, 21, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -193,7 +193,7 @@ const BYTE texture_load_32bpb_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_64bpb_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_64bpb_cs.h index 8f5da5ce0..7a37abea3 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_64bpb_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_64bpb_cs.h @@ -164,10 +164,10 @@ ret const BYTE texture_load_64bpb_cs[] = { - 68, 88, 66, 67, 58, 78, - 250, 228, 232, 27, 64, 158, - 177, 203, 109, 154, 100, 33, - 245, 49, 1, 0, 0, 0, + 68, 88, 66, 67, 92, 16, + 243, 45, 89, 227, 7, 187, + 59, 197, 251, 47, 47, 108, + 92, 190, 1, 0, 0, 0, 104, 19, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -177,7 +177,7 @@ const BYTE texture_load_64bpb_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_64bpb_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_64bpb_scaled_cs.h index 39c9a6d0a..61aad6d3f 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_64bpb_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_64bpb_scaled_cs.h @@ -180,10 +180,10 @@ ret const BYTE texture_load_64bpb_scaled_cs[] = { - 68, 88, 66, 67, 146, 51, - 188, 210, 64, 194, 16, 242, - 150, 245, 136, 237, 175, 242, - 242, 204, 1, 0, 0, 0, + 68, 88, 66, 67, 129, 207, + 119, 119, 100, 163, 90, 188, + 114, 227, 54, 159, 242, 90, + 188, 99, 1, 0, 0, 0, 108, 21, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -193,7 +193,7 @@ const BYTE texture_load_64bpb_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_8bpb_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_8bpb_cs.h index 5dfaf8bb6..7d294832c 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_8bpb_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_8bpb_cs.h @@ -139,10 +139,10 @@ ret const BYTE texture_load_8bpb_cs[] = { - 68, 88, 66, 67, 94, 187, - 134, 253, 107, 160, 196, 222, - 117, 123, 98, 19, 188, 66, - 171, 48, 1, 0, 0, 0, + 68, 88, 66, 67, 78, 119, + 192, 2, 178, 124, 184, 10, + 189, 127, 69, 194, 97, 210, + 18, 48, 1, 0, 0, 0, 36, 16, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -152,7 +152,7 @@ const BYTE texture_load_8bpb_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_8bpb_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_8bpb_scaled_cs.h index 855c58e75..4a3f556f9 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_8bpb_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_8bpb_scaled_cs.h @@ -151,10 +151,10 @@ ret const BYTE texture_load_8bpb_scaled_cs[] = { - 68, 88, 66, 67, 240, 96, - 141, 60, 254, 240, 162, 218, - 255, 165, 237, 94, 178, 76, - 40, 219, 1, 0, 0, 0, + 68, 88, 66, 67, 104, 142, + 181, 219, 239, 205, 13, 249, + 232, 59, 4, 117, 104, 185, + 138, 244, 1, 0, 0, 0, 252, 17, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -164,7 +164,7 @@ const BYTE texture_load_8bpb_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_ctx1_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_ctx1_cs.h index a275eadb2..706bee507 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_ctx1_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_ctx1_cs.h @@ -498,10 +498,10 @@ ret const BYTE texture_load_ctx1_cs[] = { - 68, 88, 66, 67, 218, 197, - 112, 160, 14, 201, 105, 66, - 53, 47, 83, 103, 144, 212, - 96, 106, 1, 0, 0, 0, + 68, 88, 66, 67, 224, 177, + 169, 46, 201, 156, 106, 177, + 123, 25, 97, 64, 20, 172, + 37, 106, 1, 0, 0, 0, 116, 65, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -511,7 +511,7 @@ const BYTE texture_load_ctx1_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_float_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_float_cs.h index 8dced34cb..143b2359f 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_float_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_float_cs.h @@ -193,10 +193,10 @@ ret const BYTE texture_load_depth_float_cs[] = { - 68, 88, 66, 67, 204, 91, - 20, 67, 68, 27, 100, 159, - 55, 40, 24, 32, 240, 93, - 21, 161, 1, 0, 0, 0, + 68, 88, 66, 67, 118, 118, + 229, 207, 223, 43, 191, 129, + 46, 182, 152, 168, 144, 51, + 219, 93, 1, 0, 0, 0, 180, 23, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -206,7 +206,7 @@ const BYTE texture_load_depth_float_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_float_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_float_scaled_cs.h index d73996e19..31ec2d1af 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_float_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_float_scaled_cs.h @@ -212,10 +212,10 @@ ret const BYTE texture_load_depth_float_scaled_cs[] = { - 68, 88, 66, 67, 97, 66, - 8, 141, 64, 232, 246, 129, - 214, 180, 240, 206, 10, 93, - 136, 7, 1, 0, 0, 0, + 68, 88, 66, 67, 88, 225, + 211, 33, 229, 40, 36, 2, + 174, 238, 58, 147, 100, 14, + 137, 242, 1, 0, 0, 0, 84, 26, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -225,7 +225,7 @@ const BYTE texture_load_depth_float_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_unorm_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_unorm_cs.h index 1824bb5c2..2aa17af24 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_unorm_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_unorm_cs.h @@ -171,10 +171,10 @@ ret const BYTE texture_load_depth_unorm_cs[] = { - 68, 88, 66, 67, 69, 6, - 138, 33, 133, 185, 5, 144, - 97, 147, 106, 215, 106, 118, - 65, 58, 1, 0, 0, 0, + 68, 88, 66, 67, 164, 201, + 22, 59, 241, 134, 166, 121, + 89, 49, 109, 127, 19, 134, + 179, 77, 1, 0, 0, 0, 28, 20, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -184,7 +184,7 @@ const BYTE texture_load_depth_unorm_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_unorm_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_unorm_scaled_cs.h index d877269d3..60e54685b 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_unorm_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_depth_unorm_scaled_cs.h @@ -190,10 +190,10 @@ ret const BYTE texture_load_depth_unorm_scaled_cs[] = { - 68, 88, 66, 67, 171, 5, - 112, 51, 93, 95, 86, 30, - 36, 93, 246, 161, 8, 191, - 36, 204, 1, 0, 0, 0, + 68, 88, 66, 67, 46, 48, + 136, 105, 27, 59, 153, 68, + 207, 149, 101, 215, 91, 14, + 213, 200, 1, 0, 0, 0, 188, 22, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -203,7 +203,7 @@ const BYTE texture_load_depth_unorm_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxn_rg8_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxn_rg8_cs.h index 0c1582b51..e2f833cab 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxn_rg8_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxn_rg8_cs.h @@ -756,10 +756,10 @@ ret const BYTE texture_load_dxn_rg8_cs[] = { - 68, 88, 66, 67, 211, 36, - 163, 209, 153, 81, 143, 128, - 61, 32, 177, 173, 61, 97, - 58, 73, 1, 0, 0, 0, + 68, 88, 66, 67, 58, 123, + 85, 153, 121, 158, 245, 188, + 30, 31, 213, 99, 33, 63, + 238, 113, 1, 0, 0, 0, 152, 107, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -769,7 +769,7 @@ const BYTE texture_load_dxn_rg8_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt1_rgba8_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt1_rgba8_cs.h index f19eb9d29..d929471f2 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt1_rgba8_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt1_rgba8_cs.h @@ -964,10 +964,10 @@ ret const BYTE texture_load_dxt1_rgba8_cs[] = { - 68, 88, 66, 67, 171, 63, - 106, 29, 93, 176, 47, 157, - 131, 34, 243, 102, 115, 232, - 70, 144, 1, 0, 0, 0, + 68, 88, 66, 67, 162, 2, + 233, 77, 119, 170, 48, 9, + 89, 123, 33, 178, 116, 19, + 98, 50, 1, 0, 0, 0, 72, 139, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -977,7 +977,7 @@ const BYTE texture_load_dxt1_rgba8_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3_rgba8_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3_rgba8_cs.h index e28a1380c..153919e73 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3_rgba8_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3_rgba8_cs.h @@ -402,10 +402,10 @@ ret const BYTE texture_load_dxt3_rgba8_cs[] = { - 68, 88, 66, 67, 202, 139, - 131, 58, 228, 184, 167, 198, - 214, 82, 38, 18, 195, 26, - 166, 103, 1, 0, 0, 0, + 68, 88, 66, 67, 12, 204, + 97, 23, 183, 245, 147, 165, + 41, 149, 238, 95, 107, 29, + 121, 103, 1, 0, 0, 0, 200, 53, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -415,7 +415,7 @@ const BYTE texture_load_dxt3_rgba8_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3a_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3a_cs.h index d9525a0e3..a5cb13f4d 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3a_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3a_cs.h @@ -223,10 +223,10 @@ ret const BYTE texture_load_dxt3a_cs[] = { - 68, 88, 66, 67, 161, 154, - 167, 17, 42, 20, 207, 220, - 166, 224, 185, 142, 22, 195, - 81, 32, 1, 0, 0, 0, + 68, 88, 66, 67, 104, 192, + 139, 183, 85, 14, 81, 232, + 35, 168, 116, 5, 160, 169, + 85, 249, 1, 0, 0, 0, 208, 28, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -236,7 +236,7 @@ const BYTE texture_load_dxt3a_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3aas1111_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3aas1111_cs.h index 70f0637e5..0b2a5e780 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3aas1111_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt3aas1111_cs.h @@ -388,10 +388,10 @@ ret const BYTE texture_load_dxt3aas1111_cs[] = { - 68, 88, 66, 67, 90, 5, - 35, 3, 92, 51, 204, 73, - 141, 97, 178, 144, 202, 117, - 220, 55, 1, 0, 0, 0, + 68, 88, 66, 67, 22, 23, + 173, 181, 12, 133, 91, 227, + 193, 248, 70, 118, 201, 133, + 160, 27, 1, 0, 0, 0, 116, 60, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -401,7 +401,7 @@ const BYTE texture_load_dxt3aas1111_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt5_rgba8_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt5_rgba8_cs.h index 4b7c67107..4ed638286 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt5_rgba8_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt5_rgba8_cs.h @@ -904,10 +904,10 @@ ret const BYTE texture_load_dxt5_rgba8_cs[] = { - 68, 88, 66, 67, 180, 185, - 178, 42, 202, 99, 164, 29, - 112, 142, 14, 240, 160, 77, - 17, 147, 1, 0, 0, 0, + 68, 88, 66, 67, 174, 151, + 189, 10, 239, 215, 42, 88, + 164, 121, 146, 214, 247, 6, + 218, 92, 1, 0, 0, 0, 136, 117, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -917,7 +917,7 @@ const BYTE texture_load_dxt5_rgba8_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt5a_r8_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt5a_r8_cs.h index df0266f2c..6f24b0572 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt5a_r8_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_dxt5a_r8_cs.h @@ -820,10 +820,10 @@ ret const BYTE texture_load_dxt5a_r8_cs[] = { - 68, 88, 66, 67, 26, 197, - 196, 184, 97, 248, 148, 119, - 98, 119, 209, 241, 44, 179, - 197, 227, 1, 0, 0, 0, + 68, 88, 66, 67, 254, 188, + 43, 237, 174, 91, 14, 57, + 46, 249, 95, 48, 189, 201, + 171, 161, 1, 0, 0, 0, 36, 112, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -833,7 +833,7 @@ const BYTE texture_load_dxt5a_r8_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_cs.h index d4719dafd..67c0cbdb1 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_cs.h @@ -206,10 +206,10 @@ ret const BYTE texture_load_r10g11b11_rgba16_cs[] = { - 68, 88, 66, 67, 131, 13, - 20, 178, 131, 231, 128, 185, - 113, 12, 55, 205, 96, 44, - 175, 81, 1, 0, 0, 0, + 68, 88, 66, 67, 47, 2, + 160, 5, 228, 91, 168, 34, + 190, 254, 217, 6, 7, 78, + 252, 141, 1, 0, 0, 0, 44, 26, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -219,7 +219,7 @@ const BYTE texture_load_r10g11b11_rgba16_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_scaled_cs.h index 867dace24..ff438bf21 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_scaled_cs.h @@ -224,10 +224,10 @@ ret const BYTE texture_load_r10g11b11_rgba16_scaled_cs[] = { - 68, 88, 66, 67, 155, 144, - 74, 111, 245, 166, 226, 11, - 144, 219, 119, 58, 140, 196, - 87, 164, 1, 0, 0, 0, + 68, 88, 66, 67, 20, 16, + 81, 114, 70, 228, 22, 196, + 107, 196, 190, 30, 167, 28, + 162, 144, 1, 0, 0, 0, 176, 28, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -237,7 +237,7 @@ const BYTE texture_load_r10g11b11_rgba16_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_snorm_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_snorm_cs.h index d0f2a724a..a64921485 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_snorm_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_snorm_cs.h @@ -316,10 +316,10 @@ ret const BYTE texture_load_r10g11b11_rgba16_snorm_cs[] = { - 68, 88, 66, 67, 132, 107, - 13, 78, 171, 0, 197, 83, - 243, 104, 225, 235, 102, 35, - 191, 255, 1, 0, 0, 0, + 68, 88, 66, 67, 126, 137, + 222, 85, 154, 50, 59, 65, + 64, 122, 77, 236, 149, 56, + 130, 232, 1, 0, 0, 0, 228, 41, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -329,7 +329,7 @@ const BYTE texture_load_r10g11b11_rgba16_snorm_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_snorm_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_snorm_scaled_cs.h index b5fa9cff7..980a7d8bb 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_snorm_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r10g11b11_rgba16_snorm_scaled_cs.h @@ -334,10 +334,10 @@ ret const BYTE texture_load_r10g11b11_rgba16_snorm_scaled_cs[] = { - 68, 88, 66, 67, 162, 29, - 212, 129, 112, 166, 188, 86, - 123, 71, 28, 10, 208, 33, - 149, 252, 1, 0, 0, 0, + 68, 88, 66, 67, 143, 241, + 241, 106, 117, 162, 243, 107, + 211, 190, 66, 35, 157, 41, + 39, 22, 1, 0, 0, 0, 104, 44, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -347,7 +347,7 @@ const BYTE texture_load_r10g11b11_rgba16_snorm_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_cs.h index a9c2bfa67..571407a24 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_cs.h @@ -210,10 +210,10 @@ ret const BYTE texture_load_r11g11b10_rgba16_cs[] = { - 68, 88, 66, 67, 140, 202, - 56, 216, 12, 60, 186, 15, - 110, 177, 222, 251, 148, 130, - 188, 179, 1, 0, 0, 0, + 68, 88, 66, 67, 128, 35, + 108, 79, 134, 63, 204, 101, + 37, 250, 141, 229, 110, 97, + 86, 185, 1, 0, 0, 0, 124, 26, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -223,7 +223,7 @@ const BYTE texture_load_r11g11b10_rgba16_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_scaled_cs.h index dcd872964..dc090002d 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_scaled_cs.h @@ -228,10 +228,10 @@ ret const BYTE texture_load_r11g11b10_rgba16_scaled_cs[] = { - 68, 88, 66, 67, 137, 198, - 120, 58, 180, 179, 225, 37, - 185, 213, 74, 210, 172, 40, - 217, 34, 1, 0, 0, 0, + 68, 88, 66, 67, 71, 13, + 151, 213, 246, 156, 242, 12, + 33, 146, 244, 98, 204, 37, + 211, 102, 1, 0, 0, 0, 0, 29, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -241,7 +241,7 @@ const BYTE texture_load_r11g11b10_rgba16_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_snorm_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_snorm_cs.h index 655fc1cc3..41980c994 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_snorm_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_snorm_cs.h @@ -316,10 +316,10 @@ ret const BYTE texture_load_r11g11b10_rgba16_snorm_cs[] = { - 68, 88, 66, 67, 213, 22, - 164, 196, 90, 153, 244, 222, - 212, 71, 213, 96, 207, 65, - 216, 237, 1, 0, 0, 0, + 68, 88, 66, 67, 148, 131, + 162, 115, 4, 155, 42, 57, + 217, 241, 77, 237, 50, 228, + 133, 33, 1, 0, 0, 0, 228, 41, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -329,7 +329,7 @@ const BYTE texture_load_r11g11b10_rgba16_snorm_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_snorm_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_snorm_scaled_cs.h index 0e333a659..500db9f89 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_snorm_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r11g11b10_rgba16_snorm_scaled_cs.h @@ -334,10 +334,10 @@ ret const BYTE texture_load_r11g11b10_rgba16_snorm_scaled_cs[] = { - 68, 88, 66, 67, 161, 142, - 120, 161, 78, 207, 43, 55, - 216, 104, 110, 214, 21, 94, - 233, 199, 1, 0, 0, 0, + 68, 88, 66, 67, 75, 177, + 94, 135, 174, 139, 119, 66, + 179, 219, 255, 123, 200, 30, + 105, 239, 1, 0, 0, 0, 104, 44, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -347,7 +347,7 @@ const BYTE texture_load_r11g11b10_rgba16_snorm_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r4g4b4a4_b4g4r4a4_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r4g4b4a4_b4g4r4a4_cs.h index 351327ab6..bbdade597 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r4g4b4a4_b4g4r4a4_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r4g4b4a4_b4g4r4a4_cs.h @@ -164,10 +164,10 @@ ret const BYTE texture_load_r4g4b4a4_b4g4r4a4_cs[] = { - 68, 88, 66, 67, 123, 178, - 4, 149, 160, 65, 196, 92, - 197, 33, 231, 200, 130, 56, - 69, 71, 1, 0, 0, 0, + 68, 88, 66, 67, 49, 31, + 131, 53, 100, 96, 178, 5, + 2, 82, 81, 83, 23, 105, + 45, 74, 1, 0, 0, 0, 28, 19, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -177,7 +177,7 @@ const BYTE texture_load_r4g4b4a4_b4g4r4a4_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r4g4b4a4_b4g4r4a4_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r4g4b4a4_b4g4r4a4_scaled_cs.h index c96fe8c51..7e639b240 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r4g4b4a4_b4g4r4a4_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r4g4b4a4_b4g4r4a4_scaled_cs.h @@ -177,10 +177,10 @@ ret const BYTE texture_load_r4g4b4a4_b4g4r4a4_scaled_cs[] = { - 68, 88, 66, 67, 217, 199, - 168, 29, 226, 179, 202, 87, - 196, 221, 10, 176, 71, 209, - 93, 59, 1, 0, 0, 0, + 68, 88, 66, 67, 77, 180, + 20, 47, 174, 216, 74, 169, + 81, 35, 81, 193, 58, 15, + 131, 7, 1, 0, 0, 0, 0, 21, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -190,7 +190,7 @@ const BYTE texture_load_r4g4b4a4_b4g4r4a4_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b5a1_b5g5r5a1_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b5a1_b5g5r5a1_cs.h index ba3b52739..fcd9812cd 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b5a1_b5g5r5a1_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b5a1_b5g5r5a1_cs.h @@ -164,10 +164,10 @@ ret const BYTE texture_load_r5g5b5a1_b5g5r5a1_cs[] = { - 68, 88, 66, 67, 135, 143, - 167, 233, 77, 208, 99, 166, - 216, 43, 121, 223, 203, 20, - 55, 249, 1, 0, 0, 0, + 68, 88, 66, 67, 142, 91, + 118, 66, 49, 193, 130, 167, + 134, 65, 2, 182, 229, 160, + 140, 89, 1, 0, 0, 0, 28, 19, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -177,7 +177,7 @@ const BYTE texture_load_r5g5b5a1_b5g5r5a1_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b5a1_b5g5r5a1_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b5a1_b5g5r5a1_scaled_cs.h index 35ef6c89b..12cc39ab1 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b5a1_b5g5r5a1_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b5a1_b5g5r5a1_scaled_cs.h @@ -177,10 +177,10 @@ ret const BYTE texture_load_r5g5b5a1_b5g5r5a1_scaled_cs[] = { - 68, 88, 66, 67, 82, 18, - 33, 217, 174, 124, 154, 217, - 180, 135, 1, 110, 160, 231, - 43, 26, 1, 0, 0, 0, + 68, 88, 66, 67, 118, 127, + 179, 72, 186, 4, 108, 94, + 144, 13, 145, 177, 14, 255, + 152, 175, 1, 0, 0, 0, 0, 21, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -190,7 +190,7 @@ const BYTE texture_load_r5g5b5a1_b5g5r5a1_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b6_b5g6r5_swizzle_rbga_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b6_b5g6r5_swizzle_rbga_cs.h index a6d581bb4..71dfe6ea8 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b6_b5g6r5_swizzle_rbga_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b6_b5g6r5_swizzle_rbga_cs.h @@ -160,10 +160,10 @@ ret const BYTE texture_load_r5g5b6_b5g6r5_swizzle_rbga_cs[] = { - 68, 88, 66, 67, 53, 93, - 80, 84, 203, 45, 115, 170, - 188, 117, 87, 176, 190, 115, - 70, 191, 1, 0, 0, 0, + 68, 88, 66, 67, 242, 245, + 114, 197, 70, 66, 49, 31, + 114, 195, 63, 33, 103, 191, + 133, 52, 1, 0, 0, 0, 148, 18, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -173,7 +173,7 @@ const BYTE texture_load_r5g5b6_b5g6r5_swizzle_rbga_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b6_b5g6r5_swizzle_rbga_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b6_b5g6r5_swizzle_rbga_scaled_cs.h index b7ed1f186..30c8a1951 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b6_b5g6r5_swizzle_rbga_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g5b6_b5g6r5_swizzle_rbga_scaled_cs.h @@ -173,10 +173,10 @@ ret const BYTE texture_load_r5g5b6_b5g6r5_swizzle_rbga_scaled_cs[] = { - 68, 88, 66, 67, 4, 135, - 9, 244, 156, 123, 41, 147, - 145, 234, 137, 185, 2, 70, - 230, 241, 1, 0, 0, 0, + 68, 88, 66, 67, 224, 225, + 230, 21, 25, 203, 114, 212, + 208, 192, 99, 236, 28, 219, + 255, 255, 1, 0, 0, 0, 120, 20, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -186,7 +186,7 @@ const BYTE texture_load_r5g5b6_b5g6r5_swizzle_rbga_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g6b5_b5g6r5_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g6b5_b5g6r5_cs.h index fa0ac7235..279724d2f 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g6b5_b5g6r5_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g6b5_b5g6r5_cs.h @@ -164,10 +164,10 @@ ret const BYTE texture_load_r5g6b5_b5g6r5_cs[] = { - 68, 88, 66, 67, 27, 115, - 39, 235, 56, 147, 179, 61, - 11, 80, 115, 207, 237, 93, - 153, 217, 1, 0, 0, 0, + 68, 88, 66, 67, 127, 73, + 53, 241, 142, 177, 61, 101, + 28, 212, 103, 166, 115, 116, + 219, 222, 1, 0, 0, 0, 28, 19, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -177,7 +177,7 @@ const BYTE texture_load_r5g6b5_b5g6r5_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g6b5_b5g6r5_scaled_cs.h b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g6b5_b5g6r5_scaled_cs.h index 8c6ef5b3e..d9849b0c3 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g6b5_b5g6r5_scaled_cs.h +++ b/src/xenia/gpu/shaders/bytecode/d3d12_5_1/texture_load_r5g6b5_b5g6r5_scaled_cs.h @@ -177,10 +177,10 @@ ret const BYTE texture_load_r5g6b5_b5g6r5_scaled_cs[] = { - 68, 88, 66, 67, 188, 32, - 89, 167, 62, 193, 205, 149, - 127, 86, 15, 49, 116, 163, - 146, 187, 1, 0, 0, 0, + 68, 88, 66, 67, 80, 139, + 235, 77, 41, 132, 249, 150, + 54, 120, 97, 180, 209, 107, + 181, 68, 1, 0, 0, 0, 0, 21, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 4, 0, 0, 44, 4, @@ -190,7 +190,7 @@ const BYTE texture_load_r5g6b5_b5g6r5_scaled_cs[] = 1, 0, 0, 0, 248, 0, 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 83, 67, 0, 5, 4, 0, + 83, 67, 0, 5, 0, 0, 181, 3, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/gpu/shaders/fullscreen_tc.vs.hlsl b/src/xenia/gpu/shaders/fullscreen_tc.vs.hlsl deleted file mode 100644 index e862f0b7c..000000000 --- a/src/xenia/gpu/shaders/fullscreen_tc.vs.hlsl +++ /dev/null @@ -1,7 +0,0 @@ -// A triangle covering the whole viewport. -void main(uint xe_vertex_id : SV_VertexID, out float2 xe_texcoord : TEXCOORD, - out float4 xe_position : SV_Position) { - xe_texcoord = float2(uint2(xe_vertex_id, xe_vertex_id << 1u) & 2u); - xe_position = - float4(xe_texcoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); -} diff --git a/src/xenia/gpu/shaders/fxaa.cs.hlsl b/src/xenia/gpu/shaders/fxaa.cs.hlsl new file mode 100644 index 000000000..74a9032bb --- /dev/null +++ b/src/xenia/gpu/shaders/fxaa.cs.hlsl @@ -0,0 +1,5 @@ +#define FXAA_QUALITY__PRESET 12 +#define FXAA_QUALITY__SUBPIX 0.75 +#define FXAA_QUALITY__EDGE_THRESHOLD 0.166 +#define FXAA_QUALITY__EDGE_THRESHOLD_MIN 0.0833 +#include "fxaa.hlsli" diff --git a/src/xenia/gpu/shaders/fxaa.hlsli b/src/xenia/gpu/shaders/fxaa.hlsli new file mode 100644 index 000000000..f20fdd4a5 --- /dev/null +++ b/src/xenia/gpu/shaders/fxaa.hlsli @@ -0,0 +1,48 @@ +// Inputs: +// - FXAA_QUALITY__PRESET +// - FXAA_QUALITY__SUBPIX +// - FXAA_QUALITY__EDGE_THRESHOLD +// - FXAA_QUALITY__EDGE_THRESHOLD_MIN + +// "use of potentially uninitialized variable" in FxaaPixelShader due to the +// early exit with a `return` in the beginning of the function, caused by a bug +// in FXC (the warning is common for early exiting in HLSL in general). +#pragma warning(disable: 4000) + +cbuffer XeApplyGammaRampConstants : register(b0) { + uint2 xe_fxaa_size; + float2 xe_fxaa_size_inv; +}; + +RWTexture2D xe_fxaa_dest : register(u0); +Texture2D xe_fxaa_source : register(t0); +SamplerState xe_sampler_linear_clamp : register(s0); + +#define FXAA_PC 1 +#define FXAA_HLSL_5 1 +#include "../../../../third_party/fxaa/FXAA3_11.h" + +[numthreads(8, 8, 1)] +void main(uint3 xe_thread_id : SV_DispatchThreadID) { + [branch] if (any(xe_thread_id.xy >= xe_fxaa_size)) { + return; + } + FxaaTex fxaa_texture; + fxaa_texture.smpl = xe_sampler_linear_clamp; + fxaa_texture.tex = xe_fxaa_source; + // Force alpha to 1 to simplify calculations, won't need it anymore anyway. + xe_fxaa_dest[xe_thread_id.xy] = + float4( + FxaaPixelShader( + (float2(xe_thread_id.xy) + 0.5) * xe_fxaa_size_inv, + (float2(xe_thread_id.xy).xyxy + float2(0.0, 1.0).xxyy) * + xe_fxaa_size_inv.xyxy, + fxaa_texture, fxaa_texture, fxaa_texture, xe_fxaa_size_inv, + float2(-0.5, 0.5).xxyy * xe_fxaa_size_inv.xyxy, + float2(-2.0, 2.0).xxyy * xe_fxaa_size_inv.xyxy, + float2(8.0, -4.0).xxyy * xe_fxaa_size_inv.xyxy, + FXAA_QUALITY__SUBPIX, FXAA_QUALITY__EDGE_THRESHOLD, + FXAA_QUALITY__EDGE_THRESHOLD_MIN, 8.0, 0.125, 0.05, + float4(1.0, -1.0, 0.25, -0.25)).rgb, + 1.0); +} diff --git a/src/xenia/gpu/shaders/fxaa_extreme.cs.hlsl b/src/xenia/gpu/shaders/fxaa_extreme.cs.hlsl new file mode 100644 index 000000000..68eb85b96 --- /dev/null +++ b/src/xenia/gpu/shaders/fxaa_extreme.cs.hlsl @@ -0,0 +1,5 @@ +#define FXAA_QUALITY__PRESET 39 +#define FXAA_QUALITY__SUBPIX 1.0 +#define FXAA_QUALITY__EDGE_THRESHOLD 0.063 +#define FXAA_QUALITY__EDGE_THRESHOLD_MIN 0.0312 +#include "fxaa.hlsli" diff --git a/src/xenia/gpu/shaders/stretch.ps.hlsl b/src/xenia/gpu/shaders/stretch.ps.hlsl deleted file mode 100644 index c7a568434..000000000 --- a/src/xenia/gpu/shaders/stretch.ps.hlsl +++ /dev/null @@ -1,8 +0,0 @@ -Texture2D xe_texture : register(t0); -SamplerState xe_sampler_linear_clamp : register(s0); - -float4 main(float2 xe_texcoord : TEXCOORD) : SV_Target { - // Force alpha to 1 to make sure the surface won't be translucent. - return float4( - xe_texture.SampleLevel(xe_sampler_linear_clamp, xe_texcoord, 0.0f), 1.0f); -} diff --git a/src/xenia/gpu/shaders/stretch_gamma.ps.hlsl b/src/xenia/gpu/shaders/stretch_gamma.ps.hlsl deleted file mode 100644 index a13e333a8..000000000 --- a/src/xenia/gpu/shaders/stretch_gamma.ps.hlsl +++ /dev/null @@ -1,20 +0,0 @@ -Texture2D xe_texture : register(t0); -Texture1D xe_gamma_ramp : register(t1); -SamplerState xe_sampler_linear_clamp : register(s0); -cbuffer XeStretchGammaRootConstants : register(b0) { - float xe_gamma_ramp_inv_size; -}; - -float4 main(float2 xe_texcoord : TEXCOORD) : SV_Target { - float3 color = - xe_texture.SampleLevel(xe_sampler_linear_clamp, xe_texcoord, 0.0f); - // The center of the first texel of the LUT contains the value for 0, and the - // center of the last texel contains the value for 1. - color = - color * (1.0f - xe_gamma_ramp_inv_size) + (0.5 * xe_gamma_ramp_inv_size); - color.r = xe_gamma_ramp.SampleLevel(xe_sampler_linear_clamp, color.r, 0.0f).r; - color.g = xe_gamma_ramp.SampleLevel(xe_sampler_linear_clamp, color.g, 0.0f).g; - color.b = xe_gamma_ramp.SampleLevel(xe_sampler_linear_clamp, color.b, 0.0f).b; - // Force alpha to 1 to make sure the surface won't be translucent. - return float4(color, 1.0f); -} diff --git a/src/xenia/gpu/trace_dump.cc b/src/xenia/gpu/trace_dump.cc index a78964d14..2932a4110 100644 --- a/src/xenia/gpu/trace_dump.cc +++ b/src/xenia/gpu/trace_dump.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -18,6 +18,7 @@ #include "xenia/gpu/graphics_system.h" #include "xenia/memory.h" #include "xenia/ui/file_picker.h" +#include "xenia/ui/presenter.h" #include "xenia/ui/window.h" #include "xenia/xbox.h" @@ -94,7 +95,8 @@ bool TraceDump::Setup() { // Create the emulator but don't initialize so we can setup the window. emulator_ = std::make_unique("", "", "", ""); X_STATUS result = emulator_->Setup( - nullptr, nullptr, [this]() { return CreateGraphicsSystem(); }, nullptr); + nullptr, nullptr, nullptr, [this]() { return CreateGraphicsSystem(); }, + nullptr); if (XFAILED(result)) { XELOGE("Failed to setup emulator: {:08X}", result); return false; @@ -125,18 +127,19 @@ int TraceDump::Run() { // Capture. int result = 0; - auto raw_image = graphics_system_->Capture(); - if (raw_image) { + ui::Presenter* presenter = graphics_system_->presenter(); + ui::RawImage raw_image; + if (presenter && presenter->CaptureGuestOutput(raw_image)) { // Save framebuffer png. auto png_path = base_output_path_.replace_extension(".png"); auto handle = filesystem::OpenFile(png_path, "wb"); auto callback = [](void* context, void* data, int size) { fwrite(data, 1, size, (FILE*)context); }; - stbi_write_png_to_func(callback, handle, static_cast(raw_image->width), - static_cast(raw_image->height), 4, - raw_image->data.data(), - static_cast(raw_image->stride)); + stbi_write_png_to_func(callback, handle, static_cast(raw_image.width), + static_cast(raw_image.height), 4, + raw_image.data.data(), + static_cast(raw_image.stride)); fclose(handle); } else { result = 1; diff --git a/src/xenia/gpu/trace_player.cc b/src/xenia/gpu/trace_player.cc index 8f2c98b8a..54db1156c 100644 --- a/src/xenia/gpu/trace_player.cc +++ b/src/xenia/gpu/trace_player.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -87,15 +87,19 @@ void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size, TracePlaybackMode playback_mode, bool clear_caches) { playing_trace_ = true; + // Pass a copy of present_last_copy_ to the thread so it's not accessible by + // multiple threads at once. + bool present_last_copy = present_last_copy_; graphics_system_->command_processor()->CallInThread([=]() { - PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches); + PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches, + present_last_copy); }); } void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size, TracePlaybackMode playback_mode, - bool clear_caches) { + bool clear_caches, bool present_last_copy) { auto memory = graphics_system_->memory(); auto command_processor = graphics_system_->command_processor(); @@ -103,7 +107,10 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, command_processor->ClearCaches(); } - command_processor->set_swap_mode(SwapMode::kIgnored); + if (present_last_copy) { + command_processor->SetIgnoreSwap(true); + } + playback_percent_ = 0; auto trace_end = trace_data + trace_size; @@ -215,8 +222,11 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, } playing_trace_ = false; - command_processor->set_swap_mode(SwapMode::kNormal); - command_processor->IssueSwap(0, 1280, 720); + + if (present_last_copy) { + command_processor->SetIgnoreSwap(false); + command_processor->IssueSwap(0, 1280, 720); + } playback_event_->Set(); } diff --git a/src/xenia/gpu/trace_player.h b/src/xenia/gpu/trace_player.h index d56205d59..cfc2702a1 100644 --- a/src/xenia/gpu/trace_player.h +++ b/src/xenia/gpu/trace_player.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -33,6 +33,9 @@ class TracePlayer : public TraceReader { ~TracePlayer() override; GraphicsSystem* graphics_system() const { return graphics_system_; } + void SetPresentLastCopy(bool present_last_copy) { + present_last_copy_ = present_last_copy; + } int current_frame_index() const { return current_frame_index_; } int current_command_index() const { return current_command_index_; } bool is_playing_trace() const { return playing_trace_; } @@ -51,9 +54,13 @@ class TracePlayer : public TraceReader { void PlayTrace(const uint8_t* trace_data, size_t trace_size, TracePlaybackMode playback_mode, bool clear_caches); void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size, - TracePlaybackMode playback_mode, bool clear_caches); + TracePlaybackMode playback_mode, bool clear_caches, + bool present_last_copy); GraphicsSystem* graphics_system_; + // Whether to present the results of the latest resolve instead of displaying + // the front buffer from the trace. + bool present_last_copy_ = false; int current_frame_index_; int current_command_index_; bool playing_trace_ = false; diff --git a/src/xenia/gpu/trace_viewer.cc b/src/xenia/gpu/trace_viewer.cc index d5f389276..660f24805 100644 --- a/src/xenia/gpu/trace_viewer.cc +++ b/src/xenia/gpu/trace_viewer.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -30,6 +30,9 @@ #include "xenia/memory.h" #include "xenia/ui/file_picker.h" #include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/presenter.h" +#include "xenia/ui/ui_event.h" #include "xenia/ui/virtual_key.h" #include "xenia/ui/window.h" #include "xenia/ui/windowed_app_context.h" @@ -51,7 +54,8 @@ static const ImVec4 kColorIgnored = TraceViewer::TraceViewer(xe::ui::WindowedAppContext& app_context, const std::string_view name) - : xe::ui::WindowedApp(app_context, name, "some.trace") { + : xe::ui::WindowedApp(app_context, name, "some.trace"), + window_listener_(*this) { AddPositionalOption("target_trace_file"); } @@ -102,22 +106,27 @@ bool TraceViewer::OnInitialize() { } bool TraceViewer::Setup() { + enum : size_t { + kZOrderImGui, + kZOrderTraceViewerInput, + }; + // Main display window. assert_true(app_context().IsInUIThread()); - window_ = xe::ui::Window::Create(app_context(), "xenia-gpu-trace-viewer"); - if (!window_->Initialize()) { - XELOGE("Failed to initialize main window"); + window_ = xe::ui::Window::Create(app_context(), "xenia-gpu-trace-viewer", + 1920, 1200); + window_->AddListener(&window_listener_); + window_->AddInputListener(&window_listener_, kZOrderTraceViewerInput); + if (!window_->Open()) { + XELOGE("Failed to open the main window"); return false; } - window_->on_closed.AddListener( - [this](xe::ui::UIEvent* e) { app_context().QuitFromUIThread(); }); - window_->Resize(1920, 1200); // Create the emulator but don't initialize so we can setup the window. emulator_ = std::make_unique("", "", "", ""); X_STATUS result = emulator_->Setup( - window_.get(), nullptr, [this]() { return CreateGraphicsSystem(); }, - nullptr); + window_.get(), nullptr, nullptr, + [this]() { return CreateGraphicsSystem(); }, nullptr); if (XFAILED(result)) { XELOGE("Failed to setup emulator: {:08X}", result); return false; @@ -125,31 +134,55 @@ bool TraceViewer::Setup() { memory_ = emulator_->memory(); graphics_system_ = emulator_->graphics_system(); - window_->set_imgui_input_enabled(true); - - window_->on_key_char.AddListener([&](xe::ui::KeyEvent* e) { - if (e->virtual_key() == xe::ui::VirtualKey::kF5) { - graphics_system_->ClearCaches(); - e->set_handled(true); - } - }); - player_ = std::make_unique(graphics_system_); + player_->SetPresentLastCopy(true); - window_->on_painting.AddListener([this](xe::ui::UIEvent* e) { - DrawUI(); - - // Continuous paint. - window_->Invalidate(); - }); - window_->Invalidate(); + // Setup drawing to the window. + xe::ui::GraphicsProvider& graphics_provider = *graphics_system_->provider(); + presenter_ = graphics_provider.CreatePresenter(); + if (!presenter_) { + XELOGE("Failed to initialize the presenter"); + return false; + } + immediate_drawer_ = graphics_provider.CreateImmediateDrawer(); + if (!immediate_drawer_) { + XELOGE("Failed to initialize the immediate drawer"); + return false; + } + immediate_drawer_->SetPresenter(presenter_.get()); + imgui_drawer_ = + std::make_unique(window_.get(), kZOrderImGui); + imgui_drawer_->SetPresenterAndImmediateDrawer(presenter_.get(), + immediate_drawer_.get()); + trace_viewer_dialog_ = std::unique_ptr( + new TraceViewerDialog(imgui_drawer_.get(), *this)); + window_->SetPresenter(presenter_.get()); return true; } +void TraceViewer::TraceViewerWindowListener::OnClosing(xe::ui::UIEvent& e) { + trace_viewer_.app_context().QuitFromUIThread(); +} + +void TraceViewer::TraceViewerWindowListener::OnKeyDown(xe::ui::KeyEvent& e) { + switch (e.virtual_key()) { + case xe::ui::VirtualKey::kF5: + trace_viewer_.graphics_system_->ClearCaches(); + break; + default: + return; + } + e.set_handled(true); +} + +void TraceViewer::TraceViewerDialog::OnDraw(ImGuiIO& io) { + trace_viewer_.DrawUI(); +} + bool TraceViewer::Load(const std::filesystem::path& trace_file_path) { auto file_name = trace_file_path.filename(); - window_->set_title("Xenia GPU Trace Viewer: " + xe::path_to_utf8(file_name)); + window_->SetTitle("Xenia GPU Trace Viewer: " + xe::path_to_utf8(file_name)); if (!player_->Open(trace_file_path)) { XELOGE("Could not load trace file"); @@ -233,8 +266,9 @@ void TraceViewer::DrawControllerUI() { void TraceViewer::DrawPacketDisassemblerUI() { ImGui::SetNextWindowCollapsed(true, ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos(ImVec2(float(window_->width()) - 500 - 5, 5), - ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos( + ImVec2(float(window_->GetActualLogicalWidth()) - 500 - 5, 5), + ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(500, 300)); if (!ImGui::Begin("Packet Disassembler", nullptr)) { ImGui::End(); @@ -1052,8 +1086,9 @@ void TraceViewer::DrawStateUI() { auto command_processor = graphics_system_->command_processor(); auto& regs = *graphics_system_->register_file(); - ImGui::SetNextWindowPos(ImVec2(float(window_->width()) - 500 - 5, 30), - ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos( + ImVec2(float(window_->GetActualLogicalWidth()) - 500 - 5, 30), + ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(500, 680)); if (!ImGui::Begin("State", nullptr)) { ImGui::End(); diff --git a/src/xenia/gpu/trace_viewer.h b/src/xenia/gpu/trace_viewer.h index 1e22439fb..ecca12792 100644 --- a/src/xenia/gpu/trace_viewer.h +++ b/src/xenia/gpu/trace_viewer.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -18,7 +18,12 @@ #include "xenia/gpu/trace_protocol.h" #include "xenia/gpu/xenos.h" #include "xenia/memory.h" +#include "xenia/ui/imgui_dialog.h" +#include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/presenter.h" #include "xenia/ui/window.h" +#include "xenia/ui/window_listener.h" #include "xenia/ui/windowed_app.h" namespace xe { @@ -38,6 +43,7 @@ class TraceViewer : public xe::ui::WindowedApp { const std::string_view name); virtual std::unique_ptr CreateGraphicsSystem() = 0; + GraphicsSystem* graphics_system() const { return graphics_system_; } void DrawMultilineString(const std::string_view str); @@ -56,13 +62,34 @@ class TraceViewer : public xe::ui::WindowedApp { virtual bool Setup(); - std::unique_ptr window_; - std::unique_ptr emulator_; - Memory* memory_ = nullptr; - GraphicsSystem* graphics_system_ = nullptr; - std::unique_ptr player_; - private: + class TraceViewerWindowListener final : public xe::ui::WindowListener, + public xe::ui::WindowInputListener { + public: + explicit TraceViewerWindowListener(TraceViewer& trace_viewer) + : trace_viewer_(trace_viewer) {} + + void OnClosing(xe::ui::UIEvent& e) override; + + void OnKeyDown(xe::ui::KeyEvent& e) override; + + private: + TraceViewer& trace_viewer_; + }; + + class TraceViewerDialog final : public ui::ImGuiDialog { + public: + explicit TraceViewerDialog(xe::ui::ImGuiDrawer* imgui_drawer, + TraceViewer& trace_viewer) + : xe::ui::ImGuiDialog(imgui_drawer), trace_viewer_(trace_viewer) {} + + protected: + void OnDraw(ImGuiIO& io) override; + + private: + TraceViewer& trace_viewer_; + }; + enum class ShaderDisplayType : int { kUcode, kTranslated, @@ -92,6 +119,20 @@ class TraceViewer : public xe::ui::WindowedApp { void DrawVertexFetcher(Shader* shader, const Shader::VertexBinding& vertex_binding, const xenos::xe_gpu_vertex_fetch_t* fetch); + + TraceViewerWindowListener window_listener_; + + std::unique_ptr window_; + + std::unique_ptr emulator_; + Memory* memory_ = nullptr; + GraphicsSystem* graphics_system_ = nullptr; + std::unique_ptr player_; + + std::unique_ptr presenter_; + std::unique_ptr immediate_drawer_; + std::unique_ptr imgui_drawer_; + std::unique_ptr trace_viewer_dialog_; }; } // namespace gpu diff --git a/src/xenia/gpu/vulkan/buffer_cache.cc b/src/xenia/gpu/vulkan/buffer_cache.cc index 0e4b73163..6ef89b289 100644 --- a/src/xenia/gpu/vulkan/buffer_cache.cc +++ b/src/xenia/gpu/vulkan/buffer_cache.cc @@ -16,6 +16,7 @@ #include "xenia/gpu/gpu_flags.h" #include "xenia/gpu/vulkan/vulkan_gpu_flags.h" #include "xenia/ui/vulkan/vulkan_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_util.h" using namespace xe::gpu::xenos; @@ -91,16 +92,17 @@ void copy_cmp_swap_32_unaligned(void* dest_ptr, const void* src_ptr, } #endif -using xe::ui::vulkan::CheckResult; +using xe::ui::vulkan::util::CheckResult; constexpr VkDeviceSize kConstantRegisterUniformRange = 512 * 4 * 4 + 8 * 4 + 32 * 4; BufferCache::BufferCache(RegisterFile* register_file, Memory* memory, - ui::vulkan::VulkanDevice* device, size_t capacity) - : register_file_(register_file), memory_(memory), device_(device) { + const ui::vulkan::VulkanProvider& provider, + size_t capacity) + : register_file_(register_file), memory_(memory), provider_(provider) { transient_buffer_ = std::make_unique( - device_, + provider_, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, capacity, 256); @@ -109,23 +111,41 @@ BufferCache::BufferCache(RegisterFile* register_file, Memory* memory, BufferCache::~BufferCache() { Shutdown(); } VkResult BufferCache::Initialize() { + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + VkResult status = VK_SUCCESS; + VkMemoryRequirements pool_reqs; transient_buffer_->GetBufferMemoryRequirements(&pool_reqs); - gpu_memory_pool_ = device_->AllocateMemory(pool_reqs); + VkMemoryAllocateInfo pool_allocate_info; + pool_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + pool_allocate_info.pNext = nullptr; + pool_allocate_info.allocationSize = pool_reqs.size; + pool_allocate_info.memoryTypeIndex = ui::vulkan::util::ChooseHostMemoryType( + provider_, pool_reqs.memoryTypeBits, false); + if (pool_allocate_info.memoryTypeIndex == UINT32_MAX) { + return VK_ERROR_INITIALIZATION_FAILED; + } + status = dfn.vkAllocateMemory(device, &pool_allocate_info, nullptr, + &gpu_memory_pool_); + if (status != VK_SUCCESS) { + return status; + } - VkResult status = transient_buffer_->Initialize(gpu_memory_pool_, 0); + status = transient_buffer_->Initialize(gpu_memory_pool_, 0); if (status != VK_SUCCESS) { return status; } // Create a memory allocator for textures. VmaVulkanFunctions vulkan_funcs = {}; - ui::vulkan::FillVMAVulkanFunctions(&vulkan_funcs, *device_); - - VmaAllocatorCreateInfo alloc_info = { - 0, *device_, *device_, 0, 0, nullptr, nullptr, 0, nullptr, &vulkan_funcs, - }; + ui::vulkan::FillVMAVulkanFunctions(&vulkan_funcs, provider_); + VmaAllocatorCreateInfo alloc_info = {}; + alloc_info.physicalDevice = provider_.physical_device(); + alloc_info.device = device; + alloc_info.pVulkanFunctions = &vulkan_funcs; + alloc_info.instance = provider_.instance(); status = vmaCreateAllocator(&alloc_info, &mem_allocator_); if (status != VK_SUCCESS) { return status; @@ -145,7 +165,8 @@ VkResult BufferCache::Initialize() { } VkResult BufferCache::CreateVertexDescriptorPool() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status; std::vector pool_sizes; @@ -154,7 +175,7 @@ VkResult BufferCache::CreateVertexDescriptorPool() { 32 * 16384, }); vertex_descriptor_pool_ = std::make_unique( - *device_, 32 * 16384, pool_sizes); + provider_, 32 * 16384, pool_sizes); // 32 storage buffers available to vertex shader. // TODO(DrChat): In the future, this could hold memexport staging data. @@ -171,7 +192,7 @@ VkResult BufferCache::CreateVertexDescriptorPool() { 1, &binding, }; - status = dfn.vkCreateDescriptorSetLayout(*device_, &layout_info, nullptr, + status = dfn.vkCreateDescriptorSetLayout(device, &layout_info, nullptr, &vertex_descriptor_set_layout_); if (status != VK_SUCCESS) { return status; @@ -183,13 +204,15 @@ VkResult BufferCache::CreateVertexDescriptorPool() { void BufferCache::FreeVertexDescriptorPool() { vertex_descriptor_pool_.reset(); - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - ui::vulkan::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, *device_, - vertex_descriptor_set_layout_); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, + device, vertex_descriptor_set_layout_); } VkResult BufferCache::CreateConstantDescriptorSet() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; // Descriptor pool used for all of our cached descriptors. @@ -207,7 +230,7 @@ VkResult BufferCache::CreateConstantDescriptorSet() { pool_sizes[0].descriptorCount = 2; transient_descriptor_pool_info.poolSizeCount = 1; transient_descriptor_pool_info.pPoolSizes = pool_sizes; - status = dfn.vkCreateDescriptorPool(*device_, &transient_descriptor_pool_info, + status = dfn.vkCreateDescriptorPool(device, &transient_descriptor_pool_info, nullptr, &constant_descriptor_pool_); if (status != VK_SUCCESS) { return status; @@ -240,8 +263,8 @@ VkResult BufferCache::CreateConstantDescriptorSet() { descriptor_set_layout_info.bindingCount = static_cast(xe::countof(bindings)); descriptor_set_layout_info.pBindings = bindings; - status = dfn.vkCreateDescriptorSetLayout(*device_, - &descriptor_set_layout_info, nullptr, + status = dfn.vkCreateDescriptorSetLayout(device, &descriptor_set_layout_info, + nullptr, &constant_descriptor_set_layout_); if (status != VK_SUCCESS) { return status; @@ -256,7 +279,7 @@ VkResult BufferCache::CreateConstantDescriptorSet() { set_alloc_info.descriptorPool = constant_descriptor_pool_; set_alloc_info.descriptorSetCount = 1; set_alloc_info.pSetLayouts = &constant_descriptor_set_layout_; - status = dfn.vkAllocateDescriptorSets(*device_, &set_alloc_info, + status = dfn.vkAllocateDescriptorSets(device, &set_alloc_info, &constant_descriptor_set_); if (status != VK_SUCCESS) { return status; @@ -289,24 +312,26 @@ VkResult BufferCache::CreateConstantDescriptorSet() { fragment_uniform_binding_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; fragment_uniform_binding_write.pBufferInfo = &buffer_info; - dfn.vkUpdateDescriptorSets(*device_, 2, descriptor_writes, 0, nullptr); + dfn.vkUpdateDescriptorSets(device, 2, descriptor_writes, 0, nullptr); return VK_SUCCESS; } void BufferCache::FreeConstantDescriptorSet() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); if (constant_descriptor_set_) { - dfn.vkFreeDescriptorSets(*device_, constant_descriptor_pool_, 1, + dfn.vkFreeDescriptorSets(device, constant_descriptor_pool_, 1, &constant_descriptor_set_); constant_descriptor_set_ = nullptr; } - ui::vulkan::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, *device_, - constant_descriptor_set_layout_); - ui::vulkan::DestroyAndNullHandle(dfn.vkDestroyDescriptorPool, *device_, - constant_descriptor_pool_); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, + device, + constant_descriptor_set_layout_); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorPool, device, + constant_descriptor_pool_); } void BufferCache::Shutdown() { @@ -319,9 +344,10 @@ void BufferCache::Shutdown() { FreeVertexDescriptorPool(); transient_buffer_->Shutdown(); - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - ui::vulkan::DestroyAndNullHandle(dfn.vkFreeMemory, *device_, - gpu_memory_pool_); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device, + gpu_memory_pool_); } std::pair BufferCache::UploadConstantRegisters( @@ -368,7 +394,7 @@ std::pair BufferCache::UploadConstantRegisters( offset, kConstantRegisterUniformRange, }; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); dfn.vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 1, &barrier, 0, nullptr); @@ -484,7 +510,7 @@ std::pair BufferCache::UploadIndexBuffer( offset, source_length, }; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); dfn.vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 0, nullptr, 1, &barrier, 0, nullptr); @@ -552,7 +578,7 @@ std::pair BufferCache::UploadVertexBuffer( offset, upload_size, }; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); dfn.vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 0, nullptr, 1, &barrier, 0, nullptr); @@ -697,8 +723,9 @@ VkDescriptorSet BufferCache::PrepareVertexSet( }; } - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - dfn.vkUpdateDescriptorSets(*device_, 1, &descriptor_write, 0, nullptr); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, nullptr); vertex_sets_[hash] = set; return set; } @@ -771,7 +798,8 @@ void BufferCache::CacheTransientData(uint32_t guest_address, } void BufferCache::Flush(VkCommandBuffer command_buffer) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); // If we are flushing a big enough chunk queue up an event. // We don't want to do this for everything but often enough so that we won't @@ -790,7 +818,7 @@ void BufferCache::Flush(VkCommandBuffer command_buffer) { dirty_range.memory = transient_buffer_->gpu_memory(); dirty_range.offset = 0; dirty_range.size = transient_buffer_->capacity(); - dfn.vkFlushMappedMemoryRanges(*device_, 1, &dirty_range); + dfn.vkFlushMappedMemoryRanges(device, 1, &dirty_range); } void BufferCache::InvalidateCache() { diff --git a/src/xenia/gpu/vulkan/buffer_cache.h b/src/xenia/gpu/vulkan/buffer_cache.h index 4080b1803..449e23558 100644 --- a/src/xenia/gpu/vulkan/buffer_cache.h +++ b/src/xenia/gpu/vulkan/buffer_cache.h @@ -17,10 +17,8 @@ #include "xenia/memory.h" #include "xenia/ui/vulkan/circular_buffer.h" #include "xenia/ui/vulkan/fenced_pools.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" - -#include "third_party/vulkan/vk_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_provider.h" #include #include @@ -35,7 +33,7 @@ namespace vulkan { class BufferCache { public: BufferCache(RegisterFile* register_file, Memory* memory, - ui::vulkan::VulkanDevice* device, size_t capacity); + const ui::vulkan::VulkanProvider& provider, size_t capacity); ~BufferCache(); VkResult Initialize(); @@ -147,7 +145,7 @@ class BufferCache { RegisterFile* register_file_ = nullptr; Memory* memory_ = nullptr; - ui::vulkan::VulkanDevice* device_ = nullptr; + const ui::vulkan::VulkanProvider& provider_; VkDeviceMemory gpu_memory_pool_ = nullptr; VmaAllocator mem_allocator_ = nullptr; diff --git a/src/xenia/gpu/vulkan/pipeline_cache.cc b/src/xenia/gpu/vulkan/pipeline_cache.cc index 128c5b133..2581dc9f2 100644 --- a/src/xenia/gpu/vulkan/pipeline_cache.cc +++ b/src/xenia/gpu/vulkan/pipeline_cache.cc @@ -16,6 +16,7 @@ #include "xenia/base/xxhash.h" #include "xenia/gpu/gpu_flags.h" #include "xenia/gpu/vulkan/vulkan_gpu_flags.h" +#include "xenia/ui/vulkan/vulkan_util.h" #include #include @@ -24,18 +25,18 @@ namespace xe { namespace gpu { namespace vulkan { -using xe::ui::vulkan::CheckResult; +using xe::ui::vulkan::util::CheckResult; // Generated with `xenia-build genspirv`. -#include "xenia/gpu/vulkan/shaders/bin/dummy_frag.h" -#include "xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.h" -#include "xenia/gpu/vulkan/shaders/bin/point_list_geom.h" -#include "xenia/gpu/vulkan/shaders/bin/quad_list_geom.h" -#include "xenia/gpu/vulkan/shaders/bin/rect_list_geom.h" +#include "xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.h" +#include "xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/line_quad_list_geom.h" +#include "xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/point_list_geom.h" +#include "xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.h" +#include "xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.h" PipelineCache::PipelineCache(RegisterFile* register_file, - ui::vulkan::VulkanDevice* device) - : register_file_(register_file), device_(device) { + const ui::vulkan::VulkanProvider& provider) + : register_file_(register_file), provider_(provider) { shader_translator_.reset(new SpirvShaderTranslator()); } @@ -45,7 +46,8 @@ VkResult PipelineCache::Initialize( VkDescriptorSetLayout uniform_descriptor_set_layout, VkDescriptorSetLayout texture_descriptor_set_layout, VkDescriptorSetLayout vertex_descriptor_set_layout) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status; // Initialize the shared driver pipeline cache. @@ -58,7 +60,7 @@ VkResult PipelineCache::Initialize( pipeline_cache_info.flags = 0; pipeline_cache_info.initialDataSize = 0; pipeline_cache_info.pInitialData = nullptr; - status = dfn.vkCreatePipelineCache(*device_, &pipeline_cache_info, nullptr, + status = dfn.vkCreatePipelineCache(device, &pipeline_cache_info, nullptr, &pipeline_cache_); if (status != VK_SUCCESS) { return status; @@ -96,7 +98,7 @@ VkResult PipelineCache::Initialize( pipeline_layout_info.pushConstantRangeCount = static_cast(xe::countof(push_constant_ranges)); pipeline_layout_info.pPushConstantRanges = push_constant_ranges; - status = dfn.vkCreatePipelineLayout(*device_, &pipeline_layout_info, nullptr, + status = dfn.vkCreatePipelineLayout(device, &pipeline_layout_info, nullptr, &pipeline_layout_); if (status != VK_SUCCESS) { return status; @@ -113,58 +115,57 @@ VkResult PipelineCache::Initialize( static_cast(sizeof(line_quad_list_geom)); shader_module_info.pCode = reinterpret_cast(line_quad_list_geom); - status = dfn.vkCreateShaderModule(*device_, &shader_module_info, nullptr, + status = dfn.vkCreateShaderModule(device, &shader_module_info, nullptr, &geometry_shaders_.line_quad_list); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName(uint64_t(geometry_shaders_.line_quad_list), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - "S(g): Line Quad List"); + provider_.SetDeviceObjectName(VK_OBJECT_TYPE_SHADER_MODULE, + uint64_t(geometry_shaders_.line_quad_list), + "S(g): Line Quad List"); shader_module_info.codeSize = static_cast(sizeof(point_list_geom)); shader_module_info.pCode = reinterpret_cast(point_list_geom); - status = dfn.vkCreateShaderModule(*device_, &shader_module_info, nullptr, + status = dfn.vkCreateShaderModule(device, &shader_module_info, nullptr, &geometry_shaders_.point_list); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName(uint64_t(geometry_shaders_.point_list), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - "S(g): Point List"); + provider_.SetDeviceObjectName(VK_OBJECT_TYPE_SHADER_MODULE, + uint64_t(geometry_shaders_.point_list), + "S(g): Point List"); shader_module_info.codeSize = static_cast(sizeof(quad_list_geom)); shader_module_info.pCode = reinterpret_cast(quad_list_geom); - status = dfn.vkCreateShaderModule(*device_, &shader_module_info, nullptr, + status = dfn.vkCreateShaderModule(device, &shader_module_info, nullptr, &geometry_shaders_.quad_list); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName(uint64_t(geometry_shaders_.quad_list), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - "S(g): Quad List"); + provider_.SetDeviceObjectName(VK_OBJECT_TYPE_SHADER_MODULE, + uint64_t(geometry_shaders_.quad_list), + "S(g): Quad List"); shader_module_info.codeSize = static_cast(sizeof(rect_list_geom)); shader_module_info.pCode = reinterpret_cast(rect_list_geom); - status = dfn.vkCreateShaderModule(*device_, &shader_module_info, nullptr, + status = dfn.vkCreateShaderModule(device, &shader_module_info, nullptr, &geometry_shaders_.rect_list); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName(uint64_t(geometry_shaders_.rect_list), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - "S(g): Rect List"); + provider_.SetDeviceObjectName(VK_OBJECT_TYPE_SHADER_MODULE, + uint64_t(geometry_shaders_.rect_list), + "S(g): Rect List"); shader_module_info.codeSize = static_cast(sizeof(dummy_frag)); shader_module_info.pCode = reinterpret_cast(dummy_frag); - status = dfn.vkCreateShaderModule(*device_, &shader_module_info, nullptr, + status = dfn.vkCreateShaderModule(device, &shader_module_info, nullptr, &dummy_pixel_shader_); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName(uint64_t(dummy_pixel_shader_), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - "S(p): Dummy"); + provider_.SetDeviceObjectName(VK_OBJECT_TYPE_SHADER_MODULE, + uint64_t(dummy_pixel_shader_), "S(g): Dummy"); return VK_SUCCESS; } @@ -172,37 +173,38 @@ VkResult PipelineCache::Initialize( void PipelineCache::Shutdown() { ClearCache(); - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); // Destroy geometry shaders. if (geometry_shaders_.line_quad_list) { - dfn.vkDestroyShaderModule(*device_, geometry_shaders_.line_quad_list, + dfn.vkDestroyShaderModule(device, geometry_shaders_.line_quad_list, nullptr); geometry_shaders_.line_quad_list = nullptr; } if (geometry_shaders_.point_list) { - dfn.vkDestroyShaderModule(*device_, geometry_shaders_.point_list, nullptr); + dfn.vkDestroyShaderModule(device, geometry_shaders_.point_list, nullptr); geometry_shaders_.point_list = nullptr; } if (geometry_shaders_.quad_list) { - dfn.vkDestroyShaderModule(*device_, geometry_shaders_.quad_list, nullptr); + dfn.vkDestroyShaderModule(device, geometry_shaders_.quad_list, nullptr); geometry_shaders_.quad_list = nullptr; } if (geometry_shaders_.rect_list) { - dfn.vkDestroyShaderModule(*device_, geometry_shaders_.rect_list, nullptr); + dfn.vkDestroyShaderModule(device, geometry_shaders_.rect_list, nullptr); geometry_shaders_.rect_list = nullptr; } if (dummy_pixel_shader_) { - dfn.vkDestroyShaderModule(*device_, dummy_pixel_shader_, nullptr); + dfn.vkDestroyShaderModule(device, dummy_pixel_shader_, nullptr); dummy_pixel_shader_ = nullptr; } if (pipeline_layout_) { - dfn.vkDestroyPipelineLayout(*device_, pipeline_layout_, nullptr); + dfn.vkDestroyPipelineLayout(device, pipeline_layout_, nullptr); pipeline_layout_ = nullptr; } if (pipeline_cache_) { - dfn.vkDestroyPipelineCache(*device_, pipeline_cache_, nullptr); + dfn.vkDestroyPipelineCache(device, pipeline_cache_, nullptr); pipeline_cache_ = nullptr; } } @@ -223,7 +225,7 @@ VulkanShader* PipelineCache::LoadShader(xenos::ShaderType shader_type, // Always create the shader and stash it away. // We need to track it even if it fails translation so we know not to try // again. - VulkanShader* shader = new VulkanShader(device_, shader_type, data_hash, + VulkanShader* shader = new VulkanShader(provider_, shader_type, data_hash, host_address, dword_count); shader_map_.insert({data_hash, shader}); @@ -278,10 +280,11 @@ PipelineCache::UpdateStatus PipelineCache::ConfigurePipeline( } void PipelineCache::ClearCache() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); // Destroy all pipelines. for (auto it : cached_pipelines_) { - dfn.vkDestroyPipeline(*device_, it.second, nullptr); + dfn.vkDestroyPipeline(device, it.second, nullptr); } cached_pipelines_.clear(); COUNT_profile_set("gpu/pipeline_cache/pipelines", 0); @@ -343,9 +346,10 @@ VkPipeline PipelineCache::GetPipeline(const RenderState* render_state, pipeline_info.basePipelineHandle = nullptr; pipeline_info.basePipelineIndex = -1; VkPipeline pipeline = nullptr; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); auto result = dfn.vkCreateGraphicsPipelines( - *device_, pipeline_cache_, 1, &pipeline_info, nullptr, &pipeline); + device, pipeline_cache_, 1, &pipeline_info, nullptr, &pipeline); if (result != VK_SUCCESS) { XELOGE("vkCreateGraphicsPipelines failed with code {}", result); assert_always(); @@ -354,9 +358,10 @@ VkPipeline PipelineCache::GetPipeline(const RenderState* render_state, // Dump shader disassembly. if (cvars::vulkan_dump_disasm) { - if (device_->HasEnabledExtension(VK_AMD_SHADER_INFO_EXTENSION_NAME)) { + if (provider_.device_extensions().amd_shader_info) { DumpShaderDisasmAMD(pipeline); - } else if (device_->device_info().properties.vendorID == 0x10DE) { + } else if (provider_.device_properties().vendorID == + uint32_t(ui::GraphicsProvider::GpuVendorID::kNvidia)) { // NVIDIA cards DumpShaderDisasmNV(pipeline_info); } @@ -421,7 +426,8 @@ static void DumpShaderStatisticsAMD(const VkShaderStatisticsInfoAMD& stats) { } void PipelineCache::DumpShaderDisasmAMD(VkPipeline pipeline) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; size_t data_size = 0; @@ -429,9 +435,9 @@ void PipelineCache::DumpShaderDisasmAMD(VkPipeline pipeline) { data_size = sizeof(stats); // Vertex shader - status = dfn.vkGetShaderInfoAMD( - *device_, pipeline, VK_SHADER_STAGE_VERTEX_BIT, - VK_SHADER_INFO_TYPE_STATISTICS_AMD, &data_size, &stats); + status = dfn.vkGetShaderInfoAMD(device, pipeline, VK_SHADER_STAGE_VERTEX_BIT, + VK_SHADER_INFO_TYPE_STATISTICS_AMD, + &data_size, &stats); if (status == VK_SUCCESS) { XELOGI("AMD Vertex Shader Statistics:"); DumpShaderStatisticsAMD(stats); @@ -439,7 +445,7 @@ void PipelineCache::DumpShaderDisasmAMD(VkPipeline pipeline) { // Fragment shader status = dfn.vkGetShaderInfoAMD( - *device_, pipeline, VK_SHADER_STAGE_FRAGMENT_BIT, + device, pipeline, VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_INFO_TYPE_STATISTICS_AMD, &data_size, &stats); if (status == VK_SUCCESS) { XELOGI("AMD Fragment Shader Statistics:"); @@ -455,7 +461,8 @@ void PipelineCache::DumpShaderDisasmNV( // This code is super ugly. Update this when NVidia includes an official // way to dump shader disassembly. - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkPipelineCacheCreateInfo pipeline_cache_info; VkPipelineCache dummy_pipeline_cache; @@ -464,23 +471,23 @@ void PipelineCache::DumpShaderDisasmNV( pipeline_cache_info.flags = 0; pipeline_cache_info.initialDataSize = 0; pipeline_cache_info.pInitialData = nullptr; - auto status = dfn.vkCreatePipelineCache(*device_, &pipeline_cache_info, - nullptr, &dummy_pipeline_cache); + auto status = dfn.vkCreatePipelineCache(device, &pipeline_cache_info, nullptr, + &dummy_pipeline_cache); CheckResult(status, "vkCreatePipelineCache"); // Create a pipeline on the dummy cache and dump it. VkPipeline dummy_pipeline; status = - dfn.vkCreateGraphicsPipelines(*device_, dummy_pipeline_cache, 1, + dfn.vkCreateGraphicsPipelines(device, dummy_pipeline_cache, 1, &pipeline_info, nullptr, &dummy_pipeline); std::vector pipeline_data; size_t data_size = 0; - status = dfn.vkGetPipelineCacheData(*device_, dummy_pipeline_cache, - &data_size, nullptr); + status = dfn.vkGetPipelineCacheData(device, dummy_pipeline_cache, &data_size, + nullptr); if (status == VK_SUCCESS) { pipeline_data.resize(data_size); - dfn.vkGetPipelineCacheData(*device_, dummy_pipeline_cache, &data_size, + dfn.vkGetPipelineCacheData(device, dummy_pipeline_cache, &data_size, pipeline_data.data()); // Scan the data for the disassembly. @@ -537,8 +544,8 @@ void PipelineCache::DumpShaderDisasmNV( disasm_fp); } - dfn.vkDestroyPipeline(*device_, dummy_pipeline, nullptr); - dfn.vkDestroyPipelineCache(*device_, dummy_pipeline_cache, nullptr); + dfn.vkDestroyPipeline(device, dummy_pipeline, nullptr); + dfn.vkDestroyPipelineCache(device, dummy_pipeline_cache, nullptr); } VkShaderModule PipelineCache::GetGeometryShader( @@ -582,7 +589,7 @@ bool PipelineCache::SetDynamicState(VkCommandBuffer command_buffer, SCOPE_profile_cpu_f("gpu"); #endif // FINE_GRAINED_DRAW_SCOPES - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); auto& regs = set_dynamic_state_registers_; bool window_offset_dirty = SetShadowRegister(®s.pa_sc_window_offset, diff --git a/src/xenia/gpu/vulkan/pipeline_cache.h b/src/xenia/gpu/vulkan/pipeline_cache.h index 64d319165..b85a52c86 100644 --- a/src/xenia/gpu/vulkan/pipeline_cache.h +++ b/src/xenia/gpu/vulkan/pipeline_cache.h @@ -20,8 +20,7 @@ #include "xenia/gpu/vulkan/vulkan_shader.h" #include "xenia/gpu/xenos.h" #include "xenia/ui/spirv/spirv_disassembler.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace gpu { @@ -38,7 +37,8 @@ class PipelineCache { kError, }; - PipelineCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device); + PipelineCache(RegisterFile* register_file, + const ui::vulkan::VulkanProvider& provider); ~PipelineCache(); VkResult Initialize(VkDescriptorSetLayout uniform_descriptor_set_layout, @@ -90,7 +90,7 @@ class PipelineCache { bool is_line_mode); RegisterFile* register_file_ = nullptr; - ui::vulkan::VulkanDevice* device_ = nullptr; + const ui::vulkan::VulkanProvider& provider_; // Temporary storage for AnalyzeUcode calls. StringBuffer ucode_disasm_buffer_; diff --git a/src/xenia/gpu/vulkan/render_cache.cc b/src/xenia/gpu/vulkan/render_cache.cc index b3dc4d389..5b15a304b 100644 --- a/src/xenia/gpu/vulkan/render_cache.cc +++ b/src/xenia/gpu/vulkan/render_cache.cc @@ -19,13 +19,14 @@ #include "xenia/gpu/gpu_flags.h" #include "xenia/gpu/registers.h" #include "xenia/gpu/vulkan/vulkan_gpu_flags.h" +#include "xenia/ui/vulkan/vulkan_util.h" namespace xe { namespace gpu { namespace vulkan { using namespace xe::gpu::xenos; -using xe::ui::vulkan::CheckResult; +using xe::ui::vulkan::util::CheckResult; constexpr uint32_t kEdramBufferCapacity = 10 * 1024 * 1024; @@ -105,7 +106,7 @@ class CachedFramebuffer { // Associated render pass VkRenderPass render_pass = nullptr; - CachedFramebuffer(const ui::vulkan::VulkanDevice& device, + CachedFramebuffer(const ui::vulkan::VulkanProvider& provider, VkRenderPass render_pass, uint32_t surface_width, uint32_t surface_height, CachedTileView* target_color_attachments[4], @@ -117,7 +118,7 @@ class CachedFramebuffer { bool IsCompatible(const RenderConfiguration& desired_config) const; private: - const ui::vulkan::VulkanDevice& device_; + const ui::vulkan::VulkanProvider& provider_; }; // Cached render passes based on register states. @@ -134,7 +135,7 @@ class CachedRenderPass { // Cache of framebuffers for the various tile attachments. std::vector cached_framebuffers; - CachedRenderPass(const ui::vulkan::VulkanDevice& device, + CachedRenderPass(const ui::vulkan::VulkanProvider& provider, const RenderConfiguration& desired_config); ~CachedRenderPass(); @@ -143,28 +144,30 @@ class CachedRenderPass { bool IsCompatible(const RenderConfiguration& desired_config) const; private: - const ui::vulkan::VulkanDevice& device_; + const ui::vulkan::VulkanProvider& provider_; }; -CachedTileView::CachedTileView(ui::vulkan::VulkanDevice* device, +CachedTileView::CachedTileView(const ui::vulkan::VulkanProvider& provider, VkDeviceMemory edram_memory, TileViewKey view_key) - : device_(device), key(std::move(view_key)) {} + : provider_(provider), key(std::move(view_key)) {} CachedTileView::~CachedTileView() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - ui::vulkan::DestroyAndNullHandle(dfn.vkDestroyImageView, *device_, - image_view); - ui::vulkan::DestroyAndNullHandle(dfn.vkDestroyImageView, *device_, - image_view_depth); - ui::vulkan::DestroyAndNullHandle(dfn.vkDestroyImageView, *device_, - image_view_stencil); - ui::vulkan::DestroyAndNullHandle(dfn.vkDestroyImage, *device_, image); - ui::vulkan::DestroyAndNullHandle(dfn.vkFreeMemory, *device_, memory); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyImageView, device, + image_view); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyImageView, device, + image_view_depth); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyImageView, device, + image_view_stencil); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyImage, device, image); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device, memory); } VkResult CachedTileView::Initialize(VkCommandBuffer command_buffer) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; // Map format to Vulkan. @@ -237,26 +240,40 @@ VkResult CachedTileView::Initialize(VkCommandBuffer command_buffer) { image_info.queueFamilyIndexCount = 0; image_info.pQueueFamilyIndices = nullptr; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - status = dfn.vkCreateImage(*device_, &image_info, nullptr, &image); + status = dfn.vkCreateImage(device, &image_info, nullptr, &image); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName( - reinterpret_cast(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, + provider_.SetDeviceObjectName( + VK_OBJECT_TYPE_IMAGE, uint64_t(image), fmt::format("RT(d): 0x{:08X} 0x{:08X}({}) 0x{:08X}({}) {} {} {}", uint32_t(key.tile_offset), uint32_t(key.tile_width), uint32_t(key.tile_width), uint32_t(key.tile_height), uint32_t(key.tile_height), uint32_t(key.color_or_depth), - uint32_t(key.msaa_samples), uint32_t(key.edram_format))); + uint32_t(key.msaa_samples), uint32_t(key.edram_format)) + .c_str()); VkMemoryRequirements memory_requirements; - dfn.vkGetImageMemoryRequirements(*device_, image, &memory_requirements); + dfn.vkGetImageMemoryRequirements(device, image, &memory_requirements); // Bind to a newly allocated chunk. // TODO: Alias from a really big buffer? - memory = device_->AllocateMemory(memory_requirements, 0); - status = dfn.vkBindImageMemory(*device_, image, memory, 0); + VkMemoryAllocateInfo memory_allocate_info; + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = nullptr; + memory_allocate_info.allocationSize = memory_requirements.size; + if (!xe::bit_scan_forward(memory_requirements.memoryTypeBits & + provider_.memory_types_device_local(), + &memory_allocate_info.memoryTypeIndex)) { + return VK_ERROR_INITIALIZATION_FAILED; + } + status = + dfn.vkAllocateMemory(device, &memory_allocate_info, nullptr, &memory); + if (status != VK_SUCCESS) { + return status; + } + status = dfn.vkBindImageMemory(device, image, memory, 0); if (status != VK_SUCCESS) { return status; } @@ -284,7 +301,7 @@ VkResult CachedTileView::Initialize(VkCommandBuffer command_buffer) { VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; } status = - dfn.vkCreateImageView(*device_, &image_view_info, nullptr, &image_view); + dfn.vkCreateImageView(device, &image_view_info, nullptr, &image_view); if (status != VK_SUCCESS) { return status; } @@ -292,14 +309,14 @@ VkResult CachedTileView::Initialize(VkCommandBuffer command_buffer) { // Create separate depth/stencil views. if (key.color_or_depth == 0) { image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - status = dfn.vkCreateImageView(*device_, &image_view_info, nullptr, + status = dfn.vkCreateImageView(device, &image_view_info, nullptr, &image_view_depth); if (status != VK_SUCCESS) { return status; } image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - status = dfn.vkCreateImageView(*device_, &image_view_info, nullptr, + status = dfn.vkCreateImageView(device, &image_view_info, nullptr, &image_view_stencil); if (status != VK_SUCCESS) { return status; @@ -338,11 +355,11 @@ VkResult CachedTileView::Initialize(VkCommandBuffer command_buffer) { } CachedFramebuffer::CachedFramebuffer( - const ui::vulkan::VulkanDevice& device, VkRenderPass render_pass, + const ui::vulkan::VulkanProvider& provider, VkRenderPass render_pass, uint32_t surface_width, uint32_t surface_height, CachedTileView* target_color_attachments[4], CachedTileView* target_depth_stencil_attachment) - : device_(device), + : provider_(provider), width(surface_width), height(surface_height), depth_stencil_attachment(target_depth_stencil_attachment), @@ -354,8 +371,9 @@ CachedFramebuffer::CachedFramebuffer( CachedFramebuffer::~CachedFramebuffer() { if (handle != VK_NULL_HANDLE) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - dfn.vkDestroyFramebuffer(device_, handle, nullptr); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkDestroyFramebuffer(device, handle, nullptr); } } @@ -381,8 +399,9 @@ VkResult CachedFramebuffer::Initialize() { framebuffer_info.width = width; framebuffer_info.height = height; framebuffer_info.layers = 1; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - return dfn.vkCreateFramebuffer(device_, &framebuffer_info, nullptr, &handle); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + return dfn.vkCreateFramebuffer(device, &framebuffer_info, nullptr, &handle); } bool CachedFramebuffer::IsCompatible( @@ -427,9 +446,9 @@ bool CachedFramebuffer::IsCompatible( return true; } -CachedRenderPass::CachedRenderPass(const ui::vulkan::VulkanDevice& device, +CachedRenderPass::CachedRenderPass(const ui::vulkan::VulkanProvider& provider, const RenderConfiguration& desired_config) - : device_(device) { + : provider_(provider) { std::memcpy(&config, &desired_config, sizeof(config)); } @@ -440,8 +459,9 @@ CachedRenderPass::~CachedRenderPass() { cached_framebuffers.clear(); if (handle != VK_NULL_HANDLE) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - dfn.vkDestroyRenderPass(device_, handle, nullptr); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkDestroyRenderPass(device, handle, nullptr); } } @@ -553,8 +573,9 @@ VkResult CachedRenderPass::Initialize() { render_pass_info.dependencyCount = 1; render_pass_info.pDependencies = dependencies; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - return dfn.vkCreateRenderPass(device_, &render_pass_info, nullptr, &handle); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + return dfn.vkCreateRenderPass(device, &render_pass_info, nullptr, &handle); } bool CachedRenderPass::IsCompatible( @@ -577,13 +598,14 @@ bool CachedRenderPass::IsCompatible( } RenderCache::RenderCache(RegisterFile* register_file, - ui::vulkan::VulkanDevice* device) - : register_file_(register_file), device_(device) {} + const ui::vulkan::VulkanProvider& provider) + : register_file_(register_file), provider_(provider) {} RenderCache::~RenderCache() { Shutdown(); } VkResult RenderCache::Initialize() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; // Create the buffer we'll bind to our memory. @@ -597,7 +619,7 @@ VkResult RenderCache::Initialize() { buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; buffer_info.queueFamilyIndexCount = 0; buffer_info.pQueueFamilyIndices = nullptr; - status = dfn.vkCreateBuffer(*device_, &buffer_info, nullptr, &edram_buffer_); + status = dfn.vkCreateBuffer(device, &buffer_info, nullptr, &edram_buffer_); CheckResult(status, "vkCreateBuffer"); if (status != VK_SUCCESS) { return status; @@ -606,20 +628,29 @@ VkResult RenderCache::Initialize() { // Query requirements for the buffer. // It should be 1:1. VkMemoryRequirements buffer_requirements; - dfn.vkGetBufferMemoryRequirements(*device_, edram_buffer_, + dfn.vkGetBufferMemoryRequirements(device, edram_buffer_, &buffer_requirements); assert_true(buffer_requirements.size == kEdramBufferCapacity); // Allocate EDRAM memory. // TODO(benvanik): do we need it host visible? - edram_memory_ = device_->AllocateMemory(buffer_requirements); - assert_not_null(edram_memory_); - if (!edram_memory_) { + VkMemoryAllocateInfo buffer_allocate_info; + buffer_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + buffer_allocate_info.pNext = nullptr; + buffer_allocate_info.allocationSize = buffer_requirements.size; + buffer_allocate_info.memoryTypeIndex = ui::vulkan::util::ChooseHostMemoryType( + provider_, buffer_requirements.memoryTypeBits, false); + if (buffer_allocate_info.memoryTypeIndex == UINT32_MAX) { return VK_ERROR_INITIALIZATION_FAILED; } + status = dfn.vkAllocateMemory(device, &buffer_allocate_info, nullptr, + &edram_memory_); + if (status != VK_SUCCESS) { + return status; + } // Bind buffer to map our entire memory. - status = dfn.vkBindBufferMemory(*device_, edram_buffer_, edram_memory_, 0); + status = dfn.vkBindBufferMemory(device, edram_buffer_, edram_memory_, 0); CheckResult(status, "vkBindBufferMemory"); if (status != VK_SUCCESS) { return status; @@ -628,16 +659,15 @@ VkResult RenderCache::Initialize() { if (status == VK_SUCCESS) { // For debugging, upload a grid into the EDRAM buffer. uint32_t* gpu_data = nullptr; - status = - dfn.vkMapMemory(*device_, edram_memory_, 0, buffer_requirements.size, 0, - reinterpret_cast(&gpu_data)); + status = dfn.vkMapMemory(device, edram_memory_, 0, buffer_requirements.size, + 0, reinterpret_cast(&gpu_data)); if (status == VK_SUCCESS) { for (int i = 0; i < kEdramBufferCapacity / 4; i++) { gpu_data[i] = (i % 8) >= 4 ? 0xFF0000FF : 0xFFFFFFFF; } - dfn.vkUnmapMemory(*device_, edram_memory_); + dfn.vkUnmapMemory(device, edram_memory_); } } @@ -647,7 +677,8 @@ VkResult RenderCache::Initialize() { void RenderCache::Shutdown() { // TODO(benvanik): wait for idle. - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); // Dispose all render passes (and their framebuffers). for (auto render_pass : cached_render_passes_) { @@ -663,11 +694,11 @@ void RenderCache::Shutdown() { // Release underlying EDRAM memory. if (edram_buffer_) { - dfn.vkDestroyBuffer(*device_, edram_buffer_, nullptr); + dfn.vkDestroyBuffer(device, edram_buffer_, nullptr); edram_buffer_ = nullptr; } if (edram_memory_) { - dfn.vkFreeMemory(*device_, edram_memory_, nullptr); + dfn.vkFreeMemory(device, edram_memory_, nullptr); edram_memory_ = nullptr; } } @@ -803,7 +834,7 @@ const RenderState* RenderCache::BeginRenderPass(VkCommandBuffer command_buffer, render_pass_begin_info.pClearValues = nullptr; // Begin the render pass. - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); dfn.vkCmdBeginRenderPass(command_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); @@ -891,11 +922,10 @@ bool RenderCache::ConfigureRenderPass(VkCommandBuffer command_buffer, // If no render pass was found in the cache create a new one. if (!render_pass) { - render_pass = new CachedRenderPass(*device_, *config); + render_pass = new CachedRenderPass(provider_, *config); VkResult status = render_pass->Initialize(); if (status != VK_SUCCESS) { - XELOGE("{}: Failed to create render pass, status {}", __func__, - ui::vulkan::to_string(status)); + XELOGE("{}: Failed to create render pass", __func__); delete render_pass; return false; } @@ -971,12 +1001,11 @@ bool RenderCache::ConfigureRenderPass(VkCommandBuffer command_buffer, surface_pitch_px = std::min(surface_pitch_px, 2560u); surface_height_px = std::min(surface_height_px, 2560u); framebuffer = new CachedFramebuffer( - *device_, render_pass->handle, surface_pitch_px, surface_height_px, + provider_, render_pass->handle, surface_pitch_px, surface_height_px, target_color_attachments, target_depth_stencil_attachment); VkResult status = framebuffer->Initialize(); if (status != VK_SUCCESS) { - XELOGE("{}: Failed to create framebuffer, status {}", __func__, - ui::vulkan::to_string(status)); + XELOGE("{}: Failed to create framebuffer", __func__); delete framebuffer; return false; } @@ -1025,11 +1054,10 @@ CachedTileView* RenderCache::FindOrCreateTileView( } // Create a new tile and add to the cache. - tile_view = new CachedTileView(device_, edram_memory_, view_key); + tile_view = new CachedTileView(provider_, edram_memory_, view_key); VkResult status = tile_view->Initialize(command_buffer); if (status != VK_SUCCESS) { - XELOGE("{}: Failed to create tile view, status {}", __func__, - ui::vulkan::to_string(status)); + XELOGE("{}: Failed to create tile view", __func__); delete tile_view; return nullptr; @@ -1042,7 +1070,7 @@ CachedTileView* RenderCache::FindOrCreateTileView( void RenderCache::UpdateTileView(VkCommandBuffer command_buffer, CachedTileView* view, bool load, bool insert_barrier) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); uint32_t tile_width = view->key.msaa_samples == uint16_t(xenos::MsaaSamples::k4X) ? 40 : 80; @@ -1111,7 +1139,7 @@ void RenderCache::EndRenderPass() { assert_not_null(current_command_buffer_); // End the render pass. - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); dfn.vkCmdEndRenderPass(current_command_buffer_); // Copy all render targets back into our EDRAM buffer. @@ -1165,7 +1193,7 @@ void RenderCache::RawCopyToImage(VkCommandBuffer command_buffer, VkImageLayout image_layout, bool color_or_depth, VkOffset3D offset, VkExtent3D extents) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); // Transition the texture into a transfer destination layout. VkImageMemoryBarrier image_barrier; @@ -1239,7 +1267,7 @@ void RenderCache::BlitToImage(VkCommandBuffer command_buffer, bool color_or_depth, uint32_t format, VkFilter filter, VkOffset3D offset, VkExtent3D extents) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); if (color_or_depth) { // Adjust similar formats for easier matching. @@ -1372,7 +1400,7 @@ void RenderCache::ClearEDRAMColor(VkCommandBuffer command_buffer, std::memcpy(clear_value.float32, color, sizeof(float) * 4); // Issue a clear command - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); dfn.vkCmdClearColorImage(command_buffer, tile_view->image, VK_IMAGE_LAYOUT_GENERAL, &clear_value, 1, &range); @@ -1412,7 +1440,7 @@ void RenderCache::ClearEDRAMDepthStencil(VkCommandBuffer command_buffer, clear_value.stencil = stencil; // Issue a clear command - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); dfn.vkCmdClearDepthStencilImage(command_buffer, tile_view->image, VK_IMAGE_LAYOUT_GENERAL, &clear_value, 1, &range); @@ -1422,7 +1450,7 @@ void RenderCache::ClearEDRAMDepthStencil(VkCommandBuffer command_buffer, } void RenderCache::FillEDRAM(VkCommandBuffer command_buffer, uint32_t value) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); dfn.vkCmdFillBuffer(command_buffer, edram_buffer_, 0, kEdramBufferCapacity, value); } diff --git a/src/xenia/gpu/vulkan/render_cache.h b/src/xenia/gpu/vulkan/render_cache.h index 9dfbf648d..f73fa39e5 100644 --- a/src/xenia/gpu/vulkan/render_cache.h +++ b/src/xenia/gpu/vulkan/render_cache.h @@ -16,8 +16,7 @@ #include "xenia/gpu/texture_info.h" #include "xenia/gpu/vulkan/vulkan_shader.h" #include "xenia/gpu/xenos.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace gpu { @@ -68,8 +67,8 @@ class CachedTileView { // (if a depth view) Image view of stencil aspect VkImageView image_view_stencil = nullptr; - CachedTileView(ui::vulkan::VulkanDevice* device, VkDeviceMemory edram_memory, - TileViewKey view_key); + CachedTileView(const ui::vulkan::VulkanProvider& provider, + VkDeviceMemory edram_memory, TileViewKey view_key); ~CachedTileView(); VkResult Initialize(VkCommandBuffer command_buffer); @@ -89,7 +88,7 @@ class CachedTileView { } private: - ui::vulkan::VulkanDevice* device_ = nullptr; + const ui::vulkan::VulkanProvider& provider_; }; // Parsed render configuration from the current render state. @@ -274,7 +273,8 @@ struct RenderState { // must check for overlap then compute the offset (in both X and Y). class RenderCache { public: - RenderCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device); + RenderCache(RegisterFile* register_file, + const ui::vulkan::VulkanProvider& provider); ~RenderCache(); VkResult Initialize(); @@ -358,7 +358,7 @@ class RenderCache { CachedFramebuffer** out_framebuffer); RegisterFile* register_file_ = nullptr; - ui::vulkan::VulkanDevice* device_ = nullptr; + const ui::vulkan::VulkanProvider& provider_; // Entire 10MiB of EDRAM. VkDeviceMemory edram_memory_ = nullptr; diff --git a/src/xenia/gpu/vulkan/shaders/bytecode/.clang-format b/src/xenia/gpu/vulkan/shaders/bytecode/.clang-format new file mode 100644 index 000000000..9d159247d --- /dev/null +++ b/src/xenia/gpu/vulkan/shaders/bytecode/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false diff --git a/src/xenia/gpu/vulkan/shaders/bin/dummy_frag.h b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.h similarity index 98% rename from src/xenia/gpu/vulkan/shaders/bin/dummy_frag.h rename to src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.h index 7bcf6bc90..035b6b881 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/dummy_frag.h +++ b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.h @@ -1,7 +1,7 @@ // generated from `xb genspirv` // source: dummy.frag const uint8_t dummy_frag[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, diff --git a/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.spv b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..d34b4e9802b3d6eab7dee3295660d5b09ef0088b GIT binary patch literal 548 zcmY*WO-sW-6nr*~Z56a4>O~~t&1!8+6~U8w$R#KJ0n0XB;%eN4WCeepzsif?Oj4mP zyv&=~_n8ekXFZV(*^<7@q&<7mfg(a20k>td;c@bujBCFf&#xzp+>wCL-0ezFy6|c5 z+B)i&uqkYaIO=EwFF;k1FEr@NK;lH*KRqb(S>`siUTN)daCY zGk5VQ5+jRoC%nKR=T3ioAKP%B(`GYi{6qNMHG&->z7Hg4{{wsnzzD`OTJdxEL)bAm z|2cE0Mc@}e{H{j7TXD|RYHhtSIO{Y23dnqF%;#hArKmHXoI6mv!M2>AxjfXqp@hI6 Ibhw560w5wh-~a#s literal 0 HcmV?d00001 diff --git a/src/xenia/gpu/vulkan/shaders/bin/dummy_frag.txt b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.txt similarity index 96% rename from src/xenia/gpu/vulkan/shaders/bin/dummy_frag.txt rename to src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.txt index 33d1d9990..944db921e 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/dummy_frag.txt +++ b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/dummy_frag.txt @@ -1,6 +1,6 @@ ; SPIR-V ; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 6 +; Generator: Khronos Glslang Reference Front End; 10 ; Bound: 50 ; Schema: 0 OpCapability Shader diff --git a/src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.h b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/line_quad_list_geom.h similarity index 99% rename from src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.h rename to src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/line_quad_list_geom.h index 28935cc4a..3524cbc22 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/line_quad_list_geom.h +++ b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/line_quad_list_geom.h @@ -1,7 +1,7 @@ // generated from `xb genspirv` // source: line_quad_list.geom const uint8_t line_quad_list_geom[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, diff --git a/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/line_quad_list_geom.spv b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/line_quad_list_geom.spv new file mode 100644 index 0000000000000000000000000000000000000000..16668f787d2e22ab642eec22e593760630441557 GIT binary patch literal 2144 zcmZvcTTc{05QQ7qh2T?@J8}6p;|BH5Io7LM$K9|poZCA%Iauvy z?bY>-H8LmN5Q~pZxe1Rd!e7F;3qK~iENjT_$PQ&kvSZn_vdNwEe#QH8eJzVEmSWfC zS7k%`hqiALRB@xy6f}=t1<`r06Sv|l2(v7WPlHa<3bQ!rmUH@1FHFNM3i_YIc9aIm z>F21G^{JcpH{Dg8x27H+JHb(ueu&a6Iu{h1^Z3M)e(ZY;yAhwyj65HAvy=F1L|#>T znFAknFKNA~ET0rTk*41z8`i~8klo(mWjxsw3l?kEJ<}d^&Ro^kGubc6s!4r zQ6%Uk+8MNxBy9(0-LrnwW^c{k+243yC;Jojaqcsq@dX9;eTEYBjpvIqj&s4W?rBcU zoVz7%^C<7JS(Nv{v1d9}z=H(BI ztIHNtUq2j=dB;n_=r^t|JSxmD1b(Z)yal*9Va`#PasHJ8Psrz^9%r+9M|`&M=>eYCiZ-z6;rDO(*9t$MK}9|De6Z=U`Jv~Q zcxu5>zpj<~@aO?IWN_r))=JFuFUud0!IKZ>PT{C`xA56xde`K0Z-!sUr#|=fS}R(K z)9;N|IO}&HpW5*B1M_C!$bYMq{g(f(h{2N&J}Nl$y_ZjKdJqTm#^9dmjiZCRVKV$ha@w1h%bg=E>hoVohapXA3gk6*)Vq1SW?%+!=p6tq%7|9nNv1h*5{T zyeXp&In)7@L!4RPlQDnO!rbo{Jo$|Rf7it93=hQ7o0_(dGdz?LGreHbyCUEAl=VIm zPt3G|P1~w`v{^pd9?RfO8@R0Pi7&Mwkd-*ZQ!!Dm%{Yn{g$rJH)IyWj5V>F$|1nVeXfHfD#}ZFZUmruhz-3H=$f z$4tp%%-Z_R_2sG_ET1~PLd0G(iEzGsX2w|^*;`SX!S53mg$sfe`od7Cg#C&pcGl?~ zr%QFYg6~Mn_l$JnC-p~7KdPXP*`=E7>Wzyw*)B$DoYpp~>oV=zQ65KinrH2pDk-8e zsuNpnMuViZdH-G#*D9u!2UnU6?I_BI<7k-JQzuu7D86sHifZ@P)l7D)j^W5|B<1a- ztdnh-VjCVEUtXoI4%Radac($WGi{!dU5u+u8|PV7SGNALNLV-JedrI+>+RUD1H zuj|%v9uMkmdv~jLdy<7x73J#IL-rZzYyYB1J`SDoCO>YD8z9NaBm6h7tV4`cV`VG7Hr-DT-f_r=^^)F?;{tz^(5Il^mbG_we3#6z#O6H`e@644kn9M2?ESl#t|YO{i{DS? z6YSL6tGZ3Rjah;B#G3)B9s zD1<$xq#yJA_lh|o{RPi4pWoEYOOkwlswR9d3UKs@zM*yO`>H@L@P~XM2KkGxP5;td zeawb<)`#AfrPEuejs9N~;6raKo`YxR%=%lu|4HfOCx-n`OHw0}{W;^YKz?c}gfqf- zzVA1si1NVV+>SA7j`_OaDZ$qE#yCj!4$%#OaWiN_yH4urmdpT2mDi2zSL?;9JPyWgJ4J=t*WncSBRKjZJsmUMdJ k{^8^Ol4~f5?V!Z literal 0 HcmV?d00001 diff --git a/src/xenia/gpu/vulkan/shaders/bin/point_list_geom.txt b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/point_list_geom.txt similarity index 99% rename from src/xenia/gpu/vulkan/shaders/bin/point_list_geom.txt rename to src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/point_list_geom.txt index 0eec50c66..42ddf5846 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/point_list_geom.txt +++ b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/point_list_geom.txt @@ -1,6 +1,6 @@ ; SPIR-V ; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 6 +; Generator: Khronos Glslang Reference Front End; 10 ; Bound: 118 ; Schema: 0 OpCapability Geometry diff --git a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.h b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.h similarity index 99% rename from src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.h rename to src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.h index fd5741897..a8e19a833 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.h +++ b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.h @@ -1,7 +1,7 @@ // generated from `xb genspirv` // source: quad_list.geom const uint8_t quad_list_geom[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, diff --git a/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.spv b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.spv new file mode 100644 index 0000000000000000000000000000000000000000..a330c444385061afb1ef2aa473dbd7a8c6a738fe GIT binary patch literal 1996 zcmZvc+fEcg5QZD}#3>5ONrN~Gc!(fD1Vjm#MR8HNFp_vX8FnY@=sL5R9TMV|ujDKE z82TI96*p!uFO#R^S zV7Z$#m)F);$UJ8XC>uL(Myywq|IQ0S@CC_D$);pWvM1S>Jd=zno7@R&7p*PRa^z{PmK_f_9FG=Fy$ZbarFA1WsH>c}&yx2>8xBJ>_ z`mq}wz404K_a7cYLD+KJUf4SJTE4rsy3+9)Z%s)|y?ZMOp}!z(`X}B|+m~I^In7D8 z+U^TKe&xrBe~W$WG#5cr5y<$dHB2`@@DjZQW{RMoM%cl>*oHnY+i2eUy@DFOX`Sv-Wt77 zM=o)Co{^?+awf&#NF!5vsnEAtmJjC}4R&u1Uk<%6*g>TgK* zNx-Sc=eGrn_^r&YX8vty*2{fB*Z!EZ{iz~CeHU$K5=|-c(HfX1#2>!HSw@EZw?HuZPl^vRS9@a z9y75hVNSs5hrFuvl7tvs%*wKac_fFm@X3LP*|{Uhd3YD_IMS;UV(>6iYZ7?Kfd`)) t;@tU;ggKxeIJp0Z5^(aFDg4j6sSm1QPn;ingtPa&IiD}e`z6_w{098iv@rkx literal 0 HcmV?d00001 diff --git a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.txt b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.txt similarity index 99% rename from src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.txt rename to src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.txt index a7767342d..e64e521bf 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/quad_list_geom.txt +++ b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/quad_list_geom.txt @@ -1,6 +1,6 @@ ; SPIR-V ; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 6 +; Generator: Khronos Glslang Reference Front End; 10 ; Bound: 76 ; Schema: 0 OpCapability Geometry diff --git a/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.h b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.h similarity index 92% rename from src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.h rename to src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.h index d613de39c..b545ff142 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.h +++ b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.h @@ -1,8 +1,8 @@ // generated from `xb genspirv` // source: rect_list.geom const uint8_t rect_list_geom[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x28, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x2A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, @@ -124,7 +124,7 @@ const uint8_t rect_list_geom[] = { 0x20, 0x00, 0x04, 0x00, 0x10, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x07, 0x00, @@ -143,14 +143,14 @@ const uint8_t rect_list_geom[] = { 0x4F, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x16, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, - 0xBC, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1A, 0x01, 0x00, 0x00, - 0x17, 0x01, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, 0x9B, 0x00, 0x04, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x1A, 0x01, 0x00, 0x00, + 0x17, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, + 0xBC, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, + 0x18, 0x01, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, 0x9B, 0x00, 0x04, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x1B, 0x01, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x1C, 0x01, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x39, 0x00, 0x00, 0x00, 0x41, 0x00, 0x07, 0x00, 0x27, 0x00, 0x00, 0x00, @@ -163,16 +163,16 @@ const uint8_t rect_list_geom[] = { 0x3E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x1F, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, - 0xBC, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x01, 0x00, 0x00, - 0x20, 0x01, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, 0x9B, 0x00, 0x04, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x23, 0x01, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, + 0xBC, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, + 0x22, 0x01, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, 0x9B, 0x00, 0x04, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x24, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x26, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x48, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, @@ -244,32 +244,32 @@ const uint8_t rect_list_geom[] = { 0x3E, 0x00, 0x03, 0x00, 0x54, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x99, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x99, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x27, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x27, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x27, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0xAE, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, + 0xAE, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, - 0x27, 0x01, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0x29, 0x01, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x99, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00, 0xDB, 0x00, 0x01, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x49, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xB1, 0x00, 0x00, 0x00, @@ -341,32 +341,32 @@ const uint8_t rect_list_geom[] = { 0xED, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, - 0x26, 0x01, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, + 0x28, 0x01, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xF9, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, - 0x5B, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, + 0x5B, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x26, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x28, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x26, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x07, 0x01, 0x00, 0x00, + 0x28, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x07, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x09, 0x01, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x09, 0x01, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00, 0xDB, 0x00, 0x01, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x49, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, diff --git a/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.spv b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.spv new file mode 100644 index 0000000000000000000000000000000000000000..b898570a94cc7f45c015a09e74f016c8f7122276 GIT binary patch literal 4440 zcmZveS#On96o$V}6beFd0HTNmWJa9OAPA=36qJMlinE8(9^qi=X-*Fb@kWtoq8Cng z$^|59#5hC}=P8KM=wHwaG0_XBD@Daf^m)GSr90@?uI}}|YuIbAJ)~`|^E->8rRXXq z7E4=-@|#|?@>3L!vlu5A6YwaCzKvTqt{7_$typ>0WjanR+8j#Tv|>t$+QEAUY9sRV zkd4T#$Q{TOl1xLUBi(rGI|J!PQ}=V8uw7W{lUWw<^#(5%%*a-Xs4=s;tjS*?ui8W^fJD)k+^tAow4qUcETN8KIF zN&-J}cCTdn2qxIo|X1&4kN}K(!jr_-iZDP4ESSq7+A}fRS zdSj@vcVzEab;#I><(~a(A7f?zm`l6+S#je3Nw6Ldd+9fMd?&NX?5lq6t@h|e z9us}YW8{qKqtC8?4&oIb=2O-15YY&oKktGuVU7BtI9Y z#QL3!cUR(PGOEk#H=EHo&-fhvy3XLv13PCA;n-6=Q6gGdQq%N zcwXYnH$U0*F`xKi#4qpybnA=x7HZ@({~|_XtvURcCNBJ!WmvyUk^0#ORvUkLvbjdr z>mJ2=wciMKZSvMH_MMQ^zA5R(h_@yj_1LeQ5%Z!*|edGm=qdveZy7K&>L|Eb9)ufN!{ z$#3EO-6+=X9NNX6Rk`Kd75PPA_dvNC&b^H{Rlnb1Iv9KLc7GQmo*B=9XJa9>l)2K06B!}k(&?ft2a73jvx_ma)Ca0Oz#v5K+o?UjhOP1NKXRwho~k0lic zQr?*W?dG3CUj1jm>C=vAe;;4(-UaB|?8!l_U2wC}<&Px1DsjKj#ntG3e_)fp29dL8 z>d}Wa=y@OVo?VMgd-$$RzR};jXX~(OkDgtJZoGV~cP_QMUTv}7d2sxe>(w6XU4X7F zU++S&{3+HDYjHeD=j=xxVoy91`npbgv4PRgUg)zPIf~c|^*Eb>N(Z@VueYAV;?n1naac;bWcPCzd@3r^~Iuqxu0?zj- z&f9i)ZBef{>fM1JF?qd%@YHgVLp z8$DuTF7N5Rh_IZ-4r}wDGWn=YY;9j6`Mi0)GkwEvd&=W}*^}^#_1@2% zo$wDLp5wUV520^Q!cd;){p_{A4g{4YjA%c^_$xg-$f(`VJ9wB;EN@=*IOorzyT|4>N1UbSz}nmc*YG?d7jZA3 z8@Ck6*q6|aU4Z1VFN3v3>?`QT%lDGcH{(^r`)Zth6YKW?cKc*+ zv{_f)OV=LfRDJ{4UidCW?$^=t_eh+-M{j_=L-xx2_R90}CZf$ezDI8%`FkYRN4xLQ z+eq|0?$tYqk2@gF@BF)9=XPG_k9~R%U7K;%D~>z=0eZyb^?nGi%@}JFM{UQ@tu6e$ z^B*C{kf=?Z*LEE2Jnm;++s9ySQJXkw`vg5=VlI36DWWZE6Gv^IpoLhwzOahoAL@pYw)aR=jwph7O@l1jhF8wpYKs8y6=&3 e_D!r`yhrv%n|0>BbnS6Y^Y>^2{Amhah5QY`PoISV literal 0 HcmV?d00001 diff --git a/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.txt b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.txt similarity index 92% rename from src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.txt rename to src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.txt index ff580ea8d..c77ff157e 100644 --- a/src/xenia/gpu/vulkan/shaders/bin/rect_list_geom.txt +++ b/src/xenia/gpu/vulkan/shaders/bytecode/vulkan_spirv/rect_list_geom.txt @@ -1,7 +1,7 @@ ; SPIR-V ; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 6 -; Bound: 296 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 298 ; Schema: 0 OpCapability Geometry OpCapability GeometryPointSize @@ -84,7 +84,7 @@ %_in_point_size_unused = OpVariable %_ptr_Input__arr_float_uint_3 Input %_ptr_Output_v2float = OpTypePointer Output %v2float %_out_point_coord_unused = OpVariable %_ptr_Output_v2float Output - %293 = OpConstantComposite %v2float %float_0_00100000005 %float_0_00100000005 + %295 = OpConstantComposite %v2float %float_0_00100000005 %float_0_00100000005 %main = OpFunction %void None %3 %5 = OpLabel %40 = OpAccessChain %_ptr_Input_float %gl_in %int_2 %int_0 %uint_0 @@ -95,11 +95,11 @@ %50 = OpAccessChain %_ptr_Input_v4float %gl_in %int_0 %int_0 %51 = OpLoad %v4float %50 %52 = OpVectorShuffle %v2float %51 %51 0 1 - %278 = OpFSub %v2float %52 %46 - %279 = OpExtInst %v2float %1 FAbs %278 - %282 = OpFOrdLessThanEqual %v2bool %279 %293 - %283 = OpAll %bool %282 - %56 = OpLogicalNot %bool %283 + %279 = OpFSub %v2float %52 %46 + %280 = OpExtInst %v2float %1 FAbs %279 + %283 = OpFOrdLessThanEqual %v2bool %280 %295 + %284 = OpAll %bool %283 + %56 = OpLogicalNot %bool %284 OpSelectionMerge %58 None OpBranchConditional %56 %57 %58 %57 = OpLabel @@ -108,13 +108,13 @@ %61 = OpAccessChain %_ptr_Input_float %gl_in %int_2 %int_0 %uint_1 %62 = OpLoad %float %61 %63 = OpCompositeConstruct %v2float %60 %62 - %287 = OpFSub %v2float %52 %63 - %288 = OpExtInst %v2float %1 FAbs %287 - %291 = OpFOrdLessThanEqual %v2bool %288 %293 - %292 = OpAll %bool %291 + %289 = OpFSub %v2float %52 %63 + %290 = OpExtInst %v2float %1 FAbs %289 + %293 = OpFOrdLessThanEqual %v2bool %290 %295 + %294 = OpAll %bool %293 OpBranch %58 %58 = OpLabel - %71 = OpPhi %bool %283 %5 %292 %57 + %71 = OpPhi %bool %284 %5 %294 %57 OpSelectionMerge %73 None OpBranchConditional %71 %72 %177 %72 = OpLabel @@ -171,23 +171,23 @@ OpStore %84 %107 OpBranch %153 %153 = OpLabel - %295 = OpPhi %int %int_0 %72 %176 %154 - %160 = OpSLessThan %bool %295 %int_16 + %297 = OpPhi %int %int_0 %72 %176 %154 + %160 = OpSLessThan %bool %297 %int_16 OpLoopMerge %155 %154 None OpBranchConditional %160 %154 %155 %154 = OpLabel - %163 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_0 %295 + %163 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_0 %297 %164 = OpLoad %v4float %163 %165 = OpFNegate %v4float %164 - %167 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_1 %295 + %167 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_1 %297 %168 = OpLoad %v4float %167 %169 = OpFAdd %v4float %165 %168 - %171 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_2 %295 + %171 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_2 %297 %172 = OpLoad %v4float %171 %173 = OpFAdd %v4float %169 %172 - %174 = OpAccessChain %_ptr_Output_v4float %out_interpolators %295 + %174 = OpAccessChain %_ptr_Output_v4float %out_interpolators %297 OpStore %174 %173 - %176 = OpIAdd %int %295 %int_1 + %176 = OpIAdd %int %297 %int_1 OpBranch %153 %155 = OpLabel OpEmitVertex @@ -247,23 +247,23 @@ OpStore %183 %198 OpBranch %243 %243 = OpLabel - %294 = OpPhi %int %int_0 %177 %265 %244 - %249 = OpSLessThan %bool %294 %int_16 + %296 = OpPhi %int %int_0 %177 %265 %244 + %249 = OpSLessThan %bool %296 %int_16 OpLoopMerge %245 %244 None OpBranchConditional %249 %244 %245 %244 = OpLabel - %252 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_0 %294 + %252 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_0 %296 %253 = OpLoad %v4float %252 - %255 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_1 %294 + %255 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_1 %296 %256 = OpLoad %v4float %255 %257 = OpFNegate %v4float %256 %258 = OpFAdd %v4float %253 %257 - %260 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_2 %294 + %260 = OpAccessChain %_ptr_Input_v4float %in_interpolators %int_2 %296 %261 = OpLoad %v4float %260 %262 = OpFAdd %v4float %258 %261 - %263 = OpAccessChain %_ptr_Output_v4float %out_interpolators %294 + %263 = OpAccessChain %_ptr_Output_v4float %out_interpolators %296 OpStore %263 %262 - %265 = OpIAdd %int %294 %int_1 + %265 = OpIAdd %int %296 %int_1 OpBranch %243 %245 = OpLabel OpEmitVertex diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index 866c51bd2..262819d46 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -23,8 +23,8 @@ #include "xenia/gpu/texture_info.h" #include "xenia/gpu/vulkan/texture_config.h" #include "xenia/gpu/vulkan/vulkan_gpu_flags.h" -#include "xenia/ui/vulkan/vulkan_instance.h" #include "xenia/ui/vulkan/vulkan_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_util.h" DECLARE_bool(texture_dump); @@ -35,7 +35,7 @@ void TextureDump(const TextureInfo& src, void* buffer, size_t length); namespace vulkan { -using xe::ui::vulkan::CheckResult; +using xe::ui::vulkan::util::CheckResult; using namespace xe::literals; @@ -58,20 +58,21 @@ const char* get_dimension_name(xenos::DataDimension dimension) { TextureCache::TextureCache(Memory* memory, RegisterFile* register_file, TraceWriter* trace_writer, - ui::vulkan::VulkanDevice* device) + ui::vulkan::VulkanProvider& provider) : memory_(memory), register_file_(register_file), trace_writer_(trace_writer), - device_(device), - staging_buffer_(device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + provider_(provider), + staging_buffer_(provider, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, kStagingBufferSize), - wb_staging_buffer_(device, VK_BUFFER_USAGE_TRANSFER_DST_BIT, + wb_staging_buffer_(provider, VK_BUFFER_USAGE_TRANSFER_DST_BIT, kStagingBufferSize) {} TextureCache::~TextureCache() { Shutdown(); } VkResult TextureCache::Initialize() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; // Descriptor pool used for all of our cached descriptors. @@ -79,16 +80,16 @@ VkResult TextureCache::Initialize() { pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; pool_sizes[0].descriptorCount = 32768; descriptor_pool_ = std::make_unique( - *device_, 32768, + provider_, 32768, std::vector(pool_sizes, std::end(pool_sizes))); wb_command_pool_ = std::make_unique( - *device_, device_->queue_family_index()); + provider_, provider_.queue_family_graphics_compute()); // Check some device limits // On low sampler counts: Rarely would we experience over 16 unique samplers. // This code could be refactored to scale up/down to the # of samplers. - auto& limits = device_->device_info().properties.limits; + auto& limits = provider_.device_properties().limits; if (limits.maxPerStageDescriptorSamplers < kMaxTextureSamplers || limits.maxPerStageDescriptorSampledImages < kMaxTextureSamplers) { XELOGE( @@ -120,7 +121,7 @@ VkResult TextureCache::Initialize() { static_cast(xe::countof(bindings)); descriptor_set_layout_info.pBindings = bindings; status = - dfn.vkCreateDescriptorSetLayout(*device_, &descriptor_set_layout_info, + dfn.vkCreateDescriptorSetLayout(device, &descriptor_set_layout_info, nullptr, &texture_descriptor_set_layout_); if (status != VK_SUCCESS) { return status; @@ -138,14 +139,16 @@ VkResult TextureCache::Initialize() { // Create a memory allocator for textures. VmaVulkanFunctions vulkan_funcs = {}; - ui::vulkan::FillVMAVulkanFunctions(&vulkan_funcs, *device_); + ui::vulkan::FillVMAVulkanFunctions(&vulkan_funcs, provider_); - VmaAllocatorCreateInfo alloc_info = { - 0, *device_, *device_, 0, 0, nullptr, nullptr, 0, nullptr, &vulkan_funcs, - }; + VmaAllocatorCreateInfo alloc_info = {}; + alloc_info.physicalDevice = provider_.physical_device(); + alloc_info.device = device; + alloc_info.pVulkanFunctions = &vulkan_funcs; + alloc_info.instance = provider_.instance(); status = vmaCreateAllocator(&alloc_info, &mem_allocator_); if (status != VK_SUCCESS) { - dfn.vkDestroyDescriptorSetLayout(*device_, texture_descriptor_set_layout_, + dfn.vkDestroyDescriptorSetLayout(device, texture_descriptor_set_layout_, nullptr); return status; } @@ -154,8 +157,6 @@ VkResult TextureCache::Initialize() { invalidated_textures_sets_[1].reserve(64); invalidated_textures_ = &invalidated_textures_sets_[0]; - device_queue_ = device_->AcquireQueue(device_->queue_family_index()); - memory_invalidation_callback_handle_ = memory_->RegisterPhysicalMemoryInvalidationCallback( MemoryInvalidationCallbackThunk, this); @@ -170,10 +171,6 @@ void TextureCache::Shutdown() { memory_invalidation_callback_handle_ = nullptr; } - if (device_queue_) { - device_->ReleaseQueue(device_queue_, device_->queue_family_index()); - } - // Free all textures allocated. ClearCache(); Scavenge(); @@ -182,8 +179,9 @@ void TextureCache::Shutdown() { vmaDestroyAllocator(mem_allocator_); mem_allocator_ = nullptr; } - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - dfn.vkDestroyDescriptorSetLayout(*device_, texture_descriptor_set_layout_, + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkDestroyDescriptorSetLayout(device, texture_descriptor_set_layout_, nullptr); } @@ -235,20 +233,19 @@ TextureCache::Texture* TextureCache::AllocateTexture( image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - const ui::vulkan::VulkanInstance* instance = device_->instance(); - const ui::vulkan::VulkanInstance::InstanceFunctions& ifn = instance->ifn(); + const ui::vulkan::VulkanProvider::InstanceFunctions& ifn = provider_.ifn(); // Check the device limits for the format before we create it. VkFormatProperties props; - ifn.vkGetPhysicalDeviceFormatProperties(*device_, format, &props); + ifn.vkGetPhysicalDeviceFormatProperties(provider_.physical_device(), format, + &props); if ((props.optimalTilingFeatures & required_flags) != required_flags) { // Texture needs conversion on upload to a native format. XELOGE( - "Texture Cache: Invalid usage flag specified on format {} ({})\n\t" - "(requested: {})", - texture_info.format_info()->name, ui::vulkan::to_string(format), - ui::vulkan::to_flags_string(static_cast( - required_flags & ~props.optimalTilingFeatures))); + "Texture Cache: Invalid usage flag specified on format {} (0x{:X})\n\t" + "(requested: 0x{:X})", + texture_info.format_info()->name, uint32_t(format), + uint32_t(required_flags & ~props.optimalTilingFeatures)); } if (texture_info.dimension != xenos::DataDimension::kCube && @@ -267,8 +264,8 @@ TextureCache::Texture* TextureCache::AllocateTexture( VkImageFormatProperties image_props; ifn.vkGetPhysicalDeviceImageFormatProperties( - *device_, format, image_info.imageType, image_info.tiling, - image_info.usage, image_info.flags, &image_props); + provider_.physical_device(), format, image_info.imageType, + image_info.tiling, image_info.usage, image_info.flags, &image_props); // TODO(DrChat): Actually check the image properties. @@ -317,10 +314,11 @@ TextureCache::Texture* TextureCache::AllocateTexture( } bool TextureCache::FreeTexture(Texture* texture) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); if (texture->in_flight_fence) { - VkResult status = dfn.vkGetFenceStatus(*device_, texture->in_flight_fence); + VkResult status = dfn.vkGetFenceStatus(device, texture->in_flight_fence); if (status != VK_SUCCESS && status != VK_ERROR_DEVICE_LOST) { // Texture still in flight. return false; @@ -328,11 +326,11 @@ bool TextureCache::FreeTexture(Texture* texture) { } if (texture->framebuffer) { - dfn.vkDestroyFramebuffer(*device_, texture->framebuffer, nullptr); + dfn.vkDestroyFramebuffer(device, texture->framebuffer, nullptr); } for (auto it = texture->views.begin(); it != texture->views.end();) { - dfn.vkDestroyImageView(*device_, (*it)->view, nullptr); + dfn.vkDestroyImageView(device, (*it)->view, nullptr); it = texture->views.erase(it); } @@ -529,14 +527,14 @@ TextureCache::Texture* TextureCache::DemandResolveTexture( } // Setup a debug name for the texture. - device_->DbgSetObjectName( - reinterpret_cast(texture->image), - VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, + provider_.SetDeviceObjectName( + VK_OBJECT_TYPE_IMAGE, uint64_t(texture->image), fmt::format( "RT: 0x{:08X} - 0x{:08X} ({}, {})", texture_info.memory.base_address, texture_info.memory.base_address + texture_info.memory.base_size, texture_info.format_info()->name, - get_dimension_name(texture_info.dimension))); + get_dimension_name(texture_info.dimension)) + .c_str()); // Setup an access watch. If this texture is touched, it is destroyed. WatchTexture(texture); @@ -612,14 +610,14 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, } // Setup a debug name for the texture. - device_->DbgSetObjectName( - reinterpret_cast(texture->image), - VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, + provider_.SetDeviceObjectName( + VK_OBJECT_TYPE_IMAGE, uint64_t(texture->image), fmt::format( "T: 0x{:08X} - 0x{:08X} ({}, {})", texture_info.memory.base_address, texture_info.memory.base_address + texture_info.memory.base_size, texture_info.format_info()->name, - get_dimension_name(texture_info.dimension))); + get_dimension_name(texture_info.dimension)) + .c_str()); textures_[texture_hash] = texture; COUNT_profile_set("gpu/texture_cache/textures", textures_.size()); @@ -703,8 +701,9 @@ TextureCache::TextureView* TextureCache::DemandView(Texture* texture, !is_cube ? 1 : 1 + texture->texture_info.depth; VkImageView view; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - auto status = dfn.vkCreateImageView(*device_, &view_info, nullptr, &view); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + auto status = dfn.vkCreateImageView(device, &view_info, nullptr, &view); CheckResult(status, "vkCreateImageView"); if (status == VK_SUCCESS) { auto texture_view = new TextureView(); @@ -844,9 +843,10 @@ TextureCache::Sampler* TextureCache::Demand(const SamplerInfo& sampler_info) { sampler_create_info.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; sampler_create_info.unnormalizedCoordinates = VK_FALSE; VkSampler vk_sampler; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); status = - dfn.vkCreateSampler(*device_, &sampler_create_info, nullptr, &vk_sampler); + dfn.vkCreateSampler(device, &sampler_create_info, nullptr, &vk_sampler); CheckResult(status, "vkCreateSampler"); if (status != VK_SUCCESS) { return nullptr; @@ -960,7 +960,8 @@ TextureCache::Texture* TextureCache::LookupAddress(uint32_t guest_address, void TextureCache::FlushPendingCommands(VkCommandBuffer command_buffer, VkFence completion_fence) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); auto status = dfn.vkEndCommandBuffer(command_buffer); CheckResult(status, "vkEndCommandBuffer"); @@ -970,21 +971,17 @@ void TextureCache::FlushPendingCommands(VkCommandBuffer command_buffer, submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &command_buffer; - if (device_queue_) { - auto status = - dfn.vkQueueSubmit(device_queue_, 1, &submit_info, completion_fence); - CheckResult(status, "vkQueueSubmit"); - } else { - std::lock_guard lock(device_->primary_queue_mutex()); - - auto status = dfn.vkQueueSubmit(device_->primary_queue(), 1, &submit_info, + { + ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition( + provider_.AcquireQueue(provider_.queue_family_graphics_compute(), 0)); + auto status = dfn.vkQueueSubmit(queue_acquisition.queue, 1, &submit_info, completion_fence); CheckResult(status, "vkQueueSubmit"); } - dfn.vkWaitForFences(*device_, 1, &completion_fence, VK_TRUE, -1); + dfn.vkWaitForFences(device, 1, &completion_fence, VK_TRUE, -1); staging_buffer_.Scavenge(); - dfn.vkResetFences(*device_, 1, &completion_fence); + dfn.vkResetFences(device, 1, &completion_fence); // Reset the command buffer and put it back into the recording state. dfn.vkResetCommandBuffer(command_buffer, 0); @@ -1169,7 +1166,7 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, TextureDump(src, unpack_buffer, unpack_length); } - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); // Transition the texture into a transfer destination layout. VkImageMemoryBarrier barrier; @@ -1313,7 +1310,7 @@ uint32_t TextureCache::ComputeTextureStorage(const TextureInfo& src) { } void TextureCache::WritebackTexture(Texture* texture) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); VkResult status = VK_SUCCESS; VkFence fence = wb_command_pool_->BeginBatch(); auto alloc = wb_staging_buffer_.Acquire(texture->alloc_info.size, fence); @@ -1361,7 +1358,8 @@ void TextureCache::WritebackTexture(Texture* texture) { // Submit the command buffer. // Submit commands and wait. { - std::lock_guard lock(device_->primary_queue_mutex()); + ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition( + provider_.AcquireQueue(provider_.queue_family_graphics_compute(), 0)); VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, @@ -1373,12 +1371,11 @@ void TextureCache::WritebackTexture(Texture* texture) { 0, nullptr, }; - status = - dfn.vkQueueSubmit(device_->primary_queue(), 1, &submit_info, fence); + status = dfn.vkQueueSubmit(queue_acquisition.queue, 1, &submit_info, fence); CheckResult(status, "vkQueueSubmit"); if (status == VK_SUCCESS) { - status = dfn.vkQueueWaitIdle(device_->primary_queue()); + status = dfn.vkQueueWaitIdle(queue_acquisition.queue); CheckResult(status, "vkQueueWaitIdle"); } } @@ -1471,8 +1468,9 @@ VkDescriptorSet TextureCache::PrepareTextureSet( // Update the descriptor set. if (update_set_info->image_write_count > 0) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - dfn.vkUpdateDescriptorSets(*device_, update_set_info->image_write_count, + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkUpdateDescriptorSets(device, update_set_info->image_write_count, update_set_info->image_writes, 0, nullptr); } @@ -1632,9 +1630,10 @@ void TextureCache::ClearCache() { textures_.clear(); COUNT_profile_set("gpu/texture_cache/textures", 0); - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); for (auto it = samplers_.begin(); it != samplers_.end(); ++it) { - dfn.vkDestroySampler(*device_, it->second->sampler, nullptr); + dfn.vkDestroySampler(device, it->second->sampler, nullptr); delete it->second; } samplers_.clear(); diff --git a/src/xenia/gpu/vulkan/texture_cache.h b/src/xenia/gpu/vulkan/texture_cache.h index dd1adbb91..a26f87fe2 100644 --- a/src/xenia/gpu/vulkan/texture_cache.h +++ b/src/xenia/gpu/vulkan/texture_cache.h @@ -26,10 +26,8 @@ #include "xenia/gpu/xenos.h" #include "xenia/ui/vulkan/circular_buffer.h" #include "xenia/ui/vulkan/fenced_pools.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" - -#include "third_party/vulkan/vk_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace gpu { @@ -78,7 +76,7 @@ class TextureCache { }; TextureCache(Memory* memory, RegisterFile* register_file, - TraceWriter* trace_writer, ui::vulkan::VulkanDevice* device); + TraceWriter* trace_writer, ui::vulkan::VulkanProvider& provider); ~TextureCache(); VkResult Initialize(); @@ -203,8 +201,7 @@ class TextureCache { RegisterFile* register_file_ = nullptr; TraceWriter* trace_writer_ = nullptr; - ui::vulkan::VulkanDevice* device_ = nullptr; - VkQueue device_queue_ = nullptr; + ui::vulkan::VulkanProvider& provider_; std::unique_ptr wb_command_pool_ = nullptr; std::unique_ptr descriptor_pool_ = nullptr; diff --git a/src/xenia/gpu/vulkan/texture_config.h b/src/xenia/gpu/vulkan/texture_config.h index 6587b68cc..dad6f1aec 100644 --- a/src/xenia/gpu/vulkan/texture_config.h +++ b/src/xenia/gpu/vulkan/texture_config.h @@ -12,7 +12,7 @@ #include "xenia/gpu/texture_info.h" #include "xenia/gpu/xenos.h" -#include "xenia/ui/vulkan/vulkan.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace gpu { diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index d9da7d9bd..be7268329 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -21,6 +21,7 @@ #include "xenia/gpu/vulkan/vulkan_gpu_flags.h" #include "xenia/gpu/vulkan/vulkan_graphics_system.h" #include "xenia/gpu/xenos.h" +#include "xenia/ui/vulkan/vulkan_presenter.h" #include "xenia/ui/vulkan/vulkan_util.h" namespace xe { @@ -29,21 +30,22 @@ namespace vulkan { using namespace xe::literals; using namespace xe::gpu::xenos; - -using xe::ui::vulkan::CheckResult; +using xe::ui::vulkan::util::CheckResult; constexpr size_t kDefaultBufferCacheCapacity = 256_MiB; VulkanCommandProcessor::VulkanCommandProcessor( VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state) - : CommandProcessor(graphics_system, kernel_state) {} + : CommandProcessor(graphics_system, kernel_state), + swap_submission_tracker_(GetVulkanProvider()) {} VulkanCommandProcessor::~VulkanCommandProcessor() = default; void VulkanCommandProcessor::RequestFrameTrace( const std::filesystem::path& root_path) { // Override traces if renderdoc is attached. - if (device_->is_renderdoc_attached()) { + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + if (provider.renderdoc_api().api_1_0_0()) { trace_requested_ = true; return; } @@ -67,21 +69,12 @@ bool VulkanCommandProcessor::SetupContext() { return false; } - // Acquire our device and queue. - auto context = static_cast(context_.get()); - device_ = context->device(); - queue_ = device_->AcquireQueue(device_->queue_family_index()); - if (!queue_) { - // Need to reuse primary queue (with locks). - queue_ = device_->primary_queue(); - queue_mutex_ = &device_->primary_queue_mutex(); - } - + ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); VkResult status = VK_SUCCESS; // Setup a blitter. - blitter_ = std::make_unique(); - status = blitter_->Initialize(device_); + blitter_ = std::make_unique(provider); + status = blitter_->Initialize(); if (status != VK_SUCCESS) { XELOGE("Unable to initialize blitter"); blitter_->Shutdown(); @@ -90,11 +83,11 @@ bool VulkanCommandProcessor::SetupContext() { // Setup fenced pools used for all our per-frame/per-draw resources. command_buffer_pool_ = std::make_unique( - *device_, device_->queue_family_index()); + provider, provider.queue_family_graphics_compute()); // Initialize the state machine caches. buffer_cache_ = std::make_unique( - register_file_, memory_, device_, kDefaultBufferCacheCapacity); + register_file_, memory_, provider, kDefaultBufferCacheCapacity); status = buffer_cache_->Initialize(); if (status != VK_SUCCESS) { XELOGE("Unable to initialize buffer cache"); @@ -103,7 +96,7 @@ bool VulkanCommandProcessor::SetupContext() { } texture_cache_ = std::make_unique(memory_, register_file_, - &trace_writer_, device_); + &trace_writer_, provider); status = texture_cache_->Initialize(); if (status != VK_SUCCESS) { XELOGE("Unable to initialize texture cache"); @@ -111,7 +104,7 @@ bool VulkanCommandProcessor::SetupContext() { return false; } - pipeline_cache_ = std::make_unique(register_file_, device_); + pipeline_cache_ = std::make_unique(register_file_, provider); status = pipeline_cache_->Initialize( buffer_cache_->constant_descriptor_set_layout(), texture_cache_->texture_descriptor_set_layout(), @@ -122,7 +115,7 @@ bool VulkanCommandProcessor::SetupContext() { return false; } - render_cache_ = std::make_unique(register_file_, device_); + render_cache_ = std::make_unique(register_file_, provider); status = render_cache_->Initialize(); if (status != VK_SUCCESS) { XELOGE("Unable to initialize render cache"); @@ -136,10 +129,14 @@ bool VulkanCommandProcessor::SetupContext() { void VulkanCommandProcessor::ShutdownContext() { // TODO(benvanik): wait until idle. - if (swap_state_.front_buffer_texture) { - // Free swap chain image. - DestroySwapImage(); - } + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); + + swap_submission_tracker_.Shutdown(); + ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyFramebuffer, device, + swap_framebuffer_); + swap_framebuffer_version_ = UINT64_MAX; buffer_cache_.reset(); pipeline_cache_.reset(); @@ -151,12 +148,6 @@ void VulkanCommandProcessor::ShutdownContext() { // Free all pools. This must come after all of our caches clean up. command_buffer_pool_.reset(); - // Release queue, if we were using an acquired one. - if (!queue_mutex_) { - device_->ReleaseQueue(queue_, device_->queue_family_index()); - queue_ = nullptr; - } - CommandProcessor::ShutdownContext(); } @@ -178,26 +169,6 @@ void VulkanCommandProcessor::MakeCoherent() { } } -void VulkanCommandProcessor::PrepareForWait() { - SCOPE_profile_cpu_f("gpu"); - - CommandProcessor::PrepareForWait(); - - // TODO(benvanik): fences and fancy stuff. We should figure out a way to - // make interrupt callbacks from the GPU so that we don't have to do a full - // synchronize here. - // glFlush(); - // glFinish(); - - context_->ClearCurrent(); -} - -void VulkanCommandProcessor::ReturnFromWait() { - context_->MakeCurrent(); - - CommandProcessor::ReturnFromWait(); -} - void VulkanCommandProcessor::WriteRegister(uint32_t index, uint32_t value) { CommandProcessor::WriteRegister(index, value); @@ -223,7 +194,7 @@ void VulkanCommandProcessor::WriteRegister(uint32_t index, uint32_t value) { } else if (index == XE_GPU_REG_DC_LUT_PWL_DATA) { UpdateGammaRampValue(GammaRampType::kPWL, value); } else if (index == XE_GPU_REG_DC_LUT_30_COLOR) { - UpdateGammaRampValue(GammaRampType::kNormal, value); + UpdateGammaRampValue(GammaRampType::kTable, value); } else if (index >= XE_GPU_REG_DC_LUT_RW_MODE && index <= XE_GPU_REG_DC_LUTA_CONTROL) { uint32_t offset = index - XE_GPU_REG_DC_LUT_RW_MODE; @@ -237,109 +208,6 @@ void VulkanCommandProcessor::WriteRegister(uint32_t index, uint32_t value) { } } -void VulkanCommandProcessor::CreateSwapImage(VkCommandBuffer setup_buffer, - VkExtent2D extents) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - - VkImageCreateInfo image_info; - std::memset(&image_info, 0, sizeof(VkImageCreateInfo)); - image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = VK_FORMAT_R8G8B8A8_UNORM; - image_info.extent = {extents.width, extents.height, 1}; - image_info.mipLevels = 1; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_info.usage = - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_info.queueFamilyIndexCount = 0; - image_info.pQueueFamilyIndices = nullptr; - image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - VkImage image_fb; - auto status = dfn.vkCreateImage(*device_, &image_info, nullptr, &image_fb); - CheckResult(status, "vkCreateImage"); - - // Bind memory to image. - VkMemoryRequirements mem_requirements; - dfn.vkGetImageMemoryRequirements(*device_, image_fb, &mem_requirements); - fb_memory_ = device_->AllocateMemory(mem_requirements, 0); - assert_not_null(fb_memory_); - - status = dfn.vkBindImageMemory(*device_, image_fb, fb_memory_, 0); - CheckResult(status, "vkBindImageMemory"); - - std::lock_guard lock(swap_state_.mutex); - swap_state_.front_buffer_texture = reinterpret_cast(image_fb); - - VkImageViewCreateInfo view_create_info = { - VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - nullptr, - 0, - image_fb, - VK_IMAGE_VIEW_TYPE_2D, - VK_FORMAT_R8G8B8A8_UNORM, - {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A}, - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, - }; - status = dfn.vkCreateImageView(*device_, &view_create_info, nullptr, - &fb_image_view_); - CheckResult(status, "vkCreateImageView"); - - VkFramebufferCreateInfo framebuffer_create_info = { - VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - nullptr, - 0, - blitter_->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, true), - 1, - &fb_image_view_, - extents.width, - extents.height, - 1, - }; - status = dfn.vkCreateFramebuffer(*device_, &framebuffer_create_info, nullptr, - &fb_framebuffer_); - CheckResult(status, "vkCreateFramebuffer"); - - // Transition image to general layout. - VkImageMemoryBarrier barrier; - std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image_fb; - barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - - dfn.vkCmdPipelineBarrier(setup_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, - nullptr, 0, nullptr, 1, &barrier); -} - -void VulkanCommandProcessor::DestroySwapImage() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - - dfn.vkDestroyFramebuffer(*device_, fb_framebuffer_, nullptr); - dfn.vkDestroyImageView(*device_, fb_image_view_, nullptr); - - std::lock_guard lock(swap_state_.mutex); - dfn.vkDestroyImage( - *device_, reinterpret_cast(swap_state_.front_buffer_texture), - nullptr); - dfn.vkFreeMemory(*device_, fb_memory_, nullptr); - - swap_state_.front_buffer_texture = 0; - fb_memory_ = nullptr; - fb_framebuffer_ = nullptr; - fb_image_view_ = nullptr; -} - void VulkanCommandProcessor::BeginFrame() { assert_false(frame_open_); @@ -351,7 +219,8 @@ void VulkanCommandProcessor::BeginFrame() { current_command_buffer_ = command_buffer_pool_->AcquireEntry(); current_setup_buffer_ = command_buffer_pool_->AcquireEntry(); - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); VkCommandBufferBeginInfo command_buffer_begin_info; command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; @@ -368,19 +237,14 @@ void VulkanCommandProcessor::BeginFrame() { // Flag renderdoc down to start a capture if requested. // The capture will end when these commands are submitted to the queue. - static uint32_t frame = 0; - if (device_->is_renderdoc_attached() && !capturing_ && - (cvars::vulkan_renderdoc_capture_all || trace_requested_)) { - if (queue_mutex_) { - queue_mutex_->lock(); - } - - capturing_ = true; - trace_requested_ = false; - device_->BeginRenderDocFrameCapture(); - - if (queue_mutex_) { - queue_mutex_->unlock(); + if ((cvars::vulkan_renderdoc_capture_all || trace_requested_) && + !capturing_) { + const RENDERDOC_API_1_0_0* renderdoc_api = + provider.renderdoc_api().api_1_0_0(); + if (renderdoc_api && !renderdoc_api->IsFrameCapturing()) { + capturing_ = true; + trace_requested_ = false; + renderdoc_api->StartFrameCapture(nullptr, nullptr); } } @@ -393,7 +257,8 @@ void VulkanCommandProcessor::EndFrame() { current_render_state_ = nullptr; } - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); VkResult status = VK_SUCCESS; status = dfn.vkEndCommandBuffer(current_setup_buffer_); CheckResult(status, "vkEndCommandBuffer"); @@ -407,170 +272,348 @@ void VulkanCommandProcessor::EndFrame() { frame_open_ = false; } -void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, - uint32_t frontbuffer_width, - uint32_t frontbuffer_height) { +void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, + uint32_t frontbuffer_width, + uint32_t frontbuffer_height) { SCOPE_profile_cpu_f("gpu"); - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - - // Build a final command buffer that copies the game's frontbuffer texture - // into our backbuffer texture. - VkCommandBuffer copy_commands = nullptr; - bool opened_batch; - if (command_buffer_pool_->has_open_batch()) { - copy_commands = command_buffer_pool_->AcquireEntry(); - opened_batch = false; - } else { - current_batch_fence_ = command_buffer_pool_->BeginBatch(); - copy_commands = command_buffer_pool_->AcquireEntry(); - opened_batch = true; + ui::Presenter* presenter = graphics_system_->presenter(); + if (!presenter) { + return; } - VkCommandBufferBeginInfo begin_info; - std::memset(&begin_info, 0, sizeof(begin_info)); - begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - auto status = dfn.vkBeginCommandBuffer(copy_commands, &begin_info); - CheckResult(status, "vkBeginCommandBuffer"); - if (!frontbuffer_ptr) { // Trace viewer does this. frontbuffer_ptr = last_copy_base_; } - if (!swap_state_.front_buffer_texture) { - CreateSwapImage(copy_commands, {frontbuffer_width, frontbuffer_height}); - } - auto swap_fb = reinterpret_cast(swap_state_.front_buffer_texture); - - auto& regs = *register_file_; - int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0; - auto group = - reinterpret_cast(®s.values[r]); - auto& fetch = group->texture_fetch; - - TextureInfo texture_info; - if (!TextureInfo::Prepare(group->texture_fetch, &texture_info)) { - assert_always(); - } - - // Issue the commands to copy the game's frontbuffer to our backbuffer. - auto texture = texture_cache_->Lookup(texture_info); - if (texture) { - texture->in_flight_fence = current_batch_fence_; - - // Insert a barrier so the GPU finishes writing to the image. - VkImageMemoryBarrier barrier; - std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier.oldLayout = texture->image_layout; - barrier.newLayout = texture->image_layout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = texture->image; - barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - - dfn.vkCmdPipelineBarrier(copy_commands, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, - nullptr, 0, nullptr, 1, &barrier); - - barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - barrier.image = swap_fb; - dfn.vkCmdPipelineBarrier(copy_commands, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, - 0, nullptr, 0, nullptr, 1, &barrier); - - // Part of the source image that we want to blit from. - VkRect2D src_rect = { - {0, 0}, - {texture->texture_info.width + 1, texture->texture_info.height + 1}, - }; - VkRect2D dst_rect = {{0, 0}, {frontbuffer_width, frontbuffer_height}}; - - VkViewport viewport = { - 0.f, 0.f, float(frontbuffer_width), float(frontbuffer_height), - 0.f, 1.f}; - - VkRect2D scissor = {{0, 0}, {frontbuffer_width, frontbuffer_height}}; - - blitter_->BlitTexture2D( - copy_commands, current_batch_fence_, - texture_cache_->DemandView(texture, 0x688)->view, src_rect, - {texture->texture_info.width + 1, texture->texture_info.height + 1}, - VK_FORMAT_R8G8B8A8_UNORM, dst_rect, - {frontbuffer_width, frontbuffer_height}, fb_framebuffer_, viewport, - scissor, VK_FILTER_LINEAR, true, true); - - std::swap(barrier.oldLayout, barrier.newLayout); - barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - dfn.vkCmdPipelineBarrier( - copy_commands, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - - std::lock_guard lock(swap_state_.mutex); - swap_state_.width = frontbuffer_width; - swap_state_.height = frontbuffer_height; - } - - status = dfn.vkEndCommandBuffer(copy_commands); - CheckResult(status, "vkEndCommandBuffer"); - - // Queue up current command buffers. - // TODO(benvanik): bigger batches. std::vector submit_buffers; if (frame_open_) { // TODO(DrChat): If the setup buffer is empty, don't bother queueing it up. submit_buffers.push_back(current_setup_buffer_); submit_buffers.push_back(current_command_buffer_); - EndFrame(); + } + bool submitted = false; + + auto& regs = *register_file_; + int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0; + auto group = + reinterpret_cast(®s.values[r]); + TextureInfo texture_info; + if (!TextureInfo::Prepare(group->texture_fetch, &texture_info)) { + assert_always(); + } + auto texture = texture_cache_->Lookup(texture_info); + if (texture) { + presenter->RefreshGuestOutput( + frontbuffer_width, frontbuffer_height, 1280, 720, + [this, frontbuffer_width, frontbuffer_height, texture, &submit_buffers, + &submitted]( + ui::Presenter::GuestOutputRefreshContext& context) -> bool { + auto& vulkan_context = static_cast< + ui::vulkan::VulkanPresenter::VulkanGuestOutputRefreshContext&>( + context); + + ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = + provider.dfn(); + VkDevice device = provider.device(); + + // Make sure the framebuffer is for the current guest output image. + if (swap_framebuffer_ != VK_NULL_HANDLE && + swap_framebuffer_version_ != vulkan_context.image_version()) { + swap_submission_tracker_.AwaitAllSubmissionsCompletion(); + dfn.vkDestroyFramebuffer(device, swap_framebuffer_, nullptr); + swap_framebuffer_ = VK_NULL_HANDLE; + } + if (swap_framebuffer_ == VK_NULL_HANDLE) { + VkRenderPass render_pass = blitter_->GetRenderPass( + ui::vulkan::VulkanPresenter::kGuestOutputFormat, true); + if (render_pass == VK_NULL_HANDLE) { + return false; + } + VkImageView guest_output_image_view = vulkan_context.image_view(); + VkFramebufferCreateInfo swap_framebuffer_create_info; + swap_framebuffer_create_info.sType = + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + swap_framebuffer_create_info.pNext = nullptr; + swap_framebuffer_create_info.flags = 0; + swap_framebuffer_create_info.renderPass = render_pass; + swap_framebuffer_create_info.attachmentCount = 1; + swap_framebuffer_create_info.pAttachments = + &guest_output_image_view; + swap_framebuffer_create_info.width = frontbuffer_width; + swap_framebuffer_create_info.height = frontbuffer_height; + swap_framebuffer_create_info.layers = 1; + if (dfn.vkCreateFramebuffer(device, &swap_framebuffer_create_info, + nullptr, + &swap_framebuffer_) != VK_SUCCESS) { + XELOGE( + "Failed to create the Vulkan framebuffer for presentation"); + return false; + } + swap_framebuffer_version_ = vulkan_context.image_version(); + } + + // Build a final command buffer that copies the game's frontbuffer + // texture into our backbuffer texture. + VkCommandBuffer copy_commands = nullptr; + bool opened_batch = !command_buffer_pool_->has_open_batch(); + if (!command_buffer_pool_->has_open_batch()) { + current_batch_fence_ = command_buffer_pool_->BeginBatch(); + } + copy_commands = command_buffer_pool_->AcquireEntry(); + + VkCommandBufferBeginInfo command_buffer_begin_info; + command_buffer_begin_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + command_buffer_begin_info.pNext = nullptr; + command_buffer_begin_info.flags = + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + command_buffer_begin_info.pInheritanceInfo = nullptr; + dfn.vkBeginCommandBuffer(copy_commands, &command_buffer_begin_info); + + texture->in_flight_fence = current_batch_fence_; + + // Insert a barrier so the GPU finishes writing to the image, and a + // barrier after the last presenter's usage of the guest output image. + VkPipelineStageFlags acquire_barrier_src_stages = 0; + VkPipelineStageFlags acquire_barrier_dst_stages = 0; + VkImageMemoryBarrier acquire_image_memory_barriers[2]; + uint32_t acquire_image_memory_barrier_count = 0; + { + acquire_barrier_src_stages |= + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT; + acquire_barrier_dst_stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + VkImageMemoryBarrier& acquire_image_memory_barrier = + acquire_image_memory_barriers + [acquire_image_memory_barrier_count++]; + acquire_image_memory_barrier.sType = + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + acquire_image_memory_barrier.pNext = nullptr; + acquire_image_memory_barrier.srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT; + acquire_image_memory_barrier.dstAccessMask = + VK_ACCESS_SHADER_READ_BIT; + acquire_image_memory_barrier.oldLayout = texture->image_layout; + acquire_image_memory_barrier.newLayout = texture->image_layout; + acquire_image_memory_barrier.srcQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + acquire_image_memory_barrier.dstQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + acquire_image_memory_barrier.image = texture->image; + ui::vulkan::util::InitializeSubresourceRange( + acquire_image_memory_barrier.subresourceRange); + } + { + acquire_barrier_dst_stages |= + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkImageMemoryBarrier& acquire_image_memory_barrier = + acquire_image_memory_barriers + [acquire_image_memory_barrier_count++]; + acquire_image_memory_barrier.sType = + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + acquire_image_memory_barrier.pNext = nullptr; + acquire_image_memory_barrier.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + // Will be overwriting all the contents. + acquire_image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + acquire_image_memory_barrier.newLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + acquire_image_memory_barrier.srcQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + acquire_image_memory_barrier.dstQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + acquire_image_memory_barrier.image = vulkan_context.image(); + ui::vulkan::util::InitializeSubresourceRange( + acquire_image_memory_barrier.subresourceRange); + if (vulkan_context.image_ever_written_previously()) { + acquire_barrier_src_stages |= + ui::vulkan::VulkanPresenter::kGuestOutputInternalStageMask; + acquire_image_memory_barrier.srcAccessMask = + ui::vulkan::VulkanPresenter::kGuestOutputInternalAccessMask; + } else { + acquire_image_memory_barrier.srcAccessMask = 0; + } + } + assert_not_zero(acquire_barrier_src_stages); + assert_not_zero(acquire_barrier_dst_stages); + assert_not_zero(acquire_image_memory_barrier_count); + dfn.vkCmdPipelineBarrier(copy_commands, acquire_barrier_src_stages, + acquire_barrier_dst_stages, 0, 0, nullptr, 0, + nullptr, acquire_image_memory_barrier_count, + acquire_image_memory_barriers); + + // Part of the source image that we want to blit from. + VkRect2D src_rect = { + {0, 0}, + {texture->texture_info.width + 1, + texture->texture_info.height + 1}, + }; + VkRect2D dst_rect = {{0, 0}, {frontbuffer_width, frontbuffer_height}}; + + VkViewport viewport = { + 0.f, 0.f, float(frontbuffer_width), float(frontbuffer_height), + 0.f, 1.f}; + + VkRect2D scissor = {{0, 0}, {frontbuffer_width, frontbuffer_height}}; + + blitter_->BlitTexture2D( + copy_commands, current_batch_fence_, + texture_cache_->DemandView(texture, 0x688)->view, src_rect, + {texture->texture_info.width + 1, + texture->texture_info.height + 1}, + ui::vulkan::VulkanPresenter::kGuestOutputFormat, dst_rect, + {frontbuffer_width, frontbuffer_height}, swap_framebuffer_, + viewport, scissor, VK_FILTER_LINEAR, true, true); + + VkPipelineStageFlags release_barrier_src_stages = 0; + VkPipelineStageFlags release_barrier_dst_stages = 0; + VkImageMemoryBarrier release_image_memory_barriers[2]; + uint32_t release_image_memory_barrier_count = 0; + { + release_barrier_src_stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + release_barrier_dst_stages |= + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT; + VkImageMemoryBarrier& release_image_memory_barrier = + release_image_memory_barriers + [release_image_memory_barrier_count++]; + release_image_memory_barrier.sType = + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + release_image_memory_barrier.pNext = nullptr; + release_image_memory_barrier.srcAccessMask = + VK_ACCESS_SHADER_READ_BIT; + release_image_memory_barrier.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_TRANSFER_WRITE_BIT; + release_image_memory_barrier.oldLayout = texture->image_layout; + release_image_memory_barrier.newLayout = texture->image_layout; + release_image_memory_barrier.srcQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + release_image_memory_barrier.dstQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + release_image_memory_barrier.image = texture->image; + ui::vulkan::util::InitializeSubresourceRange( + release_image_memory_barrier.subresourceRange); + } + { + release_barrier_src_stages |= + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + release_barrier_dst_stages |= + ui::vulkan::VulkanPresenter::kGuestOutputInternalStageMask; + VkImageMemoryBarrier& release_image_memory_barrier = + release_image_memory_barriers + [release_image_memory_barrier_count++]; + release_image_memory_barrier.sType = + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + release_image_memory_barrier.pNext = nullptr; + release_image_memory_barrier.srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + release_image_memory_barrier.dstAccessMask = + ui::vulkan::VulkanPresenter::kGuestOutputInternalAccessMask; + release_image_memory_barrier.oldLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + release_image_memory_barrier.newLayout = + ui::vulkan::VulkanPresenter::kGuestOutputInternalLayout; + release_image_memory_barrier.srcQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + release_image_memory_barrier.dstQueueFamilyIndex = + VK_QUEUE_FAMILY_IGNORED; + release_image_memory_barrier.image = vulkan_context.image(); + ui::vulkan::util::InitializeSubresourceRange( + release_image_memory_barrier.subresourceRange); + } + assert_not_zero(release_barrier_src_stages); + assert_not_zero(release_barrier_dst_stages); + assert_not_zero(release_image_memory_barrier_count); + dfn.vkCmdPipelineBarrier(copy_commands, release_barrier_src_stages, + release_barrier_dst_stages, 0, 0, nullptr, 0, + nullptr, release_image_memory_barrier_count, + release_image_memory_barriers); + + dfn.vkEndCommandBuffer(copy_commands); + + // Need to submit all the commands before giving the image back to the + // presenter so it can submit its own commands for displaying it to + // the queue. + + if (frame_open_) { + EndFrame(); + } + + if (opened_batch) { + command_buffer_pool_->EndBatch(); + } + + submit_buffers.push_back(copy_commands); + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = uint32_t(submit_buffers.size()); + submit_info.pCommandBuffers = submit_buffers.data(); + VkResult submit_result; + { + ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition( + provider.AcquireQueue(provider.queue_family_graphics_compute(), + 0)); + submit_result = dfn.vkQueueSubmit( + queue_acquisition.queue, 1, &submit_info, current_batch_fence_); + } + if (submit_result != VK_SUCCESS) { + return false; + } + submitted = true; + + // Signal the fence for destroying objects depending on the guest + // output image. + { + ui::vulkan::VulkanSubmissionTracker::FenceAcquisition + fence_acqusition = + swap_submission_tracker_.AcquireFenceToAdvanceSubmission(); + ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition( + provider.AcquireQueue(provider.queue_family_graphics_compute(), + 0)); + if (dfn.vkQueueSubmit(queue_acquisition.queue, 0, nullptr, + fence_acqusition.fence()) != VK_SUCCESS) { + fence_acqusition.SubmissionSucceededSignalFailed(); + } + } + + return true; + }); } - if (opened_batch) { - command_buffer_pool_->EndBatch(); - } + ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); - submit_buffers.push_back(copy_commands); - if (!submit_buffers.empty()) { - // TODO(benvanik): move to CP or to host (trace dump, etc). - // This only needs to surround a vkQueueSubmit. - if (queue_mutex_) { - queue_mutex_->lock(); + if (!submitted) { + // End the frame even if failed to refresh the guest output. + if (frame_open_) { + EndFrame(); } - - VkSubmitInfo submit_info; - std::memset(&submit_info, 0, sizeof(VkSubmitInfo)); - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.commandBufferCount = uint32_t(submit_buffers.size()); - submit_info.pCommandBuffers = submit_buffers.data(); - - submit_info.waitSemaphoreCount = 0; - submit_info.pWaitSemaphores = nullptr; - submit_info.pWaitDstStageMask = nullptr; - - submit_info.signalSemaphoreCount = 0; - submit_info.pSignalSemaphores = nullptr; - - status = dfn.vkQueueSubmit(queue_, 1, &submit_info, current_batch_fence_); - if (device_->is_renderdoc_attached() && capturing_) { - device_->EndRenderDocFrameCapture(); - capturing_ = false; - } - if (queue_mutex_) { - queue_mutex_->unlock(); + if (!submit_buffers.empty() || current_batch_fence_ != VK_NULL_HANDLE) { + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = uint32_t(submit_buffers.size()); + submit_info.pCommandBuffers = submit_buffers.data(); + VkResult submit_result; + { + ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition( + provider.AcquireQueue(provider.queue_family_graphics_compute(), 0)); + submit_result = dfn.vkQueueSubmit(queue_acquisition.queue, 1, + &submit_info, current_batch_fence_); + } + CheckResult(submit_result, "vkQueueSubmit"); } } - dfn.vkWaitForFences(*device_, 1, ¤t_batch_fence_, VK_TRUE, -1); + if (current_batch_fence_ != VK_NULL_HANDLE) { + dfn.vkWaitForFences(device, 1, ¤t_batch_fence_, VK_TRUE, -1); + } if (cache_clear_requested_) { cache_clear_requested_ = false; @@ -675,7 +718,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, } } - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); // Configure the pipeline for drawing. // This encodes all render state (blend, depth, etc), our shader stages, @@ -767,7 +811,8 @@ bool VulkanCommandProcessor::PopulateConstants(VkCommandBuffer command_buffer, uint32_t set_constant_offsets[2] = { static_cast(constant_offsets.first), static_cast(constant_offsets.second)}; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); dfn.vkCmdBindDescriptorSets( command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &constant_descriptor_set, @@ -820,7 +865,8 @@ bool VulkanCommandProcessor::PopulateIndexBuffer( VkIndexType index_type = info.format == xenos::IndexFormat::kInt32 ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); dfn.vkCmdBindIndexBuffer(command_buffer, buffer_ref.first, buffer_ref.second, index_type); @@ -850,7 +896,8 @@ bool VulkanCommandProcessor::PopulateVertexBuffers( return false; } - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); dfn.vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_cache_->pipeline_layout(), 2, 1, &descriptor_set, 0, nullptr); @@ -875,7 +922,8 @@ bool VulkanCommandProcessor::PopulateSamplers(VkCommandBuffer command_buffer, return false; } - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); dfn.vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_cache_->pipeline_layout(), 1, 1, &descriptor_set, 0, nullptr); @@ -1083,7 +1131,9 @@ bool VulkanCommandProcessor::IssueCopy() { render_cache_->EndRenderPass(); current_render_state_ = nullptr; } - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const ui::vulkan::VulkanProvider& provider = GetVulkanProvider(); + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); auto command_buffer = current_command_buffer_; if (texture->image_layout == VK_IMAGE_LAYOUT_UNDEFINED) { @@ -1219,8 +1269,8 @@ bool VulkanCommandProcessor::IssueCopy() { 1, }; - VkResult res = dfn.vkCreateFramebuffer(*device_, &fb_create_info, - nullptr, &texture->framebuffer); + VkResult res = dfn.vkCreateFramebuffer(device, &fb_create_info, nullptr, + &texture->framebuffer); CheckResult(res, "vkCreateFramebuffer"); } diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.h b/src/xenia/gpu/vulkan/vulkan_command_processor.h index c1fb1735e..2c2738440 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.h +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.h @@ -11,6 +11,7 @@ #define XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_ #include +#include #include #include #include @@ -27,21 +28,21 @@ #include "xenia/gpu/vulkan/pipeline_cache.h" #include "xenia/gpu/vulkan/render_cache.h" #include "xenia/gpu/vulkan/texture_cache.h" +#include "xenia/gpu/vulkan/vulkan_graphics_system.h" #include "xenia/gpu/vulkan/vulkan_shader.h" #include "xenia/gpu/xenos.h" #include "xenia/kernel/xthread.h" #include "xenia/memory.h" #include "xenia/ui/vulkan/blitter.h" #include "xenia/ui/vulkan/fenced_pools.h" -#include "xenia/ui/vulkan/vulkan_context.h" -#include "xenia/ui/vulkan/vulkan_device.h" +#include "xenia/ui/vulkan/vulkan_provider.h" +#include "xenia/ui/vulkan/vulkan_submission_tracker.h" #include "xenia/ui/vulkan/vulkan_util.h" namespace xe { namespace gpu { namespace vulkan { -class VulkanGraphicsSystem; class TextureCache; class VulkanCommandProcessor : public CommandProcessor { @@ -55,6 +56,11 @@ class VulkanCommandProcessor : public CommandProcessor { void RestoreEdramSnapshot(const void* snapshot) override; void ClearCaches() override; + ui::vulkan::VulkanProvider& GetVulkanProvider() const { + return *static_cast( + graphics_system_->provider()); + } + RenderCache* render_cache() { return render_cache_.get(); } private: @@ -62,19 +68,14 @@ class VulkanCommandProcessor : public CommandProcessor { void ShutdownContext() override; void MakeCoherent() override; - void PrepareForWait() override; - void ReturnFromWait() override; void WriteRegister(uint32_t index, uint32_t value) override; void BeginFrame(); void EndFrame(); - void CreateSwapImage(VkCommandBuffer setup_buffer, VkExtent2D extents); - void DestroySwapImage(); - - void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, - uint32_t frontbuffer_height) override; + void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, + uint32_t frontbuffer_height) override; Shader* LoadShader(xenos::ShaderType shader_type, uint32_t guest_address, const uint32_t* host_address, @@ -99,13 +100,6 @@ class VulkanCommandProcessor : public CommandProcessor { void InitializeTrace() override; - xe::ui::vulkan::VulkanDevice* device_ = nullptr; - - // front buffer / back buffer memory - VkDeviceMemory fb_memory_ = nullptr; - VkImageView fb_image_view_ = nullptr; - VkFramebuffer fb_framebuffer_ = nullptr; - uint64_t dirty_float_constants_ = 0; // Dirty float constants in blocks of 4 uint8_t dirty_bool_constants_ = 0; uint32_t dirty_loop_constants_ = 0; @@ -114,14 +108,6 @@ class VulkanCommandProcessor : public CommandProcessor { uint32_t coher_base_vc_ = 0; uint32_t coher_size_vc_ = 0; - // TODO(benvanik): abstract behind context? - // Queue used to submit work. This may be a dedicated queue for the command - // processor and no locking will be required for use. If a dedicated queue - // was not available this will be the device primary_queue and the - // queue_mutex must be used to synchronize access to it. - VkQueue queue_ = nullptr; - std::mutex* queue_mutex_ = nullptr; - // Last copy base address, for debugging only. uint32_t last_copy_base_ = 0; @@ -142,6 +128,10 @@ class VulkanCommandProcessor : public CommandProcessor { VkCommandBuffer current_command_buffer_ = nullptr; VkCommandBuffer current_setup_buffer_ = nullptr; VkFence current_batch_fence_; + + ui::vulkan::VulkanSubmissionTracker swap_submission_tracker_; + VkFramebuffer swap_framebuffer_ = VK_NULL_HANDLE; + uint64_t swap_framebuffer_version_ = UINT64_MAX; }; } // namespace vulkan diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc index 9c852c706..c42510050 100644 --- a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc @@ -2,267 +2,32 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/gpu/vulkan/vulkan_graphics_system.h" -#include -#include - -#include "xenia/base/logging.h" -#include "xenia/base/profiling.h" -#include "xenia/cpu/processor.h" -#include "xenia/gpu/gpu_flags.h" #include "xenia/gpu/vulkan/vulkan_command_processor.h" -#include "xenia/gpu/vulkan/vulkan_gpu_flags.h" #include "xenia/ui/vulkan/vulkan_provider.h" -#include "xenia/ui/vulkan/vulkan_swap_chain.h" -#include "xenia/ui/vulkan/vulkan_util.h" -#include "xenia/ui/window.h" +#include "xenia/xbox.h" namespace xe { namespace gpu { namespace vulkan { -using xe::ui::RawImage; -using xe::ui::vulkan::CheckResult; - VulkanGraphicsSystem::VulkanGraphicsSystem() {} -VulkanGraphicsSystem::~VulkanGraphicsSystem() = default; + +VulkanGraphicsSystem::~VulkanGraphicsSystem() {} X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, - ui::Window* target_window) { - // Must create the provider so we can create contexts. - auto provider = xe::ui::vulkan::VulkanProvider::Create(); - device_ = provider->device(); - provider_ = std::move(provider); - - auto result = GraphicsSystem::Setup(processor, kernel_state, target_window); - if (result) { - return result; - } - - if (target_window) { - display_context_ = reinterpret_cast( - target_window->context()); - } - - // Create our own command pool we can use for captures. - VkCommandPoolCreateInfo create_info = { - VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - nullptr, - VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | - VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - device_->queue_family_index(), - }; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - auto status = - dfn.vkCreateCommandPool(*device_, &create_info, nullptr, &command_pool_); - CheckResult(status, "vkCreateCommandPool"); - - return X_STATUS_SUCCESS; -} - -void VulkanGraphicsSystem::Shutdown() { - GraphicsSystem::Shutdown(); - - if (command_pool_ != VK_NULL_HANDLE) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - dfn.vkDestroyCommandPool(*device_, command_pool_, nullptr); - command_pool_ = VK_NULL_HANDLE; - } -} - -std::unique_ptr VulkanGraphicsSystem::Capture() { - auto& swap_state = command_processor_->swap_state(); - std::lock_guard lock(swap_state.mutex); - if (!swap_state.front_buffer_texture) { - return nullptr; - } - - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - VkResult status = VK_SUCCESS; - - VkCommandBufferAllocateInfo alloc_info = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - nullptr, - command_pool_, - VK_COMMAND_BUFFER_LEVEL_PRIMARY, - 1, - }; - - VkCommandBuffer cmd = nullptr; - status = dfn.vkAllocateCommandBuffers(*device_, &alloc_info, &cmd); - CheckResult(status, "vkAllocateCommandBuffers"); - if (status != VK_SUCCESS) { - return nullptr; - } - - VkCommandBufferBeginInfo begin_info = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - nullptr, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - nullptr, - }; - dfn.vkBeginCommandBuffer(cmd, &begin_info); - - auto front_buffer = - reinterpret_cast(swap_state.front_buffer_texture); - - status = CreateCaptureBuffer(cmd, {swap_state.width, swap_state.height}); - if (status != VK_SUCCESS) { - dfn.vkFreeCommandBuffers(*device_, command_pool_, 1, &cmd); - return nullptr; - } - - VkImageMemoryBarrier barrier; - std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = front_buffer; - barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - dfn.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, - nullptr, 1, &barrier); - - // Copy front buffer into capture image. - VkBufferImageCopy region = { - 0, 0, - 0, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, - {0, 0, 0}, {swap_state.width, swap_state.height, 1}, - }; - - dfn.vkCmdCopyImageToBuffer(cmd, front_buffer, VK_IMAGE_LAYOUT_GENERAL, - capture_buffer_, 1, ®ion); - - VkBufferMemoryBarrier memory_barrier = { - VK_STRUCTURE_TYPE_MEMORY_BARRIER, - nullptr, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - capture_buffer_, - 0, - VK_WHOLE_SIZE, - }; - dfn.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, - 1, &memory_barrier, 0, nullptr); - - status = dfn.vkEndCommandBuffer(cmd); - - // Submit commands and wait. - if (status == VK_SUCCESS) { - std::lock_guard lock(device_->primary_queue_mutex()); - VkSubmitInfo submit_info = { - VK_STRUCTURE_TYPE_SUBMIT_INFO, - nullptr, - 0, - nullptr, - nullptr, - 1, - &cmd, - 0, - nullptr, - }; - status = - dfn.vkQueueSubmit(device_->primary_queue(), 1, &submit_info, nullptr); - CheckResult(status, "vkQueueSubmit"); - - if (status == VK_SUCCESS) { - status = dfn.vkQueueWaitIdle(device_->primary_queue()); - CheckResult(status, "vkQueueWaitIdle"); - } - } - - dfn.vkFreeCommandBuffers(*device_, command_pool_, 1, &cmd); - - void* data; - if (status == VK_SUCCESS) { - status = dfn.vkMapMemory(*device_, capture_buffer_memory_, 0, VK_WHOLE_SIZE, - 0, &data); - CheckResult(status, "vkMapMemory"); - } - - if (status == VK_SUCCESS) { - std::unique_ptr raw_image(new RawImage()); - raw_image->width = swap_state.width; - raw_image->height = swap_state.height; - raw_image->stride = swap_state.width * 4; - raw_image->data.resize(raw_image->stride * raw_image->height); - - std::memcpy(raw_image->data.data(), data, - raw_image->stride * raw_image->height); - - dfn.vkUnmapMemory(*device_, capture_buffer_memory_); - DestroyCaptureBuffer(); - return raw_image; - } - - DestroyCaptureBuffer(); - return nullptr; -} - -VkResult VulkanGraphicsSystem::CreateCaptureBuffer(VkCommandBuffer cmd, - VkExtent2D extents) { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - VkResult status = VK_SUCCESS; - - VkBufferCreateInfo buffer_info = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - nullptr, - 0, - extents.width * extents.height * 4, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_SHARING_MODE_EXCLUSIVE, - 0, - nullptr, - }; - status = - dfn.vkCreateBuffer(*device_, &buffer_info, nullptr, &capture_buffer_); - if (status != VK_SUCCESS) { - return status; - } - - capture_buffer_size_ = extents.width * extents.height * 4; - - // Bind memory to buffer. - VkMemoryRequirements mem_requirements; - dfn.vkGetBufferMemoryRequirements(*device_, capture_buffer_, - &mem_requirements); - capture_buffer_memory_ = device_->AllocateMemory( - mem_requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - assert_not_null(capture_buffer_memory_); - - status = dfn.vkBindBufferMemory(*device_, capture_buffer_, - capture_buffer_memory_, 0); - CheckResult(status, "vkBindImageMemory"); - if (status != VK_SUCCESS) { - dfn.vkDestroyBuffer(*device_, capture_buffer_, nullptr); - return status; - } - - return status; -} - -void VulkanGraphicsSystem::DestroyCaptureBuffer() { - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - dfn.vkDestroyBuffer(*device_, capture_buffer_, nullptr); - dfn.vkFreeMemory(*device_, capture_buffer_memory_, nullptr); - capture_buffer_ = nullptr; - capture_buffer_memory_ = nullptr; - capture_buffer_size_ = 0; + ui::WindowedAppContext* app_context, + bool is_surface_required) { + provider_ = xe::ui::vulkan::VulkanProvider::Create(is_surface_required); + return GraphicsSystem::Setup(processor, kernel_state, app_context, + is_surface_required); } std::unique_ptr @@ -270,71 +35,6 @@ VulkanGraphicsSystem::CreateCommandProcessor() { return std::make_unique(this, kernel_state_); } -void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) { - if (!command_processor_ || !display_context_) { - return; - } - - // Check for pending swap. - auto& swap_state = command_processor_->swap_state(); - if (display_context_->WasLost()) { - // We're crashing. Cheese it. - swap_state.pending = false; - return; - } - - { - std::lock_guard lock(swap_state.mutex); - if (!swap_state.pending) { - // return; - } - - swap_state.pending = false; - } - - if (!swap_state.front_buffer_texture) { - // Not yet ready. - return; - } - - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - auto swap_chain = display_context_->swap_chain(); - auto copy_cmd_buffer = swap_chain->copy_cmd_buffer(); - auto front_buffer = - reinterpret_cast(swap_state.front_buffer_texture); - - VkImageMemoryBarrier barrier; - std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier)); - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = front_buffer; - barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - dfn.vkCmdPipelineBarrier(copy_cmd_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, - nullptr, 1, &barrier); - - VkImageBlit region; - region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - region.srcOffsets[0] = {0, 0, 0}; - region.srcOffsets[1] = {static_cast(swap_state.width), - static_cast(swap_state.height), 1}; - - region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - region.dstOffsets[0] = {0, 0, 0}; - region.dstOffsets[1] = {static_cast(swap_chain->surface_width()), - static_cast(swap_chain->surface_height()), - 1}; - dfn.vkCmdBlitImage(copy_cmd_buffer, front_buffer, VK_IMAGE_LAYOUT_GENERAL, - swap_chain->surface_image(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, - VK_FILTER_LINEAR); -} - } // namespace vulkan } // namespace gpu } // namespace xe diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.h b/src/xenia/gpu/vulkan/vulkan_graphics_system.h index 74a17aa78..2433703f2 100644 --- a/src/xenia/gpu/vulkan/vulkan_graphics_system.h +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -12,8 +12,8 @@ #include +#include "xenia/gpu/command_processor.h" #include "xenia/gpu/graphics_system.h" -#include "xenia/ui/vulkan/vulkan_context.h" namespace xe { namespace gpu { @@ -29,26 +29,11 @@ class VulkanGraphicsSystem : public GraphicsSystem { std::string name() const override { return "Vulkan - obsolete"; } X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, - ui::Window* target_window) override; - void Shutdown() override; - - std::unique_ptr Capture() override; + ui::WindowedAppContext* app_context, + bool is_surface_required) override; private: - VkResult CreateCaptureBuffer(VkCommandBuffer cmd, VkExtent2D extents); - void DestroyCaptureBuffer(); - std::unique_ptr CreateCommandProcessor() override; - void Swap(xe::ui::UIEvent* e) override; - - xe::ui::vulkan::VulkanDevice* device_ = nullptr; - xe::ui::vulkan::VulkanContext* display_context_ = nullptr; - - VkCommandPool command_pool_ = nullptr; - - VkBuffer capture_buffer_ = nullptr; - VkDeviceMemory capture_buffer_memory_ = nullptr; - VkDeviceSize capture_buffer_size_ = 0; }; } // namespace vulkan diff --git a/src/xenia/gpu/vulkan/vulkan_shader.cc b/src/xenia/gpu/vulkan/vulkan_shader.cc index f70be58eb..e23de068e 100644 --- a/src/xenia/gpu/vulkan/vulkan_shader.cc +++ b/src/xenia/gpu/vulkan/vulkan_shader.cc @@ -13,26 +13,27 @@ #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" -#include "xenia/ui/vulkan/vulkan_device.h" #include "xenia/ui/vulkan/vulkan_util.h" namespace xe { namespace gpu { namespace vulkan { -using xe::ui::vulkan::CheckResult; +using xe::ui::vulkan::util::CheckResult; -VulkanShader::VulkanShader(ui::vulkan::VulkanDevice* device, +VulkanShader::VulkanShader(const ui::vulkan::VulkanProvider& provider, xenos::ShaderType shader_type, uint64_t data_hash, const uint32_t* dword_ptr, uint32_t dword_count) - : Shader(shader_type, data_hash, dword_ptr, dword_count), device_(device) {} + : Shader(shader_type, data_hash, dword_ptr, dword_count), + provider_(provider) {} VulkanShader::VulkanTranslation::~VulkanTranslation() { if (shader_module_) { - const VulkanShader& vulkan_shader = static_cast(shader()); - const ui::vulkan::VulkanDevice* device = vulkan_shader.device_; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device->dfn(); - dfn.vkDestroyShaderModule(*device, shader_module_, nullptr); + const ui::vulkan::VulkanProvider& provider = + static_cast(shader()).provider_; + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); + dfn.vkDestroyShaderModule(device, shader_module_, nullptr); shader_module_ = nullptr; } } @@ -42,8 +43,9 @@ bool VulkanShader::VulkanTranslation::Prepare() { assert_true(is_valid()); const VulkanShader& vulkan_shader = static_cast(shader()); - const ui::vulkan::VulkanDevice* device = vulkan_shader.device_; - const ui::vulkan::VulkanDevice::DeviceFunctions& dfn = device->dfn(); + const ui::vulkan::VulkanProvider& provider = vulkan_shader.provider_; + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); // Create the shader module. VkShaderModuleCreateInfo shader_info; @@ -54,7 +56,7 @@ bool VulkanShader::VulkanTranslation::Prepare() { shader_info.pCode = reinterpret_cast(translated_binary().data()); auto status = - dfn.vkCreateShaderModule(*device, &shader_info, nullptr, &shader_module_); + dfn.vkCreateShaderModule(device, &shader_info, nullptr, &shader_module_); CheckResult(status, "vkCreateShaderModule"); char type_char; @@ -68,10 +70,10 @@ bool VulkanShader::VulkanTranslation::Prepare() { default: type_char = 'u'; } - device->DbgSetObjectName(uint64_t(shader_module_), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - fmt::format("S({}): {:016X}", type_char, - vulkan_shader.ucode_data_hash())); + provider.SetDeviceObjectName( + VK_OBJECT_TYPE_SHADER_MODULE, uint64_t(shader_module_), + fmt::format("S({}): {:016X}", type_char, vulkan_shader.ucode_data_hash()) + .c_str()); return status == VK_SUCCESS; } diff --git a/src/xenia/gpu/vulkan/vulkan_shader.h b/src/xenia/gpu/vulkan/vulkan_shader.h index 76a196bff..00e913923 100644 --- a/src/xenia/gpu/vulkan/vulkan_shader.h +++ b/src/xenia/gpu/vulkan/vulkan_shader.h @@ -13,7 +13,7 @@ #include #include "xenia/gpu/shader.h" -#include "xenia/ui/vulkan/vulkan_context.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace gpu { @@ -36,15 +36,15 @@ class VulkanShader : public Shader { VkShaderModule shader_module_ = nullptr; }; - VulkanShader(ui::vulkan::VulkanDevice* device, xenos::ShaderType shader_type, - uint64_t data_hash, const uint32_t* dword_ptr, - uint32_t dword_count); + VulkanShader(const ui::vulkan::VulkanProvider& provider, + xenos::ShaderType shader_type, uint64_t data_hash, + const uint32_t* dword_ptr, uint32_t dword_count); protected: Translation* CreateTranslationInstance(uint64_t modification) override; private: - ui::vulkan::VulkanDevice* device_ = nullptr; + const ui::vulkan::VulkanProvider& provider_; }; } // namespace vulkan diff --git a/src/xenia/gpu/vulkan/vulkan_trace_dump_main.cc b/src/xenia/gpu/vulkan/vulkan_trace_dump_main.cc index d1d78047c..4483129b8 100644 --- a/src/xenia/gpu/vulkan/vulkan_trace_dump_main.cc +++ b/src/xenia/gpu/vulkan/vulkan_trace_dump_main.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -12,7 +12,6 @@ #include "xenia/gpu/trace_dump.h" #include "xenia/gpu/vulkan/vulkan_command_processor.h" #include "xenia/gpu/vulkan/vulkan_graphics_system.h" -#include "xenia/ui/vulkan/vulkan_device.h" #include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { @@ -28,20 +27,24 @@ class VulkanTraceDump : public TraceDump { } void BeginHostCapture() override { - auto device = static_cast( - graphics_system_->provider()) - ->device(); - if (device->is_renderdoc_attached()) { - device->BeginRenderDocFrameCapture(); + const RENDERDOC_API_1_0_0* renderdoc_api = + static_cast( + graphics_system_->provider()) + ->renderdoc_api() + .api_1_0_0(); + if (renderdoc_api && !renderdoc_api->IsFrameCapturing()) { + renderdoc_api->StartFrameCapture(nullptr, nullptr); } } void EndHostCapture() override { - auto device = static_cast( - graphics_system_->provider()) - ->device(); - if (device->is_renderdoc_attached()) { - device->EndRenderDocFrameCapture(); + const RENDERDOC_API_1_0_0* renderdoc_api = + static_cast( + graphics_system_->provider()) + ->renderdoc_api() + .api_1_0_0(); + if (renderdoc_api && renderdoc_api->IsFrameCapturing()) { + renderdoc_api->EndFrameCapture(nullptr, nullptr); } } }; diff --git a/src/xenia/gpu/vulkan/vulkan_trace_viewer_main.cc b/src/xenia/gpu/vulkan/vulkan_trace_viewer_main.cc index ed296d4e0..e50abb41f 100644 --- a/src/xenia/gpu/vulkan/vulkan_trace_viewer_main.cc +++ b/src/xenia/gpu/vulkan/vulkan_trace_viewer_main.cc @@ -36,7 +36,7 @@ class VulkanTraceViewer final : public TraceViewer { uint32_t pitch, xenos::MsaaSamples samples, uint32_t base, xenos::ColorRenderTargetFormat format) override { auto command_processor = static_cast( - graphics_system_->command_processor()); + graphics_system()->command_processor()); // return command_processor->GetColorRenderTarget(pitch, samples, base, // format); return 0; @@ -46,7 +46,7 @@ class VulkanTraceViewer final : public TraceViewer { uint32_t pitch, xenos::MsaaSamples samples, uint32_t base, xenos::DepthRenderTargetFormat format) override { auto command_processor = static_cast( - graphics_system_->command_processor()); + graphics_system()->command_processor()); // return command_processor->GetDepthRenderTarget(pitch, samples, base, // format); return 0; @@ -55,7 +55,7 @@ class VulkanTraceViewer final : public TraceViewer { uintptr_t GetTextureEntry(const TextureInfo& texture_info, const SamplerInfo& sampler_info) override { auto command_processor = static_cast( - graphics_system_->command_processor()); + graphics_system()->command_processor()); // auto entry_view = // command_processor->texture_cache()->Demand(texture_info, diff --git a/src/xenia/hid/hid_demo.cc b/src/xenia/hid/hid_demo.cc index d02735a42..f4d616f9e 100644 --- a/src/xenia/hid/hid_demo.cc +++ b/src/xenia/hid/hid_demo.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -24,10 +24,14 @@ #include "xenia/base/threading.h" #include "xenia/hid/hid_flags.h" #include "xenia/hid/input_system.h" +#include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/presenter.h" #include "xenia/ui/virtual_key.h" #include "xenia/ui/vulkan/vulkan_provider.h" #include "xenia/ui/window.h" +#include "xenia/ui/window_listener.h" #include "xenia/ui/windowed_app.h" // Available input drivers: @@ -59,12 +63,41 @@ class HidDemoApp final : public ui::WindowedApp { bool OnInitialize() override; private: + enum : size_t { + kZOrderHidInput, + kZOrderImGui, + }; + + class HidDemoWindowListener final : public ui::WindowListener { + public: + explicit HidDemoWindowListener(ui::WindowedAppContext& app_context) + : app_context_(app_context) {} + void OnClosing(ui::UIEvent& e) override { app_context_.QuitFromUIThread(); } + + private: + ui::WindowedAppContext& app_context_; + }; + + class HidDemoDialog final : public ui::ImGuiDialog { + public: + explicit HidDemoDialog(ui::ImGuiDrawer* imgui_drawer, HidDemoApp& app) + : ui::ImGuiDialog(imgui_drawer), app_(app) {} + + protected: + void OnDraw(ImGuiIO& io) override; + + private: + HidDemoApp& app_; + }; + explicit HidDemoApp(ui::WindowedAppContext& app_context) - : ui::WindowedApp(app_context, "xenia-hid-demo") {} + : ui::WindowedApp(app_context, "xenia-hid-demo"), + window_listener_(app_context) {} static std::vector> CreateInputDrivers( ui::Window* window); + void Draw(ImGuiIO& io); void DrawUserInputGetState(uint32_t user_index) const; void DrawInputGetState() const; void DrawUserInputGetKeystroke(uint32_t user_index, bool poll, @@ -72,9 +105,15 @@ class HidDemoApp final : public ui::WindowedApp { void DrawInputGetKeystroke(bool poll, bool hide_repeats, bool clear_log) const; + HidDemoWindowListener window_listener_; std::unique_ptr graphics_provider_; std::unique_ptr window_; std::unique_ptr input_system_; + std::unique_ptr presenter_; + std::unique_ptr immediate_drawer_; + std::unique_ptr imgui_drawer_; + std::unique_ptr demo_dialog_; + bool is_active_ = true; }; @@ -82,71 +121,64 @@ std::vector> HidDemoApp::CreateInputDrivers( ui::Window* window) { std::vector> drivers; if (cvars::hid.compare("nop") == 0) { - drivers.emplace_back(xe::hid::nop::Create(window)); + drivers.emplace_back(xe::hid::nop::Create(window, kZOrderHidInput)); } else if (cvars::hid.compare("sdl") == 0) { - auto driver = xe::hid::sdl::Create(window); + auto driver = xe::hid::sdl::Create(window, kZOrderHidInput); if (XSUCCEEDED(driver->Setup())) { drivers.emplace_back(std::move(driver)); } #if XE_PLATFORM_WIN32 } else if (cvars::hid.compare("winkey") == 0) { - auto driver = xe::hid::winkey::Create(window); + auto driver = xe::hid::winkey::Create(window, kZOrderHidInput); if (XSUCCEEDED(driver->Setup())) { drivers.emplace_back(std::move(driver)); } } else if (cvars::hid.compare("xinput") == 0) { - auto driver = xe::hid::xinput::Create(window); + auto driver = xe::hid::xinput::Create(window, kZOrderHidInput); if (XSUCCEEDED(driver->Setup())) { drivers.emplace_back(std::move(driver)); } #endif // XE_PLATFORM_WIN32 } else { - auto sdl_driver = xe::hid::sdl::Create(window); + auto sdl_driver = xe::hid::sdl::Create(window, kZOrderHidInput); if (sdl_driver && XSUCCEEDED(sdl_driver->Setup())) { drivers.emplace_back(std::move(sdl_driver)); } #if XE_PLATFORM_WIN32 - auto xinput_driver = xe::hid::xinput::Create(window); + auto xinput_driver = xe::hid::xinput::Create(window, kZOrderHidInput); if (xinput_driver && XSUCCEEDED(xinput_driver->Setup())) { drivers.emplace_back(std::move(xinput_driver)); } - auto winkey_driver = xe::hid::winkey::Create(window); + auto winkey_driver = xe::hid::winkey::Create(window, kZOrderHidInput); if (winkey_driver && XSUCCEEDED(winkey_driver->Setup())) { drivers.emplace_back(std::move(winkey_driver)); } #endif // XE_PLATFORM_WIN32 if (drivers.empty()) { // Fallback to nop if none created. - drivers.emplace_back(xe::hid::nop::Create(window)); + drivers.emplace_back(xe::hid::nop::Create(window, kZOrderHidInput)); } } return drivers; } bool HidDemoApp::OnInitialize() { - // Create graphics provider that provides the context for the window. - graphics_provider_ = xe::ui::vulkan::VulkanProvider::Create(); + // Create the graphics provider that provides the presenter for the window. + graphics_provider_ = xe::ui::vulkan::VulkanProvider::Create(true); if (!graphics_provider_) { + XELOGE("Failed to initialize the graphics provider"); return false; } - // Create the window. - window_ = xe::ui::Window::Create(app_context(), GetName()); - if (!window_->Initialize()) { - XELOGE("Failed to initialize main window"); + // Create and configure the window. + window_ = xe::ui::Window::Create(app_context(), GetName(), + COL_WIDTH_STATE + COL_WIDTH_STROKE, + ROW_HEIGHT_GENERAL + 500); + window_->AddListener(&window_listener_); + if (!window_->Open()) { + XELOGE("Failed to open the main window"); return false; } - window_->on_closed.AddListener([this](xe::ui::UIEvent* e) { - XELOGI("User-initiated death!"); - app_context().QuitFromUIThread(); - }); - - // Initial size setting, done here so that it knows the menu exists. - window_->Resize(COL_WIDTH_STATE + COL_WIDTH_STROKE, ROW_HEIGHT_GENERAL + 500); - - // Create the graphics context for the window. The window will finish - // initialization with the context (loading resources, etc). - window_->set_context(graphics_provider_->CreateContext(window_.get())); // Initialize input system and all drivers. input_system_ = std::make_unique(window_.get()); @@ -157,71 +189,83 @@ bool HidDemoApp::OnInitialize() { input_system_->AddDriver(std::move(driver)); } - window_->Invalidate(); - - window_->set_imgui_input_enabled(true); - - window_->on_painting.AddListener([this](xe::ui::UIEvent* e) { - auto& io = window_->imgui_drawer()->GetIO(); - - const ImGuiWindowFlags wflags = - ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_NoScrollbar; - - ImGui::Begin("General", nullptr, wflags); - { - ImGui::SetWindowPos(ImVec2(0, 0)); - ImGui::SetWindowSize( - ImVec2(COL_WIDTH_STATE + COL_WIDTH_STROKE, ROW_HEIGHT_GENERAL)); - - ImGui::Text("Input System (hid) = \"%s\"", cvars::hid.c_str()); - ImGui::Checkbox("is_active", &is_active_); - } - ImGui::End(); - - ImGui::Begin("GetState()", nullptr, wflags); - { - ImGui::SetWindowPos(ImVec2(0, ROW_HEIGHT_GENERAL)); - ImGui::SetWindowSize( - ImVec2(COL_WIDTH_STATE, io.DisplaySize.y - ROW_HEIGHT_GENERAL)); - - static bool enable_GetState = false; - ImGui::Checkbox("Active", &enable_GetState); - ImGui::SameLine(); - ImGui::Checkbox("Guide Button", &cvars::guide_button); - if (enable_GetState) { - ImGui::Spacing(); - DrawInputGetState(); - } - } - ImGui::End(); - - ImGui::Begin("GetKeystroke()", nullptr, wflags); - { - ImGui::SetWindowPos(ImVec2(COL_WIDTH_STATE, ROW_HEIGHT_GENERAL)); - ImGui::SetWindowSize( - ImVec2(COL_WIDTH_STROKE, io.DisplaySize.y - ROW_HEIGHT_GENERAL)); - - static bool enable_GetKeystroke = false; - static bool hide_repeats = false; - ImGui::Checkbox("Active", &enable_GetKeystroke); - ImGui::SameLine(); - ImGui::Checkbox("Hide repeats", &hide_repeats); - ImGui::SameLine(); - const bool clear_log = ImGui::Button("Clear"); - ImGui::Spacing(); - DrawInputGetKeystroke(enable_GetKeystroke, hide_repeats, clear_log); - } - ImGui::End(); - - // Continuous paint. - window_->Invalidate(); - }); + // Setup drawing to the window. + presenter_ = graphics_provider_->CreatePresenter(); + if (!presenter_) { + XELOGE("Failed to initialize the presenter"); + return false; + } + immediate_drawer_ = graphics_provider_->CreateImmediateDrawer(); + if (!immediate_drawer_) { + XELOGE("Failed to initialize the immediate drawer"); + return false; + } + immediate_drawer_->SetPresenter(presenter_.get()); + imgui_drawer_ = + std::make_unique(window_.get(), kZOrderImGui); + imgui_drawer_->SetPresenterAndImmediateDrawer(presenter_.get(), + immediate_drawer_.get()); + demo_dialog_ = std::make_unique(imgui_drawer_.get(), *this); + window_->SetPresenter(presenter_.get()); return true; } +void HidDemoApp::HidDemoDialog::OnDraw(ImGuiIO& io) { app_.Draw(io); } + +void HidDemoApp::Draw(ImGuiIO& io) { + const ImGuiWindowFlags wflags = + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoScrollbar; + + ImGui::Begin("General", nullptr, wflags); + { + ImGui::SetWindowPos(ImVec2(0, 0)); + ImGui::SetWindowSize( + ImVec2(COL_WIDTH_STATE + COL_WIDTH_STROKE, ROW_HEIGHT_GENERAL)); + + ImGui::Text("Input System (hid) = \"%s\"", cvars::hid.c_str()); + ImGui::Checkbox("is_active", &is_active_); + } + ImGui::End(); + + ImGui::Begin("GetState()", nullptr, wflags); + { + ImGui::SetWindowPos(ImVec2(0, ROW_HEIGHT_GENERAL)); + ImGui::SetWindowSize( + ImVec2(COL_WIDTH_STATE, io.DisplaySize.y - ROW_HEIGHT_GENERAL)); + + static bool enable_GetState = false; + ImGui::Checkbox("Active", &enable_GetState); + ImGui::SameLine(); + ImGui::Checkbox("Guide Button", &cvars::guide_button); + if (enable_GetState) { + ImGui::Spacing(); + DrawInputGetState(); + } + } + ImGui::End(); + + ImGui::Begin("GetKeystroke()", nullptr, wflags); + { + ImGui::SetWindowPos(ImVec2(COL_WIDTH_STATE, ROW_HEIGHT_GENERAL)); + ImGui::SetWindowSize( + ImVec2(COL_WIDTH_STROKE, io.DisplaySize.y - ROW_HEIGHT_GENERAL)); + + static bool enable_GetKeystroke = false; + static bool hide_repeats = false; + ImGui::Checkbox("Active", &enable_GetKeystroke); + ImGui::SameLine(); + ImGui::Checkbox("Hide repeats", &hide_repeats); + ImGui::SameLine(); + const bool clear_log = ImGui::Button("Clear"); + ImGui::Spacing(); + DrawInputGetKeystroke(enable_GetKeystroke, hide_repeats, clear_log); + } + ImGui::End(); +} + void HidDemoApp::DrawUserInputGetState(uint32_t user_index) const { ImGui::Text("User %u:", user_index); diff --git a/src/xenia/hid/input_driver.cc b/src/xenia/hid/input_driver.cc deleted file mode 100644 index 1627d5dc4..000000000 --- a/src/xenia/hid/input_driver.cc +++ /dev/null @@ -1,20 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/hid/input_driver.h" - -namespace xe { -namespace hid { - -InputDriver::InputDriver(xe::ui::Window* window) : window_(window) {} - -InputDriver::~InputDriver() = default; - -} // namespace hid -} // namespace xe diff --git a/src/xenia/hid/input_driver.h b/src/xenia/hid/input_driver.h index aef8cffeb..6b0ff3472 100644 --- a/src/xenia/hid/input_driver.h +++ b/src/xenia/hid/input_driver.h @@ -10,6 +10,7 @@ #ifndef XENIA_HID_INPUT_DRIVER_H_ #define XENIA_HID_INPUT_DRIVER_H_ +#include #include #include "xenia/hid/input.h" @@ -29,7 +30,7 @@ class InputSystem; class InputDriver { public: - virtual ~InputDriver(); + virtual ~InputDriver() = default; virtual X_STATUS Setup() = 0; @@ -45,18 +46,21 @@ class InputDriver { is_active_callback_ = is_active_callback; } - private: - xe::ui::Window* window_ = nullptr; - std::function is_active_callback_ = nullptr; - protected: - explicit InputDriver(xe::ui::Window* window); + explicit InputDriver(xe::ui::Window* window, size_t window_z_order) + : window_(window), window_z_order_(window_z_order) {} xe::ui::Window* window() const { return window_; } + size_t window_z_order() const { return window_z_order_; } bool is_active() const { return !is_active_callback_ || is_active_callback_(); } + + private: + xe::ui::Window* window_; + size_t window_z_order_; + std::function is_active_callback_ = nullptr; }; } // namespace hid diff --git a/src/xenia/hid/nop/nop_hid.cc b/src/xenia/hid/nop/nop_hid.cc index 54765df5f..4d198b278 100644 --- a/src/xenia/hid/nop/nop_hid.cc +++ b/src/xenia/hid/nop/nop_hid.cc @@ -15,8 +15,9 @@ namespace xe { namespace hid { namespace nop { -std::unique_ptr Create(xe::ui::Window* window) { - return std::make_unique(window); +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order) { + return std::make_unique(window, window_z_order); } } // namespace nop diff --git a/src/xenia/hid/nop/nop_hid.h b/src/xenia/hid/nop/nop_hid.h index c2ea0a037..6d8f36016 100644 --- a/src/xenia/hid/nop/nop_hid.h +++ b/src/xenia/hid/nop/nop_hid.h @@ -18,7 +18,8 @@ namespace xe { namespace hid { namespace nop { -std::unique_ptr Create(xe::ui::Window* window); +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order); } // namespace nop } // namespace hid diff --git a/src/xenia/hid/nop/nop_input_driver.cc b/src/xenia/hid/nop/nop_input_driver.cc index bd22d65e2..da6897459 100644 --- a/src/xenia/hid/nop/nop_input_driver.cc +++ b/src/xenia/hid/nop/nop_input_driver.cc @@ -15,7 +15,8 @@ namespace xe { namespace hid { namespace nop { -NopInputDriver::NopInputDriver(xe::ui::Window* window) : InputDriver(window) {} +NopInputDriver::NopInputDriver(xe::ui::Window* window, size_t window_z_order) + : InputDriver(window, window_z_order) {} NopInputDriver::~NopInputDriver() = default; diff --git a/src/xenia/hid/nop/nop_input_driver.h b/src/xenia/hid/nop/nop_input_driver.h index be949f016..361b63a7f 100644 --- a/src/xenia/hid/nop/nop_input_driver.h +++ b/src/xenia/hid/nop/nop_input_driver.h @@ -16,9 +16,9 @@ namespace xe { namespace hid { namespace nop { -class NopInputDriver : public InputDriver { +class NopInputDriver final : public InputDriver { public: - explicit NopInputDriver(xe::ui::Window* window); + explicit NopInputDriver(xe::ui::Window* window, size_t window_z_order); ~NopInputDriver() override; X_STATUS Setup() override; diff --git a/src/xenia/hid/premake5.lua b/src/xenia/hid/premake5.lua index 1aeef5657..eaaa792e4 100644 --- a/src/xenia/hid/premake5.lua +++ b/src/xenia/hid/premake5.lua @@ -41,7 +41,6 @@ project("xenia-hid-demo") filter("platforms:Linux") links({ "SDL2", - "vulkan", "X11", "xcb", "X11-xcb", diff --git a/src/xenia/hid/sdl/sdl_hid.cc b/src/xenia/hid/sdl/sdl_hid.cc index 336423ae2..cc20274b9 100644 --- a/src/xenia/hid/sdl/sdl_hid.cc +++ b/src/xenia/hid/sdl/sdl_hid.cc @@ -15,8 +15,9 @@ namespace xe { namespace hid { namespace sdl { -std::unique_ptr Create(xe::ui::Window* window) { - return std::make_unique(window); +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order) { + return std::make_unique(window, window_z_order); } } // namespace sdl diff --git a/src/xenia/hid/sdl/sdl_hid.h b/src/xenia/hid/sdl/sdl_hid.h index ba02f0d6c..7bedd8c03 100644 --- a/src/xenia/hid/sdl/sdl_hid.h +++ b/src/xenia/hid/sdl/sdl_hid.h @@ -18,7 +18,8 @@ namespace xe { namespace hid { namespace sdl { -std::unique_ptr Create(xe::ui::Window* window); +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order); } // namespace sdl } // namespace hid diff --git a/src/xenia/hid/sdl/sdl_input_driver.cc b/src/xenia/hid/sdl/sdl_input_driver.cc index 6b348f285..044c7dcd0 100644 --- a/src/xenia/hid/sdl/sdl_input_driver.cc +++ b/src/xenia/hid/sdl/sdl_input_driver.cc @@ -33,8 +33,8 @@ namespace xe { namespace hid { namespace sdl { -SDLInputDriver::SDLInputDriver(xe::ui::Window* window) - : InputDriver(window), +SDLInputDriver::SDLInputDriver(xe::ui::Window* window, size_t window_z_order) + : InputDriver(window, window_z_order), sdl_events_initialized_(false), sdl_gamecontroller_initialized_(false), sdl_events_unflushed_(0), diff --git a/src/xenia/hid/sdl/sdl_input_driver.h b/src/xenia/hid/sdl/sdl_input_driver.h index 367206cdb..de5c48e7c 100644 --- a/src/xenia/hid/sdl/sdl_input_driver.h +++ b/src/xenia/hid/sdl/sdl_input_driver.h @@ -28,9 +28,9 @@ namespace xe { namespace hid { namespace sdl { -class SDLInputDriver : public InputDriver { +class SDLInputDriver final : public InputDriver { public: - explicit SDLInputDriver(xe::ui::Window* window); + explicit SDLInputDriver(xe::ui::Window* window, size_t window_z_order); ~SDLInputDriver() override; X_STATUS Setup() override; @@ -42,7 +42,7 @@ class SDLInputDriver : public InputDriver { X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) override; - protected: + private: struct ControllerState { SDL_GameController* sdl; X_INPUT_CAPABILITIES caps; @@ -65,7 +65,6 @@ class SDLInputDriver : public InputDriver { uint32_t repeat_time; }; - protected: void HandleEvent(const SDL_Event& event); void OnControllerDeviceAdded(const SDL_Event& event); void OnControllerDeviceRemoved(const SDL_Event& event); @@ -80,7 +79,6 @@ class SDLInputDriver : public InputDriver { void UpdateXCapabilities(ControllerState& state); void QueueControllerUpdate(); - protected: bool sdl_events_initialized_; bool sdl_gamecontroller_initialized_; int sdl_events_unflushed_; diff --git a/src/xenia/hid/winkey/winkey_hid.cc b/src/xenia/hid/winkey/winkey_hid.cc index 7244f4c53..2f75ef5c4 100644 --- a/src/xenia/hid/winkey/winkey_hid.cc +++ b/src/xenia/hid/winkey/winkey_hid.cc @@ -15,8 +15,9 @@ namespace xe { namespace hid { namespace winkey { -std::unique_ptr Create(xe::ui::Window* window) { - return std::make_unique(window); +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order) { + return std::make_unique(window, window_z_order); } } // namespace winkey diff --git a/src/xenia/hid/winkey/winkey_hid.h b/src/xenia/hid/winkey/winkey_hid.h index a17e5ba98..6061c6a06 100644 --- a/src/xenia/hid/winkey/winkey_hid.h +++ b/src/xenia/hid/winkey/winkey_hid.h @@ -18,7 +18,8 @@ namespace xe { namespace hid { namespace winkey { -std::unique_ptr Create(xe::ui::Window* window); +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order); } // namespace winkey } // namespace hid diff --git a/src/xenia/hid/winkey/winkey_input_driver.cc b/src/xenia/hid/winkey/winkey_input_driver.cc index c038e69db..53c5ef359 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.cc +++ b/src/xenia/hid/winkey/winkey_input_driver.cc @@ -80,47 +80,22 @@ void WinKeyInputDriver::ParseKeyBinding(ui::VirtualKey output_key, } } -WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window) - : InputDriver(window), packet_number_(1) { - // Register a key listener. - window->on_key_down.AddListener([this](ui::KeyEvent* evt) { - if (!is_active()) { - return; - } - - auto global_lock = global_critical_region_.Acquire(); - - KeyEvent key; - key.virtual_key = evt->virtual_key(); - key.transition = true; - key.prev_state = evt->prev_state(); - key.repeat_count = evt->repeat_count(); - key_events_.push(key); - }); - window->on_key_up.AddListener([this](ui::KeyEvent* evt) { - if (!is_active()) { - return; - } - - auto global_lock = global_critical_region_.Acquire(); - - KeyEvent key; - key.virtual_key = evt->virtual_key(); - key.transition = false; - key.prev_state = evt->prev_state(); - key.repeat_count = evt->repeat_count(); - key_events_.push(key); - }); - +WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window, + size_t window_z_order) + : InputDriver(window, window_z_order), window_input_listener_(*this) { #define XE_HID_WINKEY_BINDING(button, description, cvar_name, \ cvar_default_value) \ ParseKeyBinding(xe::ui::VirtualKey::kXInputPad##button, description, \ cvars::cvar_name); #include "winkey_binding_table.inc" #undef XE_HID_WINKEY_BINDING + + window->AddInputListener(&window_input_listener_, window_z_order); } -WinKeyInputDriver::~WinKeyInputDriver() = default; +WinKeyInputDriver::~WinKeyInputDriver() { + window()->RemoveInputListener(&window_input_listener_); +} X_STATUS WinKeyInputDriver::Setup() { return X_STATUS_SUCCESS; } @@ -162,7 +137,7 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index, int16_t thumb_rx = 0; int16_t thumb_ry = 0; - if (window()->has_focus() && is_active()) { + if (window()->HasFocus() && is_active()) { bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT); for (const KeyBinding& b : key_bindings_) { if (((b.lowercase == b.uppercase) || (b.lowercase && !capital) || @@ -331,6 +306,29 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, return result; } +void WinKeyInputDriver::WinKeyWindowInputListener::OnKeyDown(ui::KeyEvent& e) { + driver_.OnKey(e, true); +} + +void WinKeyInputDriver::WinKeyWindowInputListener::OnKeyUp(ui::KeyEvent& e) { + driver_.OnKey(e, false); +} + +void WinKeyInputDriver::OnKey(ui::KeyEvent& e, bool is_down) { + if (!is_active()) { + return; + } + + KeyEvent key; + key.virtual_key = e.virtual_key(); + key.transition = is_down; + key.prev_state = e.prev_state(); + key.repeat_count = e.repeat_count(); + + auto global_lock = global_critical_region_.Acquire(); + key_events_.push(key); +} + } // namespace winkey } // namespace hid } // namespace xe diff --git a/src/xenia/hid/winkey/winkey_input_driver.h b/src/xenia/hid/winkey/winkey_input_driver.h index 8b45c1457..e11788acf 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.h +++ b/src/xenia/hid/winkey/winkey_input_driver.h @@ -20,9 +20,9 @@ namespace xe { namespace hid { namespace winkey { -class WinKeyInputDriver : public InputDriver { +class WinKeyInputDriver final : public InputDriver { public: - explicit WinKeyInputDriver(xe::ui::Window* window); + explicit WinKeyInputDriver(xe::ui::Window* window, size_t window_z_order); ~WinKeyInputDriver() override; X_STATUS Setup() override; @@ -49,15 +49,31 @@ class WinKeyInputDriver : public InputDriver { bool lowercase = false; }; - xe::global_critical_region global_critical_region_; - std::queue key_events_; - std::vector key_bindings_; + class WinKeyWindowInputListener final : public ui::WindowInputListener { + public: + explicit WinKeyWindowInputListener(WinKeyInputDriver& driver) + : driver_(driver) {} - uint32_t packet_number_; + void OnKeyDown(ui::KeyEvent& e) override; + void OnKeyUp(ui::KeyEvent& e) override; + + private: + WinKeyInputDriver& driver_; + }; void ParseKeyBinding(ui::VirtualKey virtual_key, const std::string_view description, const std::string_view binding); + + void OnKey(ui::KeyEvent& e, bool is_down); + + WinKeyWindowInputListener window_input_listener_; + + xe::global_critical_region global_critical_region_; + std::queue key_events_; + std::vector key_bindings_; + + uint32_t packet_number_ = 1; }; } // namespace winkey diff --git a/src/xenia/hid/xinput/xinput_hid.cc b/src/xenia/hid/xinput/xinput_hid.cc index 093d266f0..21f4304f1 100644 --- a/src/xenia/hid/xinput/xinput_hid.cc +++ b/src/xenia/hid/xinput/xinput_hid.cc @@ -15,8 +15,9 @@ namespace xe { namespace hid { namespace xinput { -std::unique_ptr Create(xe::ui::Window* window) { - return std::make_unique(window); +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order) { + return std::make_unique(window, window_z_order); } } // namespace xinput diff --git a/src/xenia/hid/xinput/xinput_hid.h b/src/xenia/hid/xinput/xinput_hid.h index c295cb434..2c6cb231b 100644 --- a/src/xenia/hid/xinput/xinput_hid.h +++ b/src/xenia/hid/xinput/xinput_hid.h @@ -18,7 +18,8 @@ namespace xe { namespace hid { namespace xinput { -std::unique_ptr Create(xe::ui::Window* window); +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order); } // namespace xinput } // namespace hid diff --git a/src/xenia/hid/xinput/xinput_input_driver.cc b/src/xenia/hid/xinput/xinput_input_driver.cc index 830daa0e4..497d80089 100644 --- a/src/xenia/hid/xinput/xinput_input_driver.cc +++ b/src/xenia/hid/xinput/xinput_input_driver.cc @@ -21,8 +21,9 @@ namespace xe { namespace hid { namespace xinput { -XInputInputDriver::XInputInputDriver(xe::ui::Window* window) - : InputDriver(window), +XInputInputDriver::XInputInputDriver(xe::ui::Window* window, + size_t window_z_order) + : InputDriver(window, window_z_order), module_(nullptr), XInputGetCapabilities_(nullptr), XInputGetState_(nullptr), diff --git a/src/xenia/hid/xinput/xinput_input_driver.h b/src/xenia/hid/xinput/xinput_input_driver.h index dcde26197..b14113963 100644 --- a/src/xenia/hid/xinput/xinput_input_driver.h +++ b/src/xenia/hid/xinput/xinput_input_driver.h @@ -16,9 +16,9 @@ namespace xe { namespace hid { namespace xinput { -class XInputInputDriver : public InputDriver { +class XInputInputDriver final : public InputDriver { public: - explicit XInputInputDriver(xe::ui::Window* window); + explicit XInputInputDriver(xe::ui::Window* window, size_t window_z_order); ~XInputInputDriver() override; X_STATUS Setup() override; @@ -30,7 +30,7 @@ class XInputInputDriver : public InputDriver { X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) override; - protected: + private: void* module_; void* XInputGetCapabilities_; void* XInputGetState_; diff --git a/src/xenia/kernel/util/shim_utils.h b/src/xenia/kernel/util/shim_utils.h index 3233ec67e..0b6551615 100644 --- a/src/xenia/kernel/util/shim_utils.h +++ b/src/xenia/kernel/util/shim_utils.h @@ -458,7 +458,7 @@ inline void AppendParam(StringBuffer* string_buffer, inline void AppendParam(StringBuffer* string_buffer, pointer_t record) { string_buffer->AppendFormat("{:08X}({:08X})", record.guest_address(), - uint32_t(record->exception_code)); + uint32_t(record->code)); } template void AppendParam(StringBuffer* string_buffer, pointer_t param) { diff --git a/src/xenia/kernel/xam/xam_nui.cc b/src/xenia/kernel/xam/xam_nui.cc index 48ab0d946..53fe06e6f 100644 --- a/src/xenia/kernel/xam/xam_nui.cc +++ b/src/xenia/kernel/xam/xam_nui.cc @@ -14,6 +14,7 @@ #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_private.h" #include "xenia/ui/imgui_dialog.h" +#include "xenia/ui/imgui_drawer.h" #include "xenia/ui/window.h" #include "xenia/ui/windowed_app_context.h" #include "xenia/xbox.h" @@ -49,17 +50,21 @@ dword_result_t XamShowNuiTroubleshooterUI_entry(unknown_t unk1, unknown_t unk2, return 0; } - auto display_window = kernel_state()->emulator()->display_window(); - xe::threading::Fence fence; - if (display_window->app_context().CallInUIThreadSynchronous([&]() { - xe::ui::ImGuiDialog::ShowMessageBox( - display_window, "NUI Troubleshooter", - "The game has indicated there is a problem with NUI (Kinect).") - ->Then(&fence); - })) { - ++xam_dialogs_shown_; - fence.Wait(); - --xam_dialogs_shown_; + const Emulator* emulator = kernel_state()->emulator(); + ui::Window* display_window = emulator->display_window(); + ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer(); + if (display_window && imgui_drawer) { + xe::threading::Fence fence; + if (display_window->app_context().CallInUIThreadSynchronous([&]() { + xe::ui::ImGuiDialog::ShowMessageBox( + imgui_drawer, "NUI Troubleshooter", + "The game has indicated there is a problem with NUI (Kinect).") + ->Then(&fence); + })) { + ++xam_dialogs_shown_; + fence.Wait(); + --xam_dialogs_shown_; + } } return 0; diff --git a/src/xenia/kernel/xam/xam_ui.cc b/src/xenia/kernel/xam/xam_ui.cc index 2762facf3..be8e2c892 100644 --- a/src/xenia/kernel/xam/xam_ui.cc +++ b/src/xenia/kernel/xam/xam_ui.cc @@ -16,6 +16,7 @@ #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_private.h" #include "xenia/ui/imgui_dialog.h" +#include "xenia/ui/imgui_drawer.h" #include "xenia/ui/window.h" #include "xenia/ui/windowed_app_context.h" #include "xenia/xbox.h" @@ -51,7 +52,8 @@ class XamDialog : public xe::ui::ImGuiDialog { } protected: - XamDialog(xe::ui::Window* window) : xe::ui::ImGuiDialog(window) {} + XamDialog(xe::ui::ImGuiDrawer* imgui_drawer) + : xe::ui::ImGuiDialog(imgui_drawer) {} void OnClose() override { if (close_callback_) { @@ -206,10 +208,10 @@ DECLARE_XAM_EXPORT2(XamIsUIActive, kUI, kImplemented, kHighFrequency); class MessageBoxDialog : public XamDialog { public: - MessageBoxDialog(xe::ui::Window* window, std::string title, + MessageBoxDialog(xe::ui::ImGuiDrawer* imgui_drawer, std::string title, std::string description, std::vector buttons, uint32_t default_button) - : XamDialog(window), + : XamDialog(imgui_drawer), title_(title), description_(description), buttons_(std::move(buttons)), @@ -310,11 +312,11 @@ dword_result_t XamShowMessageBoxUI_entry( *result_ptr = dialog->chosen_button(); return X_ERROR_SUCCESS; }; - auto display_window = kernel_state()->emulator()->display_window(); + const Emulator* emulator = kernel_state()->emulator(); + ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer(); result = xeXamDispatchDialog( - new MessageBoxDialog(display_window, title, - xe::to_utf8(text_ptr.value()), buttons, - active_button), + new MessageBoxDialog(imgui_drawer, title, xe::to_utf8(text_ptr.value()), + buttons, active_button), close, overlapped); } return result; @@ -323,10 +325,10 @@ DECLARE_XAM_EXPORT1(XamShowMessageBoxUI, kUI, kImplemented); class KeyboardInputDialog : public XamDialog { public: - KeyboardInputDialog(xe::ui::Window* window, std::string title, + KeyboardInputDialog(xe::ui::ImGuiDrawer* imgui_drawer, std::string title, std::string description, std::string default_text, size_t max_length) - : XamDialog(window), + : XamDialog(imgui_drawer), title_(title), description_(description), default_text_(default_text), @@ -446,10 +448,11 @@ dword_result_t XamShowKeyboardUI_entry( return X_ERROR_SUCCESS; } }; - auto display_window = kernel_state()->emulator()->display_window(); + const Emulator* emulator = kernel_state()->emulator(); + ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer(); result = xeXamDispatchDialogEx( new KeyboardInputDialog( - display_window, title ? xe::to_utf8(title.value()) : "", + imgui_drawer, title ? xe::to_utf8(title.value()) : "", description ? xe::to_utf8(description.value()) : "", default_text ? xe::to_utf8(default_text.value()) : "", buffer_length), @@ -479,10 +482,11 @@ void XamShowDirtyDiscErrorUI_entry(dword_t user_index) { exit(1); return; } - auto display_window = kernel_state()->emulator()->display_window(); + const Emulator* emulator = kernel_state()->emulator(); + ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer(); xeXamDispatchDialog( new MessageBoxDialog( - display_window, "Disc Read Error", + imgui_drawer, "Disc Read Error", "There's been an issue reading content from the game disc.\nThis is " "likely caused by bad or unimplemented file IO calls.", {"OK"}, 0), diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc index 4470cd36e..0bc8a8f8d 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_debug.cc @@ -124,7 +124,7 @@ void HandleCppException(pointer_t record) { } void RtlRaiseException_entry(pointer_t record) { - switch (record->exception_code) { + switch (record->code) { case 0x406D1388: { HandleSetThreadName(record); return; diff --git a/src/xenia/ui/d3d12/d3d12_api.h b/src/xenia/ui/d3d12/d3d12_api.h index e474d9391..82bc44bff 100644 --- a/src/xenia/ui/d3d12/d3d12_api.h +++ b/src/xenia/ui/d3d12/d3d12_api.h @@ -10,15 +10,14 @@ #ifndef XENIA_UI_D3D12_D3D12_API_H_ #define XENIA_UI_D3D12_D3D12_API_H_ -// This must be included before D3D and DXGI for things like NOMINMAX. +// Must be included before D3D and DXGI for things like NOMINMAX. #include "xenia/base/platform_win.h" #include #include #include #include -#include -#include +#include #include // For Microsoft::WRL::ComPtr. #include diff --git a/src/xenia/ui/d3d12/d3d12_context.cc b/src/xenia/ui/d3d12/d3d12_context.cc deleted file mode 100644 index a37c61b72..000000000 --- a/src/xenia/ui/d3d12/d3d12_context.cc +++ /dev/null @@ -1,379 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2018 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/d3d12/d3d12_context.h" - -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/ui/d3d12/d3d12_immediate_drawer.h" -#include "xenia/ui/d3d12/d3d12_provider.h" -#include "xenia/ui/d3d12/d3d12_util.h" -#include "xenia/ui/window.h" - -namespace xe { -namespace ui { -namespace d3d12 { - -D3D12Context::D3D12Context(D3D12Provider* provider, Window* target_window) - : GraphicsContext(provider, target_window) {} - -D3D12Context::~D3D12Context() { Shutdown(); } - -bool D3D12Context::Initialize() { - context_lost_ = false; - - if (!target_window_) { - return true; - } - - const D3D12Provider& provider = GetD3D12Provider(); - IDXGIFactory2* dxgi_factory = provider.GetDXGIFactory(); - ID3D12Device* device = provider.GetDevice(); - ID3D12CommandQueue* direct_queue = provider.GetDirectQueue(); - - swap_fence_current_value_ = 1; - swap_fence_completed_value_ = 0; - swap_fence_completion_event_ = CreateEvent(nullptr, false, false, nullptr); - if (swap_fence_completion_event_ == nullptr) { - XELOGE("Failed to create the composition fence completion event"); - Shutdown(); - return false; - } - // Create a fence for transient resources of compositing. - if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, - IID_PPV_ARGS(&swap_fence_)))) { - XELOGE("Failed to create the composition fence"); - Shutdown(); - return false; - } - - // Create the swap chain. - swap_chain_width_ = target_window_->scaled_width(); - swap_chain_height_ = target_window_->scaled_height(); - DXGI_SWAP_CHAIN_DESC1 swap_chain_desc; - swap_chain_desc.Width = swap_chain_width_; - swap_chain_desc.Height = swap_chain_height_; - swap_chain_desc.Format = kSwapChainFormat; - swap_chain_desc.Stereo = FALSE; - swap_chain_desc.SampleDesc.Count = 1; - swap_chain_desc.SampleDesc.Quality = 0; - swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swap_chain_desc.BufferCount = kSwapChainBufferCount; - swap_chain_desc.Scaling = DXGI_SCALING_STRETCH; - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - swap_chain_desc.Flags = 0; - IDXGISwapChain1* swap_chain_1; - if (FAILED(dxgi_factory->CreateSwapChainForComposition( - provider.GetDirectQueue(), &swap_chain_desc, nullptr, - &swap_chain_1))) { - XELOGE("Failed to create a DXGI swap chain for composition"); - Shutdown(); - return false; - } - if (FAILED(swap_chain_1->QueryInterface(IID_PPV_ARGS(&swap_chain_)))) { - XELOGE("Failed to get version 3 of the DXGI swap chain interface"); - swap_chain_1->Release(); - Shutdown(); - return false; - } - swap_chain_1->Release(); - - // Create a heap for RTV descriptors of swap chain buffers. - D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc; - rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - rtv_heap_desc.NumDescriptors = kSwapChainBufferCount; - rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - rtv_heap_desc.NodeMask = 0; - if (FAILED(device->CreateDescriptorHeap( - &rtv_heap_desc, IID_PPV_ARGS(&swap_chain_rtv_heap_)))) { - XELOGE("Failed to create swap chain RTV descriptor heap"); - Shutdown(); - return false; - } - swap_chain_rtv_heap_start_ = - swap_chain_rtv_heap_->GetCPUDescriptorHandleForHeapStart(); - - // Get the buffers and create their RTV descriptors. - if (!InitializeSwapChainBuffers()) { - Shutdown(); - return false; - } - - // Create the command list for compositing. - for (uint32_t i = 0; i < kSwapCommandAllocatorCount; ++i) { - if (FAILED(device->CreateCommandAllocator( - D3D12_COMMAND_LIST_TYPE_DIRECT, - IID_PPV_ARGS(&swap_command_allocators_[i])))) { - XELOGE("Failed to create a composition command allocator"); - Shutdown(); - return false; - } - } - if (FAILED(device->CreateCommandList( - 0, D3D12_COMMAND_LIST_TYPE_DIRECT, swap_command_allocators_[0].Get(), - nullptr, IID_PPV_ARGS(&swap_command_list_)))) { - XELOGE("Failed to create the composition graphics command list"); - Shutdown(); - return false; - } - // Initially in open state, wait until BeginSwap. - swap_command_list_->Close(); - - // Associate the swap chain with the window via DirectComposition. - if (FAILED(provider.CreateDCompositionDevice(nullptr, - IID_PPV_ARGS(&dcomp_device_)))) { - XELOGE("Failed to create a DirectComposition device"); - Shutdown(); - return false; - } - if (FAILED(dcomp_device_->CreateTargetForHwnd( - reinterpret_cast(target_window_->native_handle()), TRUE, - &dcomp_target_))) { - XELOGE("Failed to create a DirectComposition target for the window"); - Shutdown(); - return false; - } - if (FAILED(dcomp_device_->CreateVisual(&dcomp_visual_))) { - XELOGE("Failed to create a DirectComposition visual"); - Shutdown(); - return false; - } - if (FAILED(dcomp_visual_->SetContent(swap_chain_.Get()))) { - XELOGE( - "Failed to set the content of the DirectComposition visual to the swap " - "chain"); - Shutdown(); - return false; - } - if (FAILED(dcomp_target_->SetRoot(dcomp_visual_.Get()))) { - XELOGE( - "Failed to set the root of the DirectComposition target to the swap " - "chain visual"); - Shutdown(); - return false; - } - if (FAILED(dcomp_device_->Commit())) { - XELOGE("Failed to commit DirectComposition commands"); - Shutdown(); - return false; - } - - // Initialize the immediate mode drawer if not offscreen. - immediate_drawer_ = std::make_unique(*this); - if (!immediate_drawer_->Initialize()) { - Shutdown(); - return false; - } - - return true; -} - -bool D3D12Context::InitializeSwapChainBuffers() { - // Get references to the buffers. - for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) { - if (FAILED( - swap_chain_->GetBuffer(i, IID_PPV_ARGS(&swap_chain_buffers_[i])))) { - XELOGE("Failed to get buffer {} of the swap chain", i); - return false; - } - } - - // Get the back buffer index for the first draw. - swap_chain_back_buffer_index_ = swap_chain_->GetCurrentBackBufferIndex(); - - // Create RTV descriptors for the swap chain buffers. - ID3D12Device* device = GetD3D12Provider().GetDevice(); - D3D12_RENDER_TARGET_VIEW_DESC rtv_desc; - rtv_desc.Format = kSwapChainFormat; - rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - rtv_desc.Texture2D.MipSlice = 0; - rtv_desc.Texture2D.PlaneSlice = 0; - for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) { - device->CreateRenderTargetView(swap_chain_buffers_[i].Get(), &rtv_desc, - GetSwapChainBufferRTV(i)); - } - - return true; -} - -void D3D12Context::Shutdown() { - if (!target_window_) { - return; - } - - if (!context_lost_ && swap_fence_ && - swap_fence_->GetCompletedValue() + 1 < swap_fence_current_value_) { - swap_fence_->SetEventOnCompletion(swap_fence_current_value_ - 1, - swap_fence_completion_event_); - WaitForSingleObject(swap_fence_completion_event_, INFINITE); - } - - immediate_drawer_.reset(); - - dcomp_visual_.Reset(); - dcomp_target_.Reset(); - dcomp_device_.Reset(); - - swap_command_list_.Reset(); - for (uint32_t i = 0; i < kSwapCommandAllocatorCount; ++i) { - swap_command_allocators_[i].Reset(); - } - - for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) { - swap_chain_buffers_[i].Reset(); - } - swap_chain_rtv_heap_.Reset(); - swap_chain_.Reset(); - - // First release the fence since it may reference the event. - swap_fence_.Reset(); - if (swap_fence_completion_event_) { - CloseHandle(swap_fence_completion_event_); - swap_fence_completion_event_ = nullptr; - } - swap_fence_current_value_ = 1; - swap_fence_completed_value_ = 0; -} - -ImmediateDrawer* D3D12Context::immediate_drawer() { - return immediate_drawer_.get(); -} - -bool D3D12Context::WasLost() { return context_lost_; } - -bool D3D12Context::BeginSwap() { - if (!target_window_ || context_lost_) { - return false; - } - - // Resize the swap chain if the window is resized. - uint32_t target_window_width = target_window_->scaled_width(); - uint32_t target_window_height = target_window_->scaled_height(); - if (swap_chain_width_ != target_window_width || - swap_chain_height_ != target_window_height) { - // Await the completion of swap chain use. - // Context loss is also faked if resizing fails. In this case, before the - // context is shut down to be recreated, frame completion must be awaited - // (this isn't done if the context is truly lost). - if (swap_fence_completed_value_ + 1 < swap_fence_current_value_) { - swap_fence_->SetEventOnCompletion(swap_fence_current_value_ - 1, - swap_fence_completion_event_); - WaitForSingleObject(swap_fence_completion_event_, INFINITE); - swap_fence_completed_value_ = swap_fence_current_value_ - 1; - } - // All buffer references must be released before resizing. - for (uint32_t i = 0; i < kSwapChainBufferCount; ++i) { - swap_chain_buffers_[i].Reset(); - } - if (FAILED(swap_chain_->ResizeBuffers( - kSwapChainBufferCount, target_window_width, target_window_height, - kSwapChainFormat, 0))) { - context_lost_ = true; - return false; - } - swap_chain_width_ = target_window_width; - swap_chain_height_ = target_window_height; - if (!InitializeSwapChainBuffers()) { - context_lost_ = true; - return false; - } - } - - // Wait for a swap command allocator to become free. - // Command allocator 0 is used when swap_fence_current_value_ is 1, 4, 7... - swap_fence_completed_value_ = swap_fence_->GetCompletedValue(); - if (swap_fence_completed_value_ + kSwapCommandAllocatorCount < - swap_fence_current_value_) { - swap_fence_->SetEventOnCompletion( - swap_fence_current_value_ - kSwapCommandAllocatorCount, - swap_fence_completion_event_); - WaitForSingleObject(swap_fence_completion_event_, INFINITE); - swap_fence_completed_value_ = swap_fence_->GetCompletedValue(); - } - - // Start the command list. - uint32_t command_allocator_index = - uint32_t((swap_fence_current_value_ + (kSwapCommandAllocatorCount - 1)) % - kSwapCommandAllocatorCount); - ID3D12CommandAllocator* command_allocator = - swap_command_allocators_[command_allocator_index].Get(); - command_allocator->Reset(); - swap_command_list_->Reset(command_allocator, nullptr); - - // Bind the back buffer as a render target and clear it. - D3D12_RESOURCE_BARRIER barrier; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = - swap_chain_buffers_[swap_chain_back_buffer_index_].Get(); - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - swap_command_list_->ResourceBarrier(1, &barrier); - D3D12_CPU_DESCRIPTOR_HANDLE back_buffer_rtv = GetSwapChainBackBufferRTV(); - swap_command_list_->OMSetRenderTargets(1, &back_buffer_rtv, TRUE, nullptr); - float clear_color[4]; - GetClearColor(clear_color); - swap_command_list_->ClearRenderTargetView(back_buffer_rtv, clear_color, 0, - nullptr); - - return true; -} - -void D3D12Context::EndSwap() { - if (!target_window_ || context_lost_) { - return; - } - - ID3D12CommandQueue* direct_queue = GetD3D12Provider().GetDirectQueue(); - - // Switch the back buffer to presentation state. - D3D12_RESOURCE_BARRIER barrier; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = - swap_chain_buffers_[swap_chain_back_buffer_index_].Get(); - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - swap_command_list_->ResourceBarrier(1, &barrier); - - // Submit the command list. - swap_command_list_->Close(); - ID3D12CommandList* execute_command_lists[] = {swap_command_list_.Get()}; - direct_queue->ExecuteCommandLists(1, execute_command_lists); - - // Present and check if the context was lost. - if (FAILED(swap_chain_->Present(0, 0))) { - context_lost_ = true; - return; - } - - // Signal the fence to wait for frame resources to become free again. - direct_queue->Signal(swap_fence_.Get(), swap_fence_current_value_++); - - // Get the back buffer index for the next frame. - swap_chain_back_buffer_index_ = swap_chain_->GetCurrentBackBufferIndex(); -} - -std::unique_ptr D3D12Context::Capture() { - // TODO(Triang3l): Read back swap chain front buffer. - return nullptr; -} - -D3D12_CPU_DESCRIPTOR_HANDLE D3D12Context::GetSwapChainBufferRTV( - uint32_t buffer_index) const { - return GetD3D12Provider().OffsetRTVDescriptor(swap_chain_rtv_heap_start_, - buffer_index); -} - -} // namespace d3d12 -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/d3d12/d3d12_context.h b/src/xenia/ui/d3d12/d3d12_context.h deleted file mode 100644 index 308d3aa6f..000000000 --- a/src/xenia/ui/d3d12/d3d12_context.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2018 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_D3D12_D3D12_CONTEXT_H_ -#define XENIA_UI_D3D12_D3D12_CONTEXT_H_ - -#include - -#include "xenia/ui/d3d12/d3d12_immediate_drawer.h" -#include "xenia/ui/d3d12/d3d12_provider.h" -#include "xenia/ui/graphics_context.h" - -namespace xe { -namespace ui { -namespace d3d12 { - -class D3D12Context : public GraphicsContext { - public: - ~D3D12Context() override; - - ImmediateDrawer* immediate_drawer() override; - - bool WasLost() override; - - bool BeginSwap() override; - void EndSwap() override; - - std::unique_ptr Capture() override; - - D3D12Provider& GetD3D12Provider() const { - return static_cast(*provider_); - } - - // The format used by DWM. - static constexpr DXGI_FORMAT kSwapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM; - ID3D12Resource* GetSwapChainBuffer(uint32_t buffer_index) const { - return swap_chain_buffers_[buffer_index].Get(); - } - uint32_t GetSwapChainBackBufferIndex() const { - return swap_chain_back_buffer_index_; - } - D3D12_CPU_DESCRIPTOR_HANDLE GetSwapChainBufferRTV( - uint32_t buffer_index) const; - D3D12_CPU_DESCRIPTOR_HANDLE GetSwapChainBackBufferRTV() const { - return GetSwapChainBufferRTV(GetSwapChainBackBufferIndex()); - } - void GetSwapChainSize(uint32_t& width, uint32_t& height) const { - width = swap_chain_width_; - height = swap_chain_height_; - } - // Inside the current BeginSwap/EndSwap pair. - uint64_t GetSwapCurrentFenceValue() const { - return swap_fence_current_value_; - } - uint64_t GetSwapCompletedFenceValue() const { - return swap_fence_completed_value_; - } - ID3D12GraphicsCommandList* GetSwapCommandList() const { - return swap_command_list_.Get(); - } - - private: - friend class D3D12Provider; - explicit D3D12Context(D3D12Provider* provider, Window* target_window); - bool Initialize(); - - private: - bool InitializeSwapChainBuffers(); - void Shutdown(); - - bool context_lost_ = false; - - static constexpr uint32_t kSwapChainBufferCount = 3; - Microsoft::WRL::ComPtr swap_chain_; - uint32_t swap_chain_width_ = 0, swap_chain_height_ = 0; - Microsoft::WRL::ComPtr - swap_chain_buffers_[kSwapChainBufferCount]; - uint32_t swap_chain_back_buffer_index_ = 0; - Microsoft::WRL::ComPtr swap_chain_rtv_heap_; - D3D12_CPU_DESCRIPTOR_HANDLE swap_chain_rtv_heap_start_; - - uint64_t swap_fence_current_value_ = 1; - uint64_t swap_fence_completed_value_ = 0; - HANDLE swap_fence_completion_event_ = nullptr; - Microsoft::WRL::ComPtr swap_fence_; - - static constexpr uint32_t kSwapCommandAllocatorCount = 3; - Microsoft::WRL::ComPtr - swap_command_allocators_[kSwapCommandAllocatorCount]; - // Current command allocator is: - // ((swap_fence_current_value_ + (kSwapCommandAllocatorCount - 1))) % - // kSwapCommandAllocatorCount. - Microsoft::WRL::ComPtr swap_command_list_; - - Microsoft::WRL::ComPtr dcomp_device_; - Microsoft::WRL::ComPtr dcomp_target_; - Microsoft::WRL::ComPtr dcomp_visual_; - - std::unique_ptr immediate_drawer_; -}; - -} // namespace d3d12 -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_D3D12_D3D12_CONTEXT_H_ diff --git a/src/xenia/ui/d3d12/d3d12_descriptor_heap_pool.cc b/src/xenia/ui/d3d12/d3d12_descriptor_heap_pool.cc index 5d3dac99d..906a8a1d9 100644 --- a/src/xenia/ui/d3d12/d3d12_descriptor_heap_pool.cc +++ b/src/xenia/ui/d3d12/d3d12_descriptor_heap_pool.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -41,6 +41,25 @@ void D3D12DescriptorHeapPool::Reclaim(uint64_t completed_submission_index) { } } +void D3D12DescriptorHeapPool::ChangeSubmissionTimeline() { + // Reclaim all submitted pages. + if (writable_last_) { + writable_last_->next = submitted_first_; + } else { + writable_first_ = submitted_first_; + } + writable_last_ = submitted_last_; + submitted_first_ = nullptr; + submitted_last_ = nullptr; + + // Mark all pages as never used yet in the new timeline. + Page* page = writable_first_; + while (page) { + page->last_submission_index = 0; + page = page->next; + } +} + void D3D12DescriptorHeapPool::ClearCache() { // Not checking current_page_used_ != 0 because asking for 0 descriptors // returns a valid heap also - but actually the new heap will be different now @@ -49,14 +68,12 @@ void D3D12DescriptorHeapPool::ClearCache() { current_page_used_ = 0; while (submitted_first_) { auto next = submitted_first_->next; - submitted_first_->heap->Release(); delete submitted_first_; submitted_first_ = next; } submitted_last_ = nullptr; while (writable_first_) { auto next = writable_first_->next; - writable_first_->heap->Release(); delete writable_first_; writable_first_ = next; } @@ -110,7 +127,7 @@ uint64_t D3D12DescriptorHeapPool::Request(uint64_t submission_index, new_heap_desc.NumDescriptors = page_size_; new_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; new_heap_desc.NodeMask = 0; - ID3D12DescriptorHeap* new_heap; + Microsoft::WRL::ComPtr new_heap; if (FAILED(device_->CreateDescriptorHeap(&new_heap_desc, IID_PPV_ARGS(&new_heap)))) { XELOGE("Failed to create a heap for {} shader-visible descriptors", diff --git a/src/xenia/ui/d3d12/d3d12_descriptor_heap_pool.h b/src/xenia/ui/d3d12/d3d12_descriptor_heap_pool.h index fbc77aeef..9eb1c5b31 100644 --- a/src/xenia/ui/d3d12/d3d12_descriptor_heap_pool.h +++ b/src/xenia/ui/d3d12/d3d12_descriptor_heap_pool.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -30,6 +30,7 @@ class D3D12DescriptorHeapPool { ~D3D12DescriptorHeapPool(); void Reclaim(uint64_t completed_submission_index); + void ChangeSubmissionTimeline(); void ClearCache(); // Because all descriptors for a single draw call must be in the same heap, @@ -65,7 +66,7 @@ class D3D12DescriptorHeapPool { // after a successful request because before a request, the heap may not exist // yet. ID3D12DescriptorHeap* GetLastRequestHeap() const { - return writable_first_->heap; + return writable_first_->heap.Get(); } D3D12_CPU_DESCRIPTOR_HANDLE GetLastRequestHeapCPUStart() const { return writable_first_->cpu_start; @@ -80,7 +81,7 @@ class D3D12DescriptorHeapPool { uint32_t page_size_; struct Page { - ID3D12DescriptorHeap* heap; + Microsoft::WRL::ComPtr heap; D3D12_CPU_DESCRIPTOR_HANDLE cpu_start; D3D12_GPU_DESCRIPTOR_HANDLE gpu_start; uint64_t last_submission_index; diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc index 5cd6b551c..76bdcb5c0 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2018 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,7 +17,7 @@ #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" -#include "xenia/ui/d3d12/d3d12_context.h" +#include "xenia/ui/d3d12/d3d12_presenter.h" #include "xenia/ui/d3d12/d3d12_util.h" namespace xe { @@ -38,35 +38,40 @@ D3D12ImmediateDrawer::D3D12ImmediateTexture::D3D12ImmediateTexture( resource_(resource), sampler_index_(sampler_index), immediate_drawer_(immediate_drawer), - immediate_drawer_index_(immediate_drawer_index) { - if (resource_) { - resource_->AddRef(); - } -} + immediate_drawer_index_(immediate_drawer_index) {} D3D12ImmediateDrawer::D3D12ImmediateTexture::~D3D12ImmediateTexture() { if (immediate_drawer_) { immediate_drawer_->OnImmediateTextureDestroyed(*this); } - if (resource_) { - resource_->Release(); - } } -void D3D12ImmediateDrawer::D3D12ImmediateTexture::OnImmediateDrawerShutdown() { +void D3D12ImmediateDrawer::D3D12ImmediateTexture::OnImmediateDrawerDestroyed() { immediate_drawer_ = nullptr; // Lifetime is not managed anymore, so don't keep the resource either. - util::ReleaseAndNull(resource_); + resource_.Reset(); } -D3D12ImmediateDrawer::D3D12ImmediateDrawer(D3D12Context& graphics_context) - : ImmediateDrawer(&graphics_context), context_(graphics_context) {} +D3D12ImmediateDrawer::~D3D12ImmediateDrawer() { + // Await GPU usage completion of all draws and texture uploads (which happen + // before draws). + auto d3d12_presenter = static_cast(presenter()); + if (d3d12_presenter) { + d3d12_presenter->AwaitUISubmissionCompletionFromUIThread( + last_paint_submission_index_); + } -D3D12ImmediateDrawer::~D3D12ImmediateDrawer() { Shutdown(); } + // Texture resources and descriptors are owned and tracked by the immediate + // drawer. Zombie texture objects are supported, but are meaningless. + assert_true(textures_.empty()); + for (D3D12ImmediateTexture* texture : textures_) { + texture->OnImmediateDrawerDestroyed(); + } + textures_.clear(); +} bool D3D12ImmediateDrawer::Initialize() { - const D3D12Provider& provider = context_.GetD3D12Provider(); - ID3D12Device* device = provider.GetDevice(); + ID3D12Device* device = provider_.GetDevice(); // Create the root signature. D3D12_ROOT_PARAMETER root_parameters[size_t(RootParameter::kCount)]; @@ -99,7 +104,7 @@ bool D3D12ImmediateDrawer::Initialize() { } { auto& root_parameter = - root_parameters[size_t(RootParameter::kViewportSizeInv)]; + root_parameters[size_t(RootParameter::kCoordinateSpaceSizeInv)]; root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; root_parameter.Constants.ShaderRegister = 0; root_parameter.Constants.RegisterSpace = 0; @@ -113,16 +118,16 @@ bool D3D12ImmediateDrawer::Initialize() { root_signature_desc.pStaticSamplers = nullptr; root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; - root_signature_ = util::CreateRootSignature(provider, root_signature_desc); - if (root_signature_ == nullptr) { - XELOGE("Failed to create the Direct3D 12 immediate drawer root signature"); - Shutdown(); + *(root_signature_.ReleaseAndGetAddressOf()) = + util::CreateRootSignature(provider_, root_signature_desc); + if (!root_signature_) { + XELOGE("D3D12ImmediateDrawer: Failed to create the root signature"); return false; } // Create the pipelines. D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_desc = {}; - pipeline_desc.pRootSignature = root_signature_; + pipeline_desc.pRootSignature = root_signature_.Get(); pipeline_desc.VS.pShaderBytecode = shaders::immediate_vs; pipeline_desc.VS.BytecodeLength = sizeof(shaders::immediate_vs); pipeline_desc.PS.pShaderBytecode = shaders::immediate_ps; @@ -133,13 +138,10 @@ bool D3D12ImmediateDrawer::Initialize() { pipeline_blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA; pipeline_blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA; pipeline_blend_desc.BlendOp = D3D12_BLEND_OP_ADD; - // Don't change alpha (always 1). - pipeline_blend_desc.SrcBlendAlpha = D3D12_BLEND_ZERO; + pipeline_blend_desc.SrcBlendAlpha = D3D12_BLEND_ONE; pipeline_blend_desc.DestBlendAlpha = D3D12_BLEND_ONE; pipeline_blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD; - pipeline_blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_RED | - D3D12_COLOR_WRITE_ENABLE_GREEN | - D3D12_COLOR_WRITE_ENABLE_BLUE; + pipeline_blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; pipeline_desc.SampleMask = UINT_MAX; pipeline_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; pipeline_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; @@ -161,23 +163,17 @@ bool D3D12ImmediateDrawer::Initialize() { UINT(xe::countof(pipeline_input_elements)); pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; pipeline_desc.NumRenderTargets = 1; - pipeline_desc.RTVFormats[0] = D3D12Context::kSwapChainFormat; + pipeline_desc.RTVFormats[0] = D3D12Presenter::kSwapChainFormat; pipeline_desc.SampleDesc.Count = 1; if (FAILED(device->CreateGraphicsPipelineState( &pipeline_desc, IID_PPV_ARGS(&pipeline_triangle_)))) { - XELOGE( - "Failed to create the Direct3D 12 immediate drawer triangle pipeline " - "state"); - Shutdown(); + XELOGE("D3D12ImmediateDrawer: Failed to create the triangle pipeline"); return false; } pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; if (FAILED(device->CreateGraphicsPipelineState( &pipeline_desc, IID_PPV_ARGS(&pipeline_line_)))) { - XELOGE( - "Failed to create the Direct3D 12 immediate drawer line pipeline " - "state"); - Shutdown(); + XELOGE("D3D12ImmediateDrawer: Failed to create the line pipeline"); return false; } @@ -190,14 +186,12 @@ bool D3D12ImmediateDrawer::Initialize() { if (FAILED(device->CreateDescriptorHeap(&sampler_heap_desc, IID_PPV_ARGS(&sampler_heap_)))) { XELOGE( - "Failed to create the Direct3D 12 immediate drawer sampler descriptor " - "heap"); - Shutdown(); + "D3D12ImmediateDrawer: Failed to create the sampler descriptor heap"); return false; } sampler_heap_cpu_start_ = sampler_heap_->GetCPUDescriptorHandleForHeapStart(); sampler_heap_gpu_start_ = sampler_heap_->GetGPUDescriptorHandleForHeapStart(); - uint32_t sampler_size = provider.GetSamplerDescriptorSize(); + uint32_t sampler_size = provider_.GetSamplerDescriptorSize(); // Nearest neighbor, clamp. D3D12_SAMPLER_DESC sampler_desc = {}; sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; @@ -228,58 +222,22 @@ bool D3D12ImmediateDrawer::Initialize() { device->CreateSampler(&sampler_desc, sampler_handle); // Create pools for draws. - vertex_buffer_pool_ = std::make_unique(provider); + vertex_buffer_pool_ = std::make_unique(provider_); texture_descriptor_pool_ = std::make_unique( device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2048); // Reset the current state. - current_command_list_ = nullptr; batch_open_ = false; return true; } -void D3D12ImmediateDrawer::Shutdown() { - for (auto& deleted_texture : textures_deleted_) { - deleted_texture.first->Release(); - } - textures_deleted_.clear(); - - for (auto& texture_upload : texture_uploads_submitted_) { - texture_upload.buffer->Release(); - texture_upload.texture->Release(); - } - texture_uploads_submitted_.clear(); - - for (auto& texture_upload : texture_uploads_pending_) { - texture_upload.buffer->Release(); - texture_upload.texture->Release(); - } - texture_uploads_pending_.clear(); - - for (D3D12ImmediateTexture* texture : textures_) { - texture->OnImmediateDrawerShutdown(); - } - textures_.clear(); - - texture_descriptor_pool_.reset(); - vertex_buffer_pool_.reset(); - - util::ReleaseAndNull(sampler_heap_); - - util::ReleaseAndNull(pipeline_line_); - util::ReleaseAndNull(pipeline_triangle_); - - util::ReleaseAndNull(root_signature_); -} - std::unique_ptr D3D12ImmediateDrawer::CreateTexture( uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool is_repeated, const uint8_t* data) { - const D3D12Provider& provider = context_.GetD3D12Provider(); - ID3D12Device* device = provider.GetDevice(); + ID3D12Device* device = provider_.GetDevice(); D3D12_HEAP_FLAGS heap_flag_create_not_zeroed = - provider.GetHeapFlagCreateNotZeroed(); + provider_.GetHeapFlagCreateNotZeroed(); D3D12_RESOURCE_DESC resource_desc; resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; @@ -293,8 +251,8 @@ std::unique_ptr D3D12ImmediateDrawer::CreateTexture( resource_desc.SampleDesc.Quality = 0; resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE; - ID3D12Resource* resource; - if (SUCCEEDED(provider.GetDevice()->CreateCommittedResource( + Microsoft::WRL::ComPtr resource; + if (SUCCEEDED(device->CreateCommittedResource( &util::kHeapPropertiesDefault, heap_flag_create_not_zeroed, &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&resource)))) { @@ -306,7 +264,7 @@ std::unique_ptr D3D12ImmediateDrawer::CreateTexture( D3D12_RESOURCE_DESC upload_buffer_desc; util::FillBufferResourceDesc(upload_buffer_desc, upload_size, D3D12_RESOURCE_FLAG_NONE); - ID3D12Resource* upload_buffer; + Microsoft::WRL::ComPtr upload_buffer; if (SUCCEEDED(device->CreateCommittedResource( &util::kHeapPropertiesUpload, heap_flag_create_not_zeroed, &upload_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, @@ -333,35 +291,30 @@ std::unique_ptr D3D12ImmediateDrawer::CreateTexture( } upload_buffer->Unmap(0, nullptr); // Defer uploading and transition to the next draw. - PendingTextureUpload& pending_upload = - texture_uploads_pending_.emplace_back(); // While the upload has not been yet completed, keep a reference to the // resource because its lifetime is not tied to that of the // ImmediateTexture (and thus to context's submissions) now. - resource->AddRef(); - pending_upload.texture = resource; - pending_upload.buffer = upload_buffer; + PendingTextureUpload& pending_upload = + texture_uploads_pending_.emplace_back(resource.Get(), + upload_buffer.Get()); } else { XELOGE( - "Failed to map a Direct3D 12 upload buffer for a {}x{} texture for " - "immediate drawing", + "D3D12ImmediateDrawer: Failed to map an upload buffer for a {}x{} " + "texture", width, height); - upload_buffer->Release(); - resource->Release(); - resource = nullptr; + upload_buffer.Reset(); + resource.Reset(); } } else { XELOGE( - "Failed to create a Direct3D 12 upload buffer for a {}x{} texture " - "for immediate drawing", + "D3D12ImmediateDrawer: Failed to create an upload buffer for a {}x{} " + "texture", width, height); - resource->Release(); - resource = nullptr; + resource.Reset(); } } else { - XELOGE("Failed to create a {}x{} Direct3D 12 texture for immediate drawing", - width, height); - resource = nullptr; + XELOGE("D3D12ImmediateDrawer: Failed to create a {}x{} texture", width, + height); } SamplerIndex sampler_index; @@ -376,35 +329,38 @@ std::unique_ptr D3D12ImmediateDrawer::CreateTexture( // Manage by this immediate drawer if successfully created a resource. std::unique_ptr texture = std::make_unique( - width, height, resource, sampler_index, resource ? this : nullptr, - textures_.size()); + width, height, resource.Get(), sampler_index, + resource ? this : nullptr, textures_.size()); if (resource) { textures_.push_back(texture.get()); - // D3D12ImmediateTexture now holds a reference. - resource->Release(); } return std::move(texture); } -void D3D12ImmediateDrawer::Begin(int render_target_width, - int render_target_height) { - assert_null(current_command_list_); +void D3D12ImmediateDrawer::Begin(UIDrawContext& ui_draw_context, + float coordinate_space_width, + float coordinate_space_height) { + ImmediateDrawer::Begin(ui_draw_context, coordinate_space_width, + coordinate_space_height); + assert_false(batch_open_); - ID3D12Device* device = context_.GetD3D12Provider().GetDevice(); + const D3D12UIDrawContext& d3d12_ui_draw_context = + static_cast(ui_draw_context); - // Use the compositing command list. - current_command_list_ = context_.GetSwapCommandList(); - - uint64_t completed_fence_value = context_.GetSwapCompletedFenceValue(); + // Update the submission index to be used throughout the current immediate + // drawer paint. + last_paint_submission_index_ = + d3d12_ui_draw_context.submission_index_current(); + last_completed_submission_index_ = + d3d12_ui_draw_context.submission_index_completed(); // Release deleted textures. for (auto it = textures_deleted_.begin(); it != textures_deleted_.end();) { - if (it->second > completed_fence_value) { + if (it->second > last_completed_submission_index_) { ++it; continue; } - it->first->Release(); if (std::next(it) != textures_deleted_.end()) { *it = textures_deleted_.back(); } @@ -414,37 +370,45 @@ void D3D12ImmediateDrawer::Begin(int render_target_width, // Release upload buffers for completed texture uploads. auto erase_uploads_end = texture_uploads_submitted_.begin(); while (erase_uploads_end != texture_uploads_submitted_.end()) { - if (erase_uploads_end->fence_value > completed_fence_value) { + if (erase_uploads_end->submission_index > + last_completed_submission_index_) { break; } - erase_uploads_end->buffer->Release(); - // Release the texture reference held for uploading. - erase_uploads_end->texture->Release(); ++erase_uploads_end; } texture_uploads_submitted_.erase(texture_uploads_submitted_.begin(), erase_uploads_end); - vertex_buffer_pool_->Reclaim(completed_fence_value); - texture_descriptor_pool_->Reclaim(completed_fence_value); + // Make sure textures created before the current frame are uploaded, even if + // nothing was drawn in the previous frames or nothing will be drawn in the + // current or subsequent ones, as that would result in upload buffers kept + // forever. + UploadTextures(); + + texture_descriptor_pool_->Reclaim(last_completed_submission_index_); + vertex_buffer_pool_->Reclaim(last_completed_submission_index_); + + // Begin drawing. + + ID3D12GraphicsCommandList* command_list = + d3d12_ui_draw_context.command_list(); - current_render_target_width_ = render_target_width; - current_render_target_height_ = render_target_height; D3D12_VIEWPORT viewport; viewport.TopLeftX = 0.0f; viewport.TopLeftY = 0.0f; - viewport.Width = float(render_target_width); - viewport.Height = float(render_target_height); + viewport.Width = float(d3d12_ui_draw_context.render_target_width()); + viewport.Height = float(d3d12_ui_draw_context.render_target_height()); viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; - current_command_list_->RSSetViewports(1, &viewport); + command_list->RSSetViewports(1, &viewport); - current_command_list_->SetGraphicsRootSignature(root_signature_); - float viewport_inv_size[2]; - viewport_inv_size[0] = 1.0f / viewport.Width; - viewport_inv_size[1] = 1.0f / viewport.Height; - current_command_list_->SetGraphicsRoot32BitConstants( - UINT(RootParameter::kViewportSizeInv), 2, viewport_inv_size, 0); + command_list->SetGraphicsRootSignature(root_signature_.Get()); + float coordinate_space_size_inv[2]; + coordinate_space_size_inv[0] = 1.0f / coordinate_space_width; + coordinate_space_size_inv[1] = 1.0f / coordinate_space_height; + command_list->SetGraphicsRoot32BitConstants( + UINT(RootParameter::kCoordinateSpaceSizeInv), 2, + coordinate_space_size_inv, 0); current_scissor_.left = 0; current_scissor_.top = 0; @@ -460,9 +424,12 @@ void D3D12ImmediateDrawer::Begin(int render_target_width, void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) { assert_false(batch_open_); - assert_not_null(current_command_list_); - uint64_t current_fence_value = context_.GetSwapCurrentFenceValue(); + const D3D12UIDrawContext& d3d12_ui_draw_context = + *static_cast(ui_draw_context()); + + ID3D12GraphicsCommandList* command_list = + d3d12_ui_draw_context.command_list(); // Bind the vertices. D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view; @@ -470,16 +437,16 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) { vertex_buffer_view.SizeInBytes = UINT(sizeof(ImmediateVertex)) * batch.vertex_count; void* vertex_buffer_mapping = vertex_buffer_pool_->Request( - current_fence_value, vertex_buffer_view.SizeInBytes, sizeof(float), - nullptr, nullptr, &vertex_buffer_view.BufferLocation); + last_paint_submission_index_, vertex_buffer_view.SizeInBytes, + sizeof(float), nullptr, nullptr, &vertex_buffer_view.BufferLocation); if (vertex_buffer_mapping == nullptr) { - XELOGE("Failed to get a buffer for {} vertices in the immediate drawer", + XELOGE("D3D12ImmediateDrawer: Failed to get a buffer for {} vertices", batch.vertex_count); return; } std::memcpy(vertex_buffer_mapping, batch.vertices, vertex_buffer_view.SizeInBytes); - current_command_list_->IASetVertexBuffers(0, 1, &vertex_buffer_view); + command_list->IASetVertexBuffers(0, 1, &vertex_buffer_view); // Bind the indices. batch_has_index_buffer_ = batch.indices != nullptr; @@ -488,16 +455,16 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) { index_buffer_view.SizeInBytes = UINT(sizeof(uint16_t)) * batch.index_count; index_buffer_view.Format = DXGI_FORMAT_R16_UINT; void* index_buffer_mapping = vertex_buffer_pool_->Request( - current_fence_value, index_buffer_view.SizeInBytes, sizeof(uint16_t), - nullptr, nullptr, &index_buffer_view.BufferLocation); + last_paint_submission_index_, index_buffer_view.SizeInBytes, + sizeof(uint16_t), nullptr, nullptr, &index_buffer_view.BufferLocation); if (index_buffer_mapping == nullptr) { - XELOGE("Failed to get a buffer for {} indices in the immediate drawer", + XELOGE("D3D12ImmediateDrawer: Failed to get a buffer for {} indices", batch.index_count); return; } std::memcpy(index_buffer_mapping, batch.indices, index_buffer_view.SizeInBytes); - current_command_list_->IASetIndexBuffer(&index_buffer_view); + command_list->IASetIndexBuffer(&index_buffer_view); } batch_open_ = true; @@ -509,30 +476,30 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { return; } - // Set the scissor rectangle if enabled. - D3D12_RECT scissor; - if (draw.scissor) { - scissor.left = draw.scissor_rect[0]; - scissor.top = current_render_target_height_ - - (draw.scissor_rect[1] + draw.scissor_rect[3]); - scissor.right = scissor.left + draw.scissor_rect[2]; - scissor.bottom = scissor.top + draw.scissor_rect[3]; - } else { - scissor.left = 0; - scissor.top = 0; - scissor.right = current_render_target_width_; - scissor.bottom = current_render_target_height_; - } - if (scissor.right <= scissor.left || scissor.bottom <= scissor.top) { - // Nothing is visible (used as the default current_scissor_ value also). + const D3D12UIDrawContext& d3d12_ui_draw_context = + *static_cast(ui_draw_context()); + ID3D12GraphicsCommandList* command_list = + d3d12_ui_draw_context.command_list(); + + // Set the scissor rectangle. + uint32_t scissor_left, scissor_top, scissor_width, scissor_height; + if (!ScissorToRenderTarget(draw, scissor_left, scissor_top, scissor_width, + scissor_height)) { + // Nothing is visible (zero area is used as the default current_scissor_ + // value also). return; } + D3D12_RECT scissor; + scissor.left = LONG(scissor_left); + scissor.top = LONG(scissor_top); + scissor.right = LONG(scissor_left + scissor_width); + scissor.bottom = LONG(scissor_top + scissor_height); if (current_scissor_.left != scissor.left || current_scissor_.top != scissor.top || current_scissor_.right != scissor.right || current_scissor_.bottom != scissor.bottom) { current_scissor_ = scissor; - current_command_list_->RSSetScissorRects(1, &scissor); + command_list->RSSetScissorRects(1, &scissor); } // Ensure texture data is available if any texture is loaded, upload all in a @@ -542,30 +509,27 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { // Bind the texture. If this is the first draw in a frame, the descriptor heap // index will be invalid initially, and the texture will be bound regardless // of what's in current_texture_. - uint64_t current_fence_value = context_.GetSwapCurrentFenceValue(); auto texture = static_cast(draw.texture); ID3D12Resource* texture_resource = texture ? texture->resource() : nullptr; bool bind_texture = current_texture_ != texture_resource; uint32_t texture_descriptor_index; uint64_t texture_heap_index = texture_descriptor_pool_->Request( - current_fence_value, current_texture_descriptor_heap_index_, + last_paint_submission_index_, current_texture_descriptor_heap_index_, bind_texture ? 1 : 0, 1, texture_descriptor_index); if (texture_heap_index == D3D12DescriptorHeapPool::kHeapIndexInvalid) { return; } if (texture_resource) { - texture->SetLastUsageFenceValue(current_fence_value); + texture->SetLastUsageSubmissionIndex(last_paint_submission_index_); } if (current_texture_descriptor_heap_index_ != texture_heap_index) { current_texture_descriptor_heap_index_ = texture_heap_index; bind_texture = true; ID3D12DescriptorHeap* descriptor_heaps[] = { - texture_descriptor_pool_->GetLastRequestHeap(), sampler_heap_}; - current_command_list_->SetDescriptorHeaps(2, descriptor_heaps); + texture_descriptor_pool_->GetLastRequestHeap(), sampler_heap_.Get()}; + command_list->SetDescriptorHeaps(2, descriptor_heaps); } - const D3D12Provider& provider = context_.GetD3D12Provider(); - if (bind_texture) { current_texture_ = texture_resource; D3D12_SHADER_RESOURCE_VIEW_DESC texture_view_desc; @@ -587,14 +551,14 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { texture_view_desc.Texture2D.MipLevels = 1; texture_view_desc.Texture2D.PlaneSlice = 0; texture_view_desc.Texture2D.ResourceMinLODClamp = 0.0f; - provider.GetDevice()->CreateShaderResourceView( + provider_.GetDevice()->CreateShaderResourceView( texture_resource, &texture_view_desc, - provider.OffsetViewDescriptor( + provider_.OffsetViewDescriptor( texture_descriptor_pool_->GetLastRequestHeapCPUStart(), texture_descriptor_index)); - current_command_list_->SetGraphicsRootDescriptorTable( + command_list->SetGraphicsRootDescriptorTable( UINT(RootParameter::kTexture), - provider.OffsetViewDescriptor( + provider_.OffsetViewDescriptor( texture_descriptor_pool_->GetLastRequestHeapGPUStart(), texture_descriptor_index)); } @@ -605,10 +569,10 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { texture_resource ? texture->sampler_index() : SamplerIndex::kNearestClamp; if (current_sampler_index_ != sampler_index) { current_sampler_index_ = sampler_index; - current_command_list_->SetGraphicsRootDescriptorTable( + command_list->SetGraphicsRootDescriptorTable( UINT(RootParameter::kSampler), - provider.OffsetSamplerDescriptor(sampler_heap_gpu_start_, - uint32_t(sampler_index))); + provider_.OffsetSamplerDescriptor(sampler_heap_gpu_start_, + uint32_t(sampler_index))); } // Set the primitive type and the pipeline for it. @@ -617,11 +581,11 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { switch (draw.primitive_type) { case ImmediatePrimitiveType::kLines: primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; - pipeline = pipeline_line_; + pipeline = pipeline_line_.Get(); break; case ImmediatePrimitiveType::kTriangles: primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - pipeline = pipeline_triangle_; + pipeline = pipeline_triangle_.Get(); break; default: assert_unhandled_case(draw.primitive_type); @@ -629,16 +593,16 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { } if (current_primitive_topology_ != primitive_topology) { current_primitive_topology_ = primitive_topology; - current_command_list_->IASetPrimitiveTopology(primitive_topology); - current_command_list_->SetPipelineState(pipeline); + command_list->IASetPrimitiveTopology(primitive_topology); + command_list->SetPipelineState(pipeline); } // Draw. if (batch_has_index_buffer_) { - current_command_list_->DrawIndexedInstanced( - draw.count, 1, draw.index_offset, draw.base_vertex, 0); + command_list->DrawIndexedInstanced(draw.count, 1, draw.index_offset, + draw.base_vertex, 0); } else { - current_command_list_->DrawInstanced(draw.count, 1, draw.base_vertex, 0); + command_list->DrawInstanced(draw.count, 1, draw.base_vertex, 0); } } @@ -646,11 +610,29 @@ void D3D12ImmediateDrawer::EndDrawBatch() { batch_open_ = false; } void D3D12ImmediateDrawer::End() { assert_false(batch_open_); - if (current_command_list_) { - // Don't keep upload buffers forever if nothing was drawn in this frame. - UploadTextures(); - current_command_list_ = nullptr; + + ImmediateDrawer::End(); +} + +void D3D12ImmediateDrawer::OnLeavePresenter() { + // Leaving the presenter's submission timeline - await GPU usage completion of + // all draws and texture uploads (which happen before draws) and reset + // submission indices. + D3D12Presenter& d3d12_presenter = *static_cast(presenter()); + d3d12_presenter.AwaitUISubmissionCompletionFromUIThread( + last_paint_submission_index_); + + for (D3D12ImmediateTexture* texture : textures_) { + texture->SetLastUsageSubmissionIndex(0); } + + texture_uploads_submitted_.clear(); + + vertex_buffer_pool_->ChangeSubmissionTimeline(); + texture_descriptor_pool_->ChangeSubmissionTimeline(); + + last_paint_submission_index_ = 0; + last_completed_submission_index_ = 0; } void D3D12ImmediateDrawer::OnImmediateTextureDestroyed( @@ -665,35 +647,35 @@ void D3D12ImmediateDrawer::OnImmediateTextureDestroyed( // Queue for delayed release. ID3D12Resource* resource = texture.resource(); - uint64_t last_usage_fence_value = texture.last_usage_fence_value(); + UINT64 last_usage_submission_index = texture.last_usage_submission_index(); if (resource && - last_usage_fence_value > context_.GetSwapCompletedFenceValue()) { - resource->AddRef(); - textures_deleted_.push_back( - std::make_pair(resource, last_usage_fence_value)); + last_usage_submission_index > last_completed_submission_index_) { + textures_deleted_.emplace_back(resource, last_usage_submission_index); } } void D3D12ImmediateDrawer::UploadTextures() { - assert_not_null(current_command_list_); if (texture_uploads_pending_.empty()) { // Called often - don't initialize anything. return; } - ID3D12Device* device = context_.GetD3D12Provider().GetDevice(); - uint64_t current_fence_value = context_.GetSwapCurrentFenceValue(); + ID3D12Device* device = provider_.GetDevice(); + const D3D12UIDrawContext& d3d12_ui_draw_context = + *static_cast(ui_draw_context()); + ID3D12GraphicsCommandList* command_list = + d3d12_ui_draw_context.command_list(); // Copy all at once, then transition all at once (not interleaving copying and // pipeline barriers). std::vector barriers; barriers.reserve(texture_uploads_pending_.size()); for (const PendingTextureUpload& pending_upload : texture_uploads_pending_) { - ID3D12Resource* texture = pending_upload.texture; + ID3D12Resource* texture = pending_upload.texture.Get(); D3D12_RESOURCE_DESC texture_desc = texture->GetDesc(); D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; - location_source.pResource = pending_upload.buffer; + location_source.pResource = pending_upload.buffer.Get(); location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; device->GetCopyableFootprints(&texture_desc, 0, 1, 0, &location_source.PlacedFootprint, nullptr, @@ -701,8 +683,8 @@ void D3D12ImmediateDrawer::UploadTextures() { location_dest.pResource = texture; location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; location_dest.SubresourceIndex = 0; - current_command_list_->CopyTextureRegion(&location_dest, 0, 0, 0, - &location_source, nullptr); + command_list->CopyTextureRegion(&location_dest, 0, 0, 0, &location_source, + nullptr); D3D12_RESOURCE_BARRIER& barrier = barriers.emplace_back(); barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; @@ -712,18 +694,12 @@ void D3D12ImmediateDrawer::UploadTextures() { barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - SubmittedTextureUpload& submitted_upload = - texture_uploads_submitted_.emplace_back(); - // Transfer the reference to the texture - need to keep it until the upload - // is completed. - submitted_upload.texture = texture; - submitted_upload.buffer = pending_upload.buffer; - submitted_upload.fence_value = current_fence_value; + texture_uploads_submitted_.emplace_back( + texture, pending_upload.buffer.Get(), last_paint_submission_index_); } texture_uploads_pending_.clear(); assert_false(barriers.empty()); - current_command_list_->ResourceBarrier(UINT(barriers.size()), - barriers.data()); + command_list->ResourceBarrier(UINT(barriers.size()), barriers.data()); } } // namespace d3d12 diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h index fbc362f59..2c3691e1f 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2018 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,6 +17,7 @@ #include "xenia/ui/d3d12/d3d12_api.h" #include "xenia/ui/d3d12/d3d12_descriptor_heap_pool.h" +#include "xenia/ui/d3d12/d3d12_provider.h" #include "xenia/ui/d3d12/d3d12_upload_buffer_pool.h" #include "xenia/ui/immediate_drawer.h" @@ -24,15 +25,19 @@ namespace xe { namespace ui { namespace d3d12 { -class D3D12Context; - -class D3D12ImmediateDrawer : public ImmediateDrawer { +class D3D12ImmediateDrawer final : public ImmediateDrawer { public: - D3D12ImmediateDrawer(D3D12Context& graphics_context); - ~D3D12ImmediateDrawer() override; + static std::unique_ptr Create( + const D3D12Provider& provider) { + auto immediate_drawer = std::unique_ptr( + new D3D12ImmediateDrawer(provider)); + if (!immediate_drawer->Initialize()) { + return nullptr; + } + return std::move(immediate_drawer); + } - bool Initialize(); - void Shutdown(); + ~D3D12ImmediateDrawer(); std::unique_ptr CreateTexture(uint32_t width, uint32_t height, @@ -40,12 +45,16 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { bool is_repeated, const uint8_t* data) override; - void Begin(int render_target_width, int render_target_height) override; + void Begin(UIDrawContext& ui_draw_context, float coordinate_space_width, + float coordinate_space_height) override; void BeginDrawBatch(const ImmediateDrawBatch& batch) override; void Draw(const ImmediateDraw& draw) override; void EndDrawBatch() override; void End() override; + protected: + void OnLeavePresenter() override; + private: enum class SamplerIndex { kNearestClamp, @@ -57,7 +66,7 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { kInvalid = kCount }; - class D3D12ImmediateTexture : public ImmediateTexture { + class D3D12ImmediateTexture final : public ImmediateTexture { public: static constexpr DXGI_FORMAT kFormat = DXGI_FORMAT_R8G8B8A8_UNORM; D3D12ImmediateTexture(uint32_t width, uint32_t height, @@ -66,75 +75,93 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { size_t immediate_drawer_index); ~D3D12ImmediateTexture() override; - ID3D12Resource* resource() const { return resource_; } + ID3D12Resource* resource() const { return resource_.Get(); } SamplerIndex sampler_index() const { return sampler_index_; } size_t immediate_drawer_index() const { return immediate_drawer_index_; } void SetImmediateDrawerIndex(size_t index) { immediate_drawer_index_ = index; } - void OnImmediateDrawerShutdown(); + void OnImmediateDrawerDestroyed(); - uint64_t last_usage_fence_value() const { return last_usage_fence_value_; } - void SetLastUsageFenceValue(uint64_t fence_value) { - last_usage_fence_value_ = fence_value; + UINT64 last_usage_submission_index() const { + return last_usage_submission_index_; + } + void SetLastUsageSubmissionIndex(UINT64 submission_index) { + last_usage_submission_index_ = submission_index; } private: - ID3D12Resource* resource_; + Microsoft::WRL::ComPtr resource_; SamplerIndex sampler_index_; D3D12ImmediateDrawer* immediate_drawer_; size_t immediate_drawer_index_; - uint64_t last_usage_fence_value_ = 0; + UINT64 last_usage_submission_index_ = 0; }; + D3D12ImmediateDrawer(const D3D12Provider& provider) : provider_(provider) {} + bool Initialize(); + void OnImmediateTextureDestroyed(D3D12ImmediateTexture& texture); void UploadTextures(); - D3D12Context& context_; + const D3D12Provider& provider_; - ID3D12RootSignature* root_signature_ = nullptr; + Microsoft::WRL::ComPtr root_signature_; enum class RootParameter { kTexture, kSampler, - kViewportSizeInv, + kCoordinateSpaceSizeInv, kCount }; - ID3D12PipelineState* pipeline_triangle_ = nullptr; - ID3D12PipelineState* pipeline_line_ = nullptr; + Microsoft::WRL::ComPtr pipeline_triangle_; + Microsoft::WRL::ComPtr pipeline_line_; - ID3D12DescriptorHeap* sampler_heap_ = nullptr; + Microsoft::WRL::ComPtr sampler_heap_; D3D12_CPU_DESCRIPTOR_HANDLE sampler_heap_cpu_start_; D3D12_GPU_DESCRIPTOR_HANDLE sampler_heap_gpu_start_; - std::unique_ptr vertex_buffer_pool_; - std::unique_ptr texture_descriptor_pool_; - // Only with non-null resources. std::vector textures_; struct PendingTextureUpload { - ID3D12Resource* texture; - ID3D12Resource* buffer; + PendingTextureUpload(ID3D12Resource* texture, ID3D12Resource* buffer) + : texture(texture), buffer(buffer) {} + Microsoft::WRL::ComPtr texture; + Microsoft::WRL::ComPtr buffer; }; std::vector texture_uploads_pending_; struct SubmittedTextureUpload { - ID3D12Resource* texture; - ID3D12Resource* buffer; - uint64_t fence_value; + SubmittedTextureUpload(ID3D12Resource* texture, ID3D12Resource* buffer, + UINT64 submission_index) + : texture(texture), + buffer(buffer), + submission_index(submission_index) {} + Microsoft::WRL::ComPtr texture; + Microsoft::WRL::ComPtr buffer; + UINT64 submission_index; }; std::deque texture_uploads_submitted_; - std::vector> textures_deleted_; + std::deque, UINT64>> + textures_deleted_; + + std::unique_ptr vertex_buffer_pool_; + std::unique_ptr texture_descriptor_pool_; + + // The submission index within the current Begin (or the last, if outside + // one). + UINT64 last_paint_submission_index_ = 0; + // Completed submission index as of the latest Begin, to coarsely skip delayed + // texture deletion. + UINT64 last_completed_submission_index_ = 0; - ID3D12GraphicsCommandList* current_command_list_ = nullptr; - int current_render_target_width_, current_render_target_height_; bool batch_open_ = false; bool batch_has_index_buffer_; D3D12_RECT current_scissor_; diff --git a/src/xenia/ui/d3d12/d3d12_presenter.cc b/src/xenia/ui/d3d12/d3d12_presenter.cc new file mode 100644 index 000000000..574b66d0d --- /dev/null +++ b/src/xenia/ui/d3d12/d3d12_presenter.cc @@ -0,0 +1,1466 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/d3d12/d3d12_presenter.h" + +#include +#include +#include +#include + +#include "xenia/base/assert.h" +#include "xenia/base/cvar.h" +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/ui/d3d12/d3d12_provider.h" +#include "xenia/ui/d3d12/d3d12_util.h" +#include "xenia/ui/surface_win.h" + +DEFINE_bool( + d3d12_allow_variable_refresh_rate_and_tearing, true, + "In fullscreen, allow using variable refresh rate on displays supporting " + "it. On displays not supporting VRR, screen tearing may occur in certain " + "cases.", + "D3D12"); + +namespace xe { +namespace ui { +namespace d3d12 { + +// Generated with `xb buildshaders`. +namespace shaders { +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_dither_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_dither_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_dither_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_easu_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_dither_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_ps.h" +#include "xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_triangle_strip_rect_vs.h" +} // namespace shaders + +D3D12Presenter::~D3D12Presenter() { + // Await completion of the usage of everything before destroying anything. + // From most likely the latest to most likely the earliest to be signaled, so + // just one sleep will likely be needed. + paint_context_.AwaitSwapChainUsageCompletion(); + guest_output_resource_refresher_submission_tracker_.Shutdown(); + ui_submission_tracker_.Shutdown(); +} + +Surface::TypeFlags D3D12Presenter::GetSupportedSurfaceTypes() const { + Surface::TypeFlags types = 0; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_GAMES) + types |= Surface::kTypeFlag_Win32Hwnd; +#endif + return types; +} + +bool D3D12Presenter::CaptureGuestOutput(RawImage& image_out) { + Microsoft::WRL::ComPtr guest_output_resource; + { + uint32_t guest_output_mailbox_index; + std::unique_lock guest_output_consumer_lock( + ConsumeGuestOutput(guest_output_mailbox_index, nullptr, nullptr)); + if (guest_output_mailbox_index != UINT32_MAX) { + guest_output_resource = + guest_output_resources_[guest_output_mailbox_index].second; + } + // Incremented the reference count of the guest output resource - safe to + // leave the consumer critical section now. + } + if (!guest_output_resource) { + return false; + } + + ID3D12Device* device = provider_.GetDevice(); + + D3D12_RESOURCE_DESC texture_desc = guest_output_resource->GetDesc(); + D3D12_TEXTURE_COPY_LOCATION copy_dest; + UINT64 copy_dest_size; + device->GetCopyableFootprints(&texture_desc, 0, 1, 0, + ©_dest.PlacedFootprint, nullptr, nullptr, + ©_dest_size); + + D3D12_RESOURCE_DESC buffer_desc; + util::FillBufferResourceDesc(buffer_desc, copy_dest_size, + D3D12_RESOURCE_FLAG_NONE); + Microsoft::WRL::ComPtr buffer; + // Create zeroed not to leak data in the row padding. + if (FAILED(device->CreateCommittedResource( + &util::kHeapPropertiesReadback, D3D12_HEAP_FLAG_NONE, &buffer_desc, + D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&buffer)))) { + XELOGE("D3D12Presenter: Failed to create the guest output capture buffer"); + return false; + } + + { + Microsoft::WRL::ComPtr command_allocator; + if (FAILED( + device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, + IID_PPV_ARGS(&command_allocator)))) { + XELOGE( + "D3D12Presenter: Failed to create the guest output capturing command " + "allocator"); + return false; + } + Microsoft::WRL::ComPtr command_list; + if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, + command_allocator.Get(), nullptr, + IID_PPV_ARGS(&command_list)))) { + XELOGE( + "D3D12Presenter: Failed to create the guest output capturing command " + "list"); + return false; + } + + D3D12_RESOURCE_BARRIER barrier; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = guest_output_resource.Get(); + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = kGuestOutputInternalState; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + if constexpr (kGuestOutputInternalState != + D3D12_RESOURCE_STATE_COPY_SOURCE) { + command_list->ResourceBarrier(1, &barrier); + } + copy_dest.pResource = buffer.Get(); + copy_dest.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + D3D12_TEXTURE_COPY_LOCATION copy_source; + copy_source.pResource = guest_output_resource.Get(); + copy_source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + copy_source.SubresourceIndex = 0; + command_list->CopyTextureRegion(©_dest, 0, 0, 0, ©_source, nullptr); + if constexpr (kGuestOutputInternalState != + D3D12_RESOURCE_STATE_COPY_SOURCE) { + std::swap(barrier.Transition.StateBefore, barrier.Transition.StateAfter); + command_list->ResourceBarrier(1, &barrier); + } + if (FAILED(command_list->Close())) { + XELOGE( + "D3D12Presenter: Failed to close the guest output capturing command " + "list"); + return false; + } + + ID3D12CommandQueue* direct_queue = provider_.GetDirectQueue(); + + // Make sure that if any work is submitted, any `return` will cause an await + // before releasing the command allocator / list and the resource the RAII + // way in the destruction of the submission tracker - so create after the + // objects referenced in the submission - but don't submit anything if + // failed to initialize the fence. + D3D12SubmissionTracker submission_tracker; + if (!submission_tracker.Initialize(device, direct_queue)) { + return false; + } + ID3D12CommandList* execute_command_list = command_list.Get(); + direct_queue->ExecuteCommandLists(1, &execute_command_list); + if (!submission_tracker.NextSubmission()) { + XELOGE( + "D3D12Presenter: Failed to signal the guest output capturing fence"); + return false; + } + if (!submission_tracker.AwaitAllSubmissionsCompletion()) { + XELOGE( + "D3D12Presenter: Failed to await the guest output capturing fence"); + return false; + } + } + + D3D12_RANGE read_range; + read_range.Begin = copy_dest.PlacedFootprint.Offset; + read_range.End = copy_dest_size; + void* mapping; + if (FAILED(buffer->Map(0, &read_range, &mapping))) { + XELOGE("D3D12Presenter: Failed to map the guest output capture buffer"); + return false; + } + image_out.width = uint32_t(texture_desc.Width); + image_out.height = uint32_t(texture_desc.Height); + image_out.stride = sizeof(uint32_t) * image_out.width; + image_out.data.resize(image_out.stride * image_out.height); + uint32_t* image_out_pixels = + reinterpret_cast(image_out.data.data()); + for (uint32_t y = 0; y < image_out.height; ++y) { + uint32_t* dest_row = &image_out_pixels[size_t(image_out.width) * y]; + const uint32_t* source_row = reinterpret_cast( + reinterpret_cast(mapping) + + copy_dest.PlacedFootprint.Offset + + size_t(copy_dest.PlacedFootprint.Footprint.RowPitch) * y); + for (uint32_t x = 0; x < image_out.width; ++x) { + dest_row[x] = Packed10bpcRGBTo8bpcBytes(source_row[x]); + } + } + // Unmapping will be done implicitly when the resource goes out of scope and + // gets destroyed. + return true; +} + +Presenter::SurfacePaintConnectResult +D3D12Presenter::ConnectOrReconnectPaintingToSurfaceFromUIThread( + Surface& new_surface, uint32_t new_surface_width, + uint32_t new_surface_height, bool was_paintable, + bool& is_vsync_implicit_out) { + uint32_t new_swap_chain_width = std::min( + new_surface_width, uint32_t(D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)); + uint32_t new_swap_chain_height = std::min( + new_surface_height, uint32_t(D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)); + + // ConnectOrReconnectPaintingToSurfaceFromUIThread may be called only for the + // surface of the current swap chain or when the old swap chain has already + // been destroyed, if the surface is the same, try resizing. + if (paint_context_.swap_chain) { + if (was_paintable && + paint_context_.swap_chain_width == new_swap_chain_width && + paint_context_.swap_chain_height == new_swap_chain_height) { + is_vsync_implicit_out = false; + return SurfacePaintConnectResult::kSuccessUnchanged; + } + paint_context_.AwaitSwapChainUsageCompletion(); + // Using the current swap_chain_allows_tearing_ value that's consistent with + // the creation of the swap chain because ResizeBuffers can't toggle the + // tearing flag. + for (Microsoft::WRL::ComPtr& swap_chain_buffer_ref : + paint_context_.swap_chain_buffers) { + swap_chain_buffer_ref.Reset(); + } + bool swap_chain_resized = + SUCCEEDED(paint_context_.swap_chain->ResizeBuffers( + 0, UINT(new_swap_chain_width), UINT(new_swap_chain_height), + DXGI_FORMAT_UNKNOWN, + paint_context_.swap_chain_allows_tearing + ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING + : 0)); + if (swap_chain_resized) { + for (uint32_t i = 0; i < PaintContext::kSwapChainBufferCount; ++i) { + if (FAILED(paint_context_.swap_chain->GetBuffer( + i, IID_PPV_ARGS(&paint_context_.swap_chain_buffers[i])))) { + swap_chain_resized = false; + break; + } + } + if (swap_chain_resized) { + paint_context_.swap_chain_width = new_swap_chain_width; + paint_context_.swap_chain_height = new_swap_chain_height; + } + } + if (!swap_chain_resized) { + XELOGE("D3D12Presenter: Failed to resize a swap chain"); + // Failed to resize, retry creating from scratch. + paint_context_.DestroySwapChain(); + } + } + + if (!paint_context_.swap_chain) { + // Create a new swap chain. + Surface::TypeIndex surface_type = new_surface.GetType(); + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc; + swap_chain_desc.Width = UINT(new_swap_chain_width); + swap_chain_desc.Height = UINT(new_swap_chain_height); + swap_chain_desc.Format = kSwapChainFormat; + swap_chain_desc.Stereo = FALSE; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.BufferCount = UINT(PaintContext::kSwapChainBufferCount); + // DXGI_SCALING_STRETCH may cause the content to "shake" while resizing, + // with relayout done for the guest output twice visually rather than once, + // and the UI becoming stretched and then jumping to normal. If it's + // possible to cover the entire surface without stretching, don't stretch. + // After resizing, the presenter repaints as soon as possible anyway, so + swap_chain_desc.Scaling = (new_swap_chain_width == new_surface_width && + new_swap_chain_height == new_surface_height) + ? DXGI_SCALING_NONE + : DXGI_SCALING_STRETCH; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + swap_chain_desc.Flags = 0; + if (cvars::d3d12_allow_variable_refresh_rate_and_tearing && + dxgi_supports_tearing_) { + // Allow tearing in borderless fullscreen to support variable refresh + // rate. + swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + } + IDXGIFactory2* dxgi_factory = provider_.GetDXGIFactory(); + ID3D12CommandQueue* direct_queue = provider_.GetDirectQueue(); + Microsoft::WRL::ComPtr swap_chain_1; + switch (surface_type) { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_GAMES) + case Surface::kTypeIndex_Win32Hwnd: { + HWND surface_hwnd = + static_cast(new_surface).hwnd(); + if (FAILED(dxgi_factory->CreateSwapChainForHwnd( + direct_queue, surface_hwnd, &swap_chain_desc, nullptr, nullptr, + &swap_chain_1))) { + XELOGE("D3D12Presenter: Failed to create a swap chain for the HWND"); + return SurfacePaintConnectResult::kFailure; + } + // Disable automatic Alt+Enter handling - DXGI fullscreen doesn't + // support ALLOW_TEARING, and using custom fullscreen in ui::Win32Window + // anyway as with Alt+Enter the menu is kept, state changes are tracked + // better, and nothing is presented for some reason. + dxgi_factory->MakeWindowAssociation(surface_hwnd, + DXGI_MWA_NO_ALT_ENTER); + } break; +#endif + default: + assert_unhandled_case(surface_type); + XELOGE( + "D3D12Presenter: Tried to create a swap chain for an unsupported " + "Xenia surface type"); + return SurfacePaintConnectResult::kFailureSurfaceUnusable; + } + if (FAILED(swap_chain_1->QueryInterface( + IID_PPV_ARGS(&paint_context_.swap_chain)))) { + XELOGE( + "D3D12Presenter: Failed to get version 3 of the swap chain " + "interface"); + return SurfacePaintConnectResult::kFailure; + } + // From now on, in case of any failure, DestroySwapChain must be called + // before returning. + paint_context_.swap_chain_width = new_swap_chain_width; + paint_context_.swap_chain_height = new_swap_chain_height; + paint_context_.swap_chain_allows_tearing = + (swap_chain_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) != 0; + for (uint32_t i = 0; i < PaintContext::kSwapChainBufferCount; ++i) { + if (FAILED(paint_context_.swap_chain->GetBuffer( + i, IID_PPV_ARGS(&paint_context_.swap_chain_buffers[i])))) { + XELOGE( + "D3D12Presenter: Failed to get buffer {} of a {}-buffer swap chain", + i, PaintContext::kSwapChainBufferCount); + paint_context_.DestroySwapChain(); + return SurfacePaintConnectResult::kFailure; + } + } + } + + ID3D12Device* device = provider_.GetDevice(); + + // Create the RTV descriptors. + D3D12_CPU_DESCRIPTOR_HANDLE rtv_heap_start = + paint_context_.rtv_heap->GetCPUDescriptorHandleForHeapStart(); + D3D12_RENDER_TARGET_VIEW_DESC rtv_desc; + rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + rtv_desc.Texture2D.MipSlice = 0; + rtv_desc.Texture2D.PlaneSlice = 0; + for (uint32_t i = 0; i < PaintContext::kSwapChainBufferCount; ++i) { + ID3D12Resource* swap_chain_buffer = + paint_context_.swap_chain_buffers[i].Get(); + rtv_desc.Format = kSwapChainFormat; + device->CreateRenderTargetView( + swap_chain_buffer, &rtv_desc, + provider_.OffsetRTVDescriptor( + rtv_heap_start, PaintContext::kRTVIndexSwapChainBuffer0 + i)); + } + + is_vsync_implicit_out = false; + return SurfacePaintConnectResult::kSuccess; +} + +void D3D12Presenter::DisconnectPaintingFromSurfaceFromUIThreadImpl() { + paint_context_.DestroySwapChain(); +} + +bool D3D12Presenter::RefreshGuestOutputImpl( + uint32_t mailbox_index, uint32_t frontbuffer_width, + uint32_t frontbuffer_height, + std::function refresher, + bool& is_8bpc_out_ref) { + assert_not_zero(frontbuffer_width); + assert_not_zero(frontbuffer_height); + std::pair>& + guest_output_resource_ref = guest_output_resources_[mailbox_index]; + if (guest_output_resource_ref.second) { + D3D12_RESOURCE_DESC guest_output_resource_current_desc = + guest_output_resource_ref.second->GetDesc(); + if (guest_output_resource_current_desc.Width != frontbuffer_width || + guest_output_resource_current_desc.Height != frontbuffer_height) { + // Main target painting has its own reference to the textures for reading + // in its own submission tracker timeline, safe to release here. + guest_output_resource_refresher_submission_tracker_ + .AwaitSubmissionCompletion(guest_output_resource_ref.first); + guest_output_resource_ref.second.Reset(); + } + } + if (!guest_output_resource_ref.second) { + ID3D12Device* device = provider_.GetDevice(); + D3D12_RESOURCE_DESC guest_output_resource_new_desc; + guest_output_resource_new_desc.Dimension = + D3D12_RESOURCE_DIMENSION_TEXTURE2D; + guest_output_resource_new_desc.Alignment = 0; + guest_output_resource_new_desc.Width = frontbuffer_width; + guest_output_resource_new_desc.Height = frontbuffer_height; + guest_output_resource_new_desc.DepthOrArraySize = 1; + guest_output_resource_new_desc.MipLevels = 1; + guest_output_resource_new_desc.Format = kGuestOutputFormat; + guest_output_resource_new_desc.SampleDesc.Count = 1; + guest_output_resource_new_desc.SampleDesc.Quality = 0; + guest_output_resource_new_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + guest_output_resource_new_desc.Flags = + D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + if (FAILED(device->CreateCommittedResource( + &util::kHeapPropertiesDefault, + provider_.GetHeapFlagCreateNotZeroed(), + &guest_output_resource_new_desc, kGuestOutputInternalState, nullptr, + IID_PPV_ARGS(&guest_output_resource_ref.second)))) { + XELOGE("D3D12Presenter: Failed to create the guest output {}x{} texture", + frontbuffer_width, frontbuffer_height); + return false; + } + } + D3D12GuestOutputRefreshContext context( + is_8bpc_out_ref, guest_output_resource_ref.second.Get()); + bool refresher_succeeded = refresher(context); + // Even if the refresher has returned false, it still might have submitted + // some commands referencing the resource. It's better to put an excessive + // signal and wait slightly longer, for nothing important, while shutting down + // than to destroy the resource while it's still in use. + guest_output_resource_ref.first = + guest_output_resource_refresher_submission_tracker_ + .GetCurrentSubmission(); + guest_output_resource_refresher_submission_tracker_.NextSubmission(); + return refresher_succeeded; +} + +void D3D12Presenter::PaintContext::DestroySwapChain() { + if (!swap_chain) { + return; + } + AwaitSwapChainUsageCompletion(); + for (Microsoft::WRL::ComPtr& swap_chain_buffer_ref : + swap_chain_buffers) { + swap_chain_buffer_ref.Reset(); + } + swap_chain.Reset(); + swap_chain_allows_tearing = false; + swap_chain_height = 0; + swap_chain_width = 0; +} + +Presenter::PaintResult D3D12Presenter::PaintAndPresentImpl( + bool execute_ui_drawers) { + // Begin the command list with the command allocator not currently potentially + // used on the GPU. + UINT64 current_paint_submission = + paint_context_.paint_submission_tracker.GetCurrentSubmission(); + UINT64 command_allocator_count = + UINT64(paint_context_.command_allocators.size()); + if (current_paint_submission >= command_allocator_count) { + paint_context_.paint_submission_tracker.AwaitSubmissionCompletion( + current_paint_submission - command_allocator_count); + } + ID3D12CommandAllocator* command_allocator = + paint_context_ + .command_allocators[current_paint_submission % + command_allocator_count] + .Get(); + command_allocator->Reset(); + ID3D12GraphicsCommandList* command_list = paint_context_.command_list.Get(); + command_list->Reset(command_allocator, nullptr); + + ID3D12Device* device = provider_.GetDevice(); + + // Obtain the RTV heap and the back buffer. + D3D12_CPU_DESCRIPTOR_HANDLE rtv_heap_start = + paint_context_.rtv_heap->GetCPUDescriptorHandleForHeapStart(); + UINT back_buffer_index = + paint_context_.swap_chain->GetCurrentBackBufferIndex(); + D3D12_CPU_DESCRIPTOR_HANDLE back_buffer_rtv = provider_.OffsetRTVDescriptor( + rtv_heap_start, + PaintContext::kRTVIndexSwapChainBuffer0 + back_buffer_index); + ID3D12Resource* back_buffer = + paint_context_.swap_chain_buffers[back_buffer_index].Get(); + bool back_buffer_acquired = false; + bool back_buffer_bound = false; + bool back_buffer_clear_needed = true; + const float kBackBufferClearColor[] = {0.0f, 0.0f, 0.0f, 1.0f}; + + // Draw the guest output. + + GuestOutputProperties guest_output_properties; + GuestOutputPaintConfig guest_output_paint_config; + Microsoft::WRL::ComPtr guest_output_resource; + { + uint32_t guest_output_mailbox_index; + std::unique_lock guest_output_consumer_lock( + ConsumeGuestOutput(guest_output_mailbox_index, &guest_output_properties, + &guest_output_paint_config)); + if (guest_output_mailbox_index != UINT32_MAX) { + guest_output_resource = + guest_output_resources_[guest_output_mailbox_index].second; + } + // Incremented the reference count of the guest output resource - safe to + // leave the consumer critical section now as everything here either will be + // using the new reference or is exclusively owned by main target painting + // (and multiple threads can't paint the main target at the same time). + } + + if (guest_output_resource) { + GuestOutputPaintFlow guest_output_flow = GetGuestOutputPaintFlow( + guest_output_properties, paint_context_.swap_chain_width, + paint_context_.swap_chain_height, D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, + D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION, guest_output_paint_config); + + // Check if all guest output paint effects are supported by the + // implementation. + if (guest_output_flow.effect_count) { + if (!guest_output_paint_final_pipelines_[size_t( + guest_output_flow.effects[guest_output_flow.effect_count - 1])]) { + guest_output_flow.effect_count = 0; + } + for (size_t i = 0; i + 1 < guest_output_flow.effect_count; ++i) { + if (!guest_output_paint_intermediate_pipelines_[size_t( + guest_output_flow.effects[i])]) { + guest_output_flow.effect_count = 0; + break; + } + } + } + + if (guest_output_flow.effect_count) { + ID3D12DescriptorHeap* view_heap = paint_context_.view_heap.Get(); + D3D12_CPU_DESCRIPTOR_HANDLE view_heap_cpu_start = + view_heap->GetCPUDescriptorHandleForHeapStart(); + + // Store the main target reference to the guest output texture so it's not + // destroyed while it's still potentially in use by main target painting + // queued on the GPU. + size_t guest_output_resource_paint_ref_index = SIZE_MAX; + size_t guest_output_resource_paint_ref_new_index = SIZE_MAX; + // Try to find the existing reference to the same texture, or an already + // released (or a taken, but never actually used) slot. + for (size_t i = 0; + i < paint_context_.guest_output_resource_paint_refs.size(); ++i) { + const std::pair>& + guest_output_resource_paint_ref = + paint_context_.guest_output_resource_paint_refs[i]; + if (guest_output_resource_paint_ref.second == guest_output_resource) { + guest_output_resource_paint_ref_index = i; + break; + } + if (guest_output_resource_paint_ref_new_index == SIZE_MAX && + (!guest_output_resource_paint_ref.second || + !guest_output_resource_paint_ref.first)) { + guest_output_resource_paint_ref_new_index = i; + } + } + if (guest_output_resource_paint_ref_index == SIZE_MAX) { + // New texture - store the reference and create the descriptors. + if (guest_output_resource_paint_ref_new_index == SIZE_MAX) { + // Replace the earliest used reference. + guest_output_resource_paint_ref_new_index = 0; + for (size_t i = 1; + i < paint_context_.guest_output_resource_paint_refs.size(); + ++i) { + if (paint_context_.guest_output_resource_paint_refs[i].first < + paint_context_ + .guest_output_resource_paint_refs + [guest_output_resource_paint_ref_new_index] + .first) { + guest_output_resource_paint_ref_new_index = i; + } + } + // Await the completion of the usage of the old guest output + // resource and its SRV descriptors. + paint_context_.paint_submission_tracker.AwaitSubmissionCompletion( + paint_context_ + .guest_output_resource_paint_refs + [guest_output_resource_paint_ref_new_index] + .first); + } + guest_output_resource_paint_ref_index = + guest_output_resource_paint_ref_new_index; + // The actual submission index will be set if the texture is actually + // used, not dropped due to some error. + paint_context_.guest_output_resource_paint_refs + [guest_output_resource_paint_ref_index] = + std::make_pair(UINT64(0), guest_output_resource); + // Create the SRV descriptor of the new texture. + D3D12_SHADER_RESOURCE_VIEW_DESC guest_output_resource_srv_desc; + guest_output_resource_srv_desc.Format = kGuestOutputFormat; + guest_output_resource_srv_desc.ViewDimension = + D3D12_SRV_DIMENSION_TEXTURE2D; + guest_output_resource_srv_desc.Shader4ComponentMapping = + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + guest_output_resource_srv_desc.Texture2D.MostDetailedMip = 0; + guest_output_resource_srv_desc.Texture2D.MipLevels = 1; + guest_output_resource_srv_desc.Texture2D.PlaneSlice = 0; + guest_output_resource_srv_desc.Texture2D.ResourceMinLODClamp = 0.0f; + device->CreateShaderResourceView( + guest_output_resource.Get(), &guest_output_resource_srv_desc, + provider_.OffsetViewDescriptor( + view_heap_cpu_start, + PaintContext::kViewIndexGuestOutput0Srv + + uint32_t(guest_output_resource_paint_ref_index))); + } + + // Make sure intermediate textures of the needed size are available, and + // unneeded intermediate textures are destroyed. + for (size_t i = 0; i < kMaxGuestOutputPaintEffects - 1; ++i) { + std::pair intermediate_needed_size(0, 0); + if (i + 1 < guest_output_flow.effect_count) { + intermediate_needed_size = guest_output_flow.effect_output_sizes[i]; + } + Microsoft::WRL::ComPtr& intermediate_texture_ptr_ref = + paint_context_.guest_output_intermediate_textures[i]; + std::pair intermediate_current_size(0, 0); + if (intermediate_texture_ptr_ref) { + D3D12_RESOURCE_DESC intermediate_current_desc = + intermediate_texture_ptr_ref->GetDesc(); + intermediate_current_size.first = + uint32_t(intermediate_current_desc.Width); + intermediate_current_size.second = intermediate_current_desc.Height; + } + if (intermediate_current_size != intermediate_needed_size) { + if (intermediate_needed_size.first && + intermediate_needed_size.second) { + // Need to replace immediately as a new texture with the requested + // size is needed. + if (intermediate_texture_ptr_ref) { + paint_context_.paint_submission_tracker.AwaitSubmissionCompletion( + paint_context_.guest_output_intermediate_texture_last_usage); + intermediate_texture_ptr_ref.Reset(); + } + // Resource. + D3D12_RESOURCE_DESC intermediate_desc; + intermediate_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + intermediate_desc.Alignment = 0; + intermediate_desc.Width = intermediate_needed_size.first; + intermediate_desc.Height = intermediate_needed_size.second; + intermediate_desc.DepthOrArraySize = 1; + intermediate_desc.MipLevels = 1; + intermediate_desc.Format = kGuestOutputIntermediateFormat; + intermediate_desc.SampleDesc.Count = 1; + intermediate_desc.SampleDesc.Quality = 0; + intermediate_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + intermediate_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + if (FAILED(device->CreateCommittedResource( + &util::kHeapPropertiesDefault, + provider_.GetHeapFlagCreateNotZeroed(), &intermediate_desc, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, + IID_PPV_ARGS(&intermediate_texture_ptr_ref)))) { + XELOGE( + "D3D12Presenter: Failed to create a guest output " + "presentation intermediate texture"); + // Don't display the guest output, and don't try to create more + // intermediate textures (only destroy them). + guest_output_flow.effect_count = 0; + continue; + } + ID3D12Resource* intermediate_texture = + intermediate_texture_ptr_ref.Get(); + // SRV. + D3D12_SHADER_RESOURCE_VIEW_DESC intermediate_srv_desc; + intermediate_srv_desc.Format = kGuestOutputIntermediateFormat; + intermediate_srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + intermediate_srv_desc.Shader4ComponentMapping = + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + intermediate_srv_desc.Texture2D.MostDetailedMip = 0; + intermediate_srv_desc.Texture2D.MipLevels = 1; + intermediate_srv_desc.Texture2D.PlaneSlice = 0; + intermediate_srv_desc.Texture2D.ResourceMinLODClamp = 0.0f; + device->CreateShaderResourceView( + intermediate_texture, &intermediate_srv_desc, + provider_.OffsetViewDescriptor( + view_heap_cpu_start, + uint32_t( + PaintContext::kViewIndexGuestOutputIntermediate0Srv + + i))); + // RTV. + D3D12_RENDER_TARGET_VIEW_DESC intermediate_rtv_desc; + intermediate_rtv_desc.Format = kGuestOutputIntermediateFormat; + intermediate_rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + intermediate_rtv_desc.Texture2D.MipSlice = 0; + intermediate_rtv_desc.Texture2D.PlaneSlice = 0; + device->CreateRenderTargetView( + intermediate_texture, &intermediate_rtv_desc, + provider_.OffsetRTVDescriptor( + rtv_heap_start, + uint32_t(PaintContext::kRTVIndexGuestOutputIntermediate0 + + i))); + } else { + // Was previously needed, but not anymore - destroy when possible. + if (intermediate_texture_ptr_ref && + paint_context_.paint_submission_tracker + .GetCompletedSubmission() >= + paint_context_ + .guest_output_intermediate_texture_last_usage) { + intermediate_texture_ptr_ref.Reset(); + } + } + } + } + + if (guest_output_flow.effect_count) { + paint_context_ + .guest_output_resource_paint_refs + [guest_output_resource_paint_ref_index] + .first = current_paint_submission; + if (guest_output_flow.effect_count > 1) { + paint_context_.guest_output_intermediate_texture_last_usage = + current_paint_submission; + } + + command_list->SetDescriptorHeaps(1, &view_heap); + } + + // This effect loop must not be aborted so the states of the resources + // involved are consistent. + D3D12_GPU_DESCRIPTOR_HANDLE view_heap_gpu_start = + view_heap->GetGPUDescriptorHandleForHeapStart(); + for (size_t i = 0; i < guest_output_flow.effect_count; ++i) { + bool is_final_effect = i + 1 >= guest_output_flow.effect_count; + + GuestOutputPaintEffect effect = guest_output_flow.effects[i]; + + ID3D12Resource* effect_dest_resource; + int32_t effect_rect_x, effect_rect_y; + if (is_final_effect) { + effect_dest_resource = back_buffer; + if (!back_buffer_acquired) { + D3D12_RESOURCE_BARRIER barrier_present_to_rtv; + barrier_present_to_rtv.Type = + D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_present_to_rtv.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_present_to_rtv.Transition.pResource = back_buffer; + barrier_present_to_rtv.Transition.Subresource = + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier_present_to_rtv.Transition.StateBefore = + D3D12_RESOURCE_STATE_PRESENT; + barrier_present_to_rtv.Transition.StateAfter = + D3D12_RESOURCE_STATE_RENDER_TARGET; + command_list->ResourceBarrier(1, &barrier_present_to_rtv); + back_buffer_acquired = true; + } + effect_rect_x = guest_output_flow.output_x; + effect_rect_y = guest_output_flow.output_y; + } else { + effect_dest_resource = + paint_context_.guest_output_intermediate_textures[i].Get(); + if (!i) { + // If this is not the first effect, the transition has been done at + // the end of the previous effect in a single command. + D3D12_RESOURCE_BARRIER barrier_srv_to_rtv; + barrier_srv_to_rtv.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_srv_to_rtv.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_srv_to_rtv.Transition.pResource = effect_dest_resource; + barrier_srv_to_rtv.Transition.Subresource = + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier_srv_to_rtv.Transition.StateBefore = + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + barrier_srv_to_rtv.Transition.StateAfter = + D3D12_RESOURCE_STATE_RENDER_TARGET; + command_list->ResourceBarrier(1, &barrier_srv_to_rtv); + } + command_list->DiscardResource(effect_dest_resource, nullptr); + effect_rect_x = 0; + effect_rect_y = 0; + } + + if (is_final_effect) { + if (!back_buffer_bound) { + command_list->OMSetRenderTargets(1, &back_buffer_rtv, TRUE, + nullptr); + back_buffer_bound = true; + } + } else { + D3D12_CPU_DESCRIPTOR_HANDLE intermediate_rtv = + provider_.OffsetRTVDescriptor( + rtv_heap_start, + uint32_t(PaintContext::kRTVIndexGuestOutputIntermediate0 + + i)); + command_list->OMSetRenderTargets(1, &intermediate_rtv, TRUE, nullptr); + back_buffer_bound = false; + } + if (is_final_effect) { + back_buffer_bound = true; + } + D3D12_RESOURCE_DESC effect_dest_resource_desc = + effect_dest_resource->GetDesc(); + D3D12_VIEWPORT viewport; + viewport.TopLeftX = 0.0f; + viewport.TopLeftY = 0.0f; + viewport.Width = float(effect_dest_resource_desc.Width); + viewport.Height = float(effect_dest_resource_desc.Height); + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + command_list->RSSetViewports(1, &viewport); + D3D12_RECT scissor; + scissor.left = 0; + scissor.top = 0; + scissor.right = LONG(effect_dest_resource_desc.Width); + scissor.bottom = LONG(effect_dest_resource_desc.Height); + command_list->RSSetScissorRects(1, &scissor); + + command_list->SetPipelineState( + is_final_effect + ? guest_output_paint_final_pipelines_[size_t(effect)].Get() + : guest_output_paint_intermediate_pipelines_[size_t(effect)] + .Get()); + GuestOutputPaintRootSignatureIndex + guest_output_paint_root_signature_index = + GetGuestOutputPaintRootSignatureIndex(effect); + command_list->SetGraphicsRootSignature( + guest_output_paint_root_signatures_ + [size_t(guest_output_paint_root_signature_index)] + .Get()); + + UINT effect_src_view_index = UINT( + i ? (PaintContext::kViewIndexGuestOutputIntermediate0Srv + (i - 1)) + : (PaintContext::kViewIndexGuestOutput0Srv + + guest_output_resource_paint_ref_index)); + command_list->SetGraphicsRootDescriptorTable( + UINT(GuestOutputPaintRootParameter::kSource), + provider_.OffsetViewDescriptor(view_heap_gpu_start, + effect_src_view_index)); + + GuestOutputPaintRectangleConstants effect_rect_constants; + float effect_x_to_ndc = 2.0f / viewport.Width; + float effect_y_to_ndc = 2.0f / viewport.Height; + effect_rect_constants.x = + -1.0f + float(effect_rect_x) * effect_x_to_ndc; + // +Y is -V. + effect_rect_constants.y = 1.0f - float(effect_rect_y) * effect_y_to_ndc; + effect_rect_constants.width = + float(guest_output_flow.effect_output_sizes[i].first) * + effect_x_to_ndc; + effect_rect_constants.height = + -float(guest_output_flow.effect_output_sizes[i].second) * + effect_y_to_ndc; + command_list->SetGraphicsRoot32BitConstants( + UINT(GuestOutputPaintRootParameter::kRectangle), + sizeof(effect_rect_constants) / sizeof(uint32_t), + &effect_rect_constants, 0); + + UINT effect_constants_size = 0; + union { + BilinearConstants bilinear; + CasSharpenConstants cas_sharpen; + CasResampleConstants cas_resample; + FsrEasuConstants fsr_easu; + FsrRcasConstants fsr_rcas; + } effect_constants; + switch (guest_output_paint_root_signature_index) { + case kGuestOutputPaintRootSignatureIndexBilinear: { + effect_constants_size = sizeof(effect_constants.bilinear); + effect_constants.bilinear.Initialize(guest_output_flow, i); + } break; + case kGuestOutputPaintRootSignatureIndexCasSharpen: { + effect_constants_size = sizeof(effect_constants.cas_sharpen); + effect_constants.cas_sharpen.Initialize(guest_output_flow, i, + guest_output_paint_config); + } break; + case kGuestOutputPaintRootSignatureIndexCasResample: { + effect_constants_size = sizeof(effect_constants.cas_resample); + effect_constants.cas_resample.Initialize(guest_output_flow, i, + guest_output_paint_config); + } break; + case kGuestOutputPaintRootSignatureIndexFsrEasu: { + effect_constants_size = sizeof(effect_constants.fsr_easu); + effect_constants.fsr_easu.Initialize(guest_output_flow, i); + } break; + case kGuestOutputPaintRootSignatureIndexFsrRcas: { + effect_constants_size = sizeof(effect_constants.fsr_rcas); + effect_constants.fsr_rcas.Initialize(guest_output_flow, i, + guest_output_paint_config); + } break; + default: + break; + } + if (effect_constants_size) { + command_list->SetGraphicsRoot32BitConstants( + UINT(GuestOutputPaintRootParameter::kEffectConstants), + effect_constants_size / sizeof(uint32_t), &effect_constants, 0); + } + + command_list->IASetPrimitiveTopology( + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + command_list->DrawInstanced(4, 1, 0, 0); + + if (is_final_effect) { + // Clear the letterbox around the guest output if the guest output + // doesn't cover the entire back buffer. + if (guest_output_flow.letterbox_clear_rectangle_count) { + D3D12_RECT letterbox_clear_d3d12_rectangles + [GuestOutputPaintFlow::kMaxClearRectangles]; + for (size_t i = 0; + i < guest_output_flow.letterbox_clear_rectangle_count; ++i) { + D3D12_RECT& letterbox_clear_d3d12_rectangle = + letterbox_clear_d3d12_rectangles[i]; + const GuestOutputPaintFlow::ClearRectangle& + letterbox_clear_rectangle = + guest_output_flow.letterbox_clear_rectangles[i]; + letterbox_clear_d3d12_rectangle.left = + LONG(letterbox_clear_rectangle.x); + letterbox_clear_d3d12_rectangle.top = + LONG(letterbox_clear_rectangle.y); + letterbox_clear_d3d12_rectangle.right = + LONG(letterbox_clear_rectangle.x + + letterbox_clear_rectangle.width); + letterbox_clear_d3d12_rectangle.bottom = + LONG(letterbox_clear_rectangle.y + + letterbox_clear_rectangle.height); + } + command_list->ClearRenderTargetView( + back_buffer_rtv, kBackBufferClearColor, + UINT(guest_output_flow.letterbox_clear_rectangle_count), + letterbox_clear_d3d12_rectangles); + } + back_buffer_clear_needed = false; + } else { + D3D12_RESOURCE_BARRIER barriers[2]; + UINT barrier_count = 0; + // Transition the newly written intermediate image to SRV for use as + // the source in the next effect. + { + assert_true(barrier_count < xe::countof(barriers)); + D3D12_RESOURCE_BARRIER& barrier_rtv_to_srv = + barriers[barrier_count++]; + barrier_rtv_to_srv.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_rtv_to_srv.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_rtv_to_srv.Transition.pResource = effect_dest_resource; + barrier_rtv_to_srv.Transition.Subresource = + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier_rtv_to_srv.Transition.StateBefore = + D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier_rtv_to_srv.Transition.StateAfter = + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + } + // Merge the current destination > next source transition with the + // acquisition of the destination for the next effect. + if (i + 2 < guest_output_flow.effect_count) { + // The next effect won't be the last - transition the next + // intermediate destination to RTV. + assert_true(barrier_count < xe::countof(barriers)); + D3D12_RESOURCE_BARRIER& barrier_srv_to_rtv = + barriers[barrier_count++]; + barrier_srv_to_rtv.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_srv_to_rtv.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_srv_to_rtv.Transition.pResource = + paint_context_.guest_output_intermediate_textures[i + 1].Get(); + barrier_srv_to_rtv.Transition.Subresource = + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier_srv_to_rtv.Transition.StateBefore = + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + barrier_srv_to_rtv.Transition.StateAfter = + D3D12_RESOURCE_STATE_RENDER_TARGET; + } else { + // The next effect draws to the back buffer - merge into one + // ResourceBarrier command. + if (!back_buffer_acquired) { + assert_true(barrier_count < xe::countof(barriers)); + D3D12_RESOURCE_BARRIER& barrier_present_to_rtv = + barriers[barrier_count++]; + barrier_present_to_rtv.Type = + D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_present_to_rtv.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_present_to_rtv.Transition.pResource = back_buffer; + barrier_present_to_rtv.Transition.Subresource = + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier_present_to_rtv.Transition.StateBefore = + D3D12_RESOURCE_STATE_PRESENT; + barrier_present_to_rtv.Transition.StateAfter = + D3D12_RESOURCE_STATE_RENDER_TARGET; + back_buffer_acquired = true; + } + } + if (barrier_count) { + command_list->ResourceBarrier(barrier_count, barriers); + } + } + } + } + } + + // Release main target guest output texture references that aren't needed + // anymore (this is done after various potential guest-output-related main + // target submission tracker waits so the completed submission value is the + // most actual). + UINT64 completed_paint_submission = + paint_context_.paint_submission_tracker.GetCompletedSubmission(); + for (std::pair>& + guest_output_resource_paint_ref : + paint_context_.guest_output_resource_paint_refs) { + if (!guest_output_resource_paint_ref.second || + guest_output_resource_paint_ref.second == guest_output_resource) { + continue; + } + if (completed_paint_submission >= guest_output_resource_paint_ref.first) { + guest_output_resource_paint_ref.second.Reset(); + } + } + + // If no guest output has been drawn, the transitioned of the back buffer to + // RTV hasn't been done yet, and it's needed to clear it, and optionally to + // draw the UI. + if (!back_buffer_acquired) { + D3D12_RESOURCE_BARRIER barrier_present_to_rtv; + barrier_present_to_rtv.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_present_to_rtv.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_present_to_rtv.Transition.pResource = back_buffer; + barrier_present_to_rtv.Transition.Subresource = + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier_present_to_rtv.Transition.StateBefore = + D3D12_RESOURCE_STATE_PRESENT; + barrier_present_to_rtv.Transition.StateAfter = + D3D12_RESOURCE_STATE_RENDER_TARGET; + command_list->ResourceBarrier(1, &barrier_present_to_rtv); + back_buffer_acquired = true; + } + + if (back_buffer_clear_needed) { + command_list->ClearRenderTargetView(back_buffer_rtv, kBackBufferClearColor, + 0, nullptr); + back_buffer_clear_needed = false; + } + + if (execute_ui_drawers) { + // Draw the UI. + if (!back_buffer_bound) { + command_list->OMSetRenderTargets(1, &back_buffer_rtv, TRUE, nullptr); + back_buffer_bound = true; + } + D3D12UIDrawContext ui_draw_context( + *this, paint_context_.swap_chain_width, + paint_context_.swap_chain_height, command_list, + ui_submission_tracker_.GetCurrentSubmission(), + ui_submission_tracker_.GetCompletedSubmission()); + ExecuteUIDrawersFromUIThread(ui_draw_context); + } + + // End drawing to the back buffer. + D3D12_RESOURCE_BARRIER barrier_rtv_to_present; + barrier_rtv_to_present.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier_rtv_to_present.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier_rtv_to_present.Transition.pResource = back_buffer; + barrier_rtv_to_present.Transition.Subresource = + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier_rtv_to_present.Transition.StateBefore = + D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier_rtv_to_present.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + command_list->ResourceBarrier(1, &barrier_rtv_to_present); + + // Execute and present. + command_list->Close(); + ID3D12CommandList* execute_command_list = command_list; + provider_.GetDirectQueue()->ExecuteCommandLists(1, &execute_command_list); + if (execute_ui_drawers) { + ui_submission_tracker_.NextSubmission(); + } + paint_context_.paint_submission_tracker.NextSubmission(); + // Present as soon as possible, without waiting for vsync (the host refresh + // rate may be something like 144 Hz, which is not a multiple of the common + // 30 Hz or 60 Hz guest refresh rate), and allowing dropping outdated queued + // frames for lower latency. Also, if possible, allowing tearing to use + // variable refresh rate in borderless fullscreen (note that if DXGI + // fullscreen is ever used in, the allow tearing flag must not be passed in + // fullscreen, but DXGI fullscreen is largely unneeded with the flip + // presentation model used in Direct3D 12). + HRESULT present_result = paint_context_.swap_chain->Present( + 0, DXGI_PRESENT_RESTART | (paint_context_.swap_chain_allows_tearing + ? DXGI_PRESENT_ALLOW_TEARING + : 0)); + // Even if presentation has failed, work might have been enqueued anyway + // internally before the failure according to Jesse Natalie from the DirectX + // Discord server. + paint_context_.present_submission_tracker.NextSubmission(); + switch (present_result) { + case DXGI_ERROR_DEVICE_REMOVED: + return PaintResult::kGpuLostExternally; + case DXGI_ERROR_DEVICE_RESET: + return PaintResult::kGpuLostResponsible; + default: + return SUCCEEDED(present_result) ? PaintResult::kPresented + : PaintResult::kNotPresented; + } +} + +bool D3D12Presenter::InitializeSurfaceIndependent() { + // Check if DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING is supported. + { + Microsoft::WRL::ComPtr dxgi_factory_5; + if (SUCCEEDED(provider_.GetDXGIFactory()->QueryInterface( + IID_PPV_ARGS(&dxgi_factory_5)))) { + BOOL tearing_feature_data; + dxgi_supports_tearing_ = + SUCCEEDED(dxgi_factory_5->CheckFeatureSupport( + DXGI_FEATURE_PRESENT_ALLOW_TEARING, &tearing_feature_data, + sizeof(tearing_feature_data))) && + tearing_feature_data; + } + } + + ID3D12Device* device = provider_.GetDevice(); + + // Initialize static guest output painting objects. + + // Guest output painting root signatures. + // One (texture) for bilinear, two (texture and constants) for AMD FidelityFX + // CAS and FSR. + D3D12_ROOT_PARAMETER guest_output_paint_root_parameters[UINT( + GuestOutputPaintRootParameter::kCount)]; + // Source texture. + D3D12_DESCRIPTOR_RANGE guest_output_paint_root_descriptor_range_source; + guest_output_paint_root_descriptor_range_source.RangeType = + D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + guest_output_paint_root_descriptor_range_source.NumDescriptors = 1; + guest_output_paint_root_descriptor_range_source.BaseShaderRegister = 0; + guest_output_paint_root_descriptor_range_source.RegisterSpace = 0; + guest_output_paint_root_descriptor_range_source + .OffsetInDescriptorsFromTableStart = 0; + { + D3D12_ROOT_PARAMETER& guest_output_paint_root_parameter_source = + guest_output_paint_root_parameters[UINT( + GuestOutputPaintRootParameter::kSource)]; + guest_output_paint_root_parameter_source.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + guest_output_paint_root_parameter_source.DescriptorTable + .NumDescriptorRanges = 1; + guest_output_paint_root_parameter_source.DescriptorTable.pDescriptorRanges = + &guest_output_paint_root_descriptor_range_source; + guest_output_paint_root_parameter_source.ShaderVisibility = + D3D12_SHADER_VISIBILITY_PIXEL; + } + // Rectangle. + { + D3D12_ROOT_PARAMETER& guest_output_paint_root_parameter_rect = + guest_output_paint_root_parameters[UINT( + GuestOutputPaintRootParameter::kRectangle)]; + guest_output_paint_root_parameter_rect.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + guest_output_paint_root_parameter_rect.Constants.ShaderRegister = 0; + guest_output_paint_root_parameter_rect.Constants.RegisterSpace = 0; + guest_output_paint_root_parameter_rect.Constants.Num32BitValues = + sizeof(GuestOutputPaintRectangleConstants) / sizeof(uint32_t); + guest_output_paint_root_parameter_rect.ShaderVisibility = + D3D12_SHADER_VISIBILITY_VERTEX; + } + // Pixel shader constants. + D3D12_ROOT_PARAMETER& guest_output_paint_root_parameter_effect_constants = + guest_output_paint_root_parameters[UINT( + GuestOutputPaintRootParameter::kEffectConstants)]; + guest_output_paint_root_parameter_effect_constants.ParameterType = + D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + guest_output_paint_root_parameter_effect_constants.Constants.ShaderRegister = + 0; + guest_output_paint_root_parameter_effect_constants.Constants.RegisterSpace = + 0; + guest_output_paint_root_parameter_effect_constants.ShaderVisibility = + D3D12_SHADER_VISIBILITY_PIXEL; + // Bilinear sampler. + D3D12_STATIC_SAMPLER_DESC guest_output_paint_root_sampler; + guest_output_paint_root_sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + guest_output_paint_root_sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + guest_output_paint_root_sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + guest_output_paint_root_sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + guest_output_paint_root_sampler.MipLODBias = 0.0f; + guest_output_paint_root_sampler.MaxAnisotropy = 1; + guest_output_paint_root_sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + guest_output_paint_root_sampler.BorderColor = + D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; + guest_output_paint_root_sampler.MinLOD = 0.0f; + guest_output_paint_root_sampler.MaxLOD = 0.0f; + guest_output_paint_root_sampler.ShaderRegister = 0; + guest_output_paint_root_sampler.RegisterSpace = 0; + guest_output_paint_root_sampler.ShaderVisibility = + D3D12_SHADER_VISIBILITY_PIXEL; + D3D12_ROOT_SIGNATURE_DESC guest_output_paint_root_signature_desc; + guest_output_paint_root_signature_desc.NumParameters = + UINT(GuestOutputPaintRootParameter::kCount); + guest_output_paint_root_signature_desc.pParameters = + guest_output_paint_root_parameters; + guest_output_paint_root_signature_desc.NumStaticSamplers = 1; + guest_output_paint_root_signature_desc.pStaticSamplers = + &guest_output_paint_root_sampler; + guest_output_paint_root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; + // Bilinear filtering (needs the sampler). + guest_output_paint_root_parameter_effect_constants.Constants.Num32BitValues = + sizeof(BilinearConstants) / sizeof(uint32_t); + { + ID3D12RootSignature* guest_output_paint_root_signature = + util::CreateRootSignature(provider_, + guest_output_paint_root_signature_desc); + if (!guest_output_paint_root_signature) { + XELOGE( + "D3D12Presenter: Failed to create the guest output bilinear " + "filtering presentation root signature"); + return false; + } + *(guest_output_paint_root_signatures_ + [kGuestOutputPaintRootSignatureIndexBilinear] + .ReleaseAndGetAddressOf()) = guest_output_paint_root_signature; + } + // EASU (needs the sampler). + guest_output_paint_root_parameter_effect_constants.Constants.Num32BitValues = + sizeof(FsrEasuConstants) / sizeof(uint32_t); + { + ID3D12RootSignature* guest_output_paint_root_signature = + util::CreateRootSignature(provider_, + guest_output_paint_root_signature_desc); + if (!guest_output_paint_root_signature) { + XELOGE( + "D3D12Presenter: Failed to create the guest output AMD FidelityFX " + "FSR EASU presentation root signature"); + return false; + } + *(guest_output_paint_root_signatures_ + [kGuestOutputPaintRootSignatureIndexFsrEasu] + .ReleaseAndGetAddressOf()) = guest_output_paint_root_signature; + } + // RCAS and CAS don't need the sampler. + guest_output_paint_root_signature_desc.NumStaticSamplers = 0; + // RCAS. + guest_output_paint_root_parameter_effect_constants.Constants.Num32BitValues = + sizeof(FsrRcasConstants) / sizeof(uint32_t); + { + ID3D12RootSignature* guest_output_paint_root_signature = + util::CreateRootSignature(provider_, + guest_output_paint_root_signature_desc); + if (!guest_output_paint_root_signature) { + XELOGE( + "D3D12Presenter: Failed to create the guest output AMD FidelityFX " + "FSR RCAS presentation root signature"); + return false; + } + *(guest_output_paint_root_signatures_ + [kGuestOutputPaintRootSignatureIndexFsrRcas] + .ReleaseAndGetAddressOf()) = guest_output_paint_root_signature; + } + // CAS, sharpening only. + guest_output_paint_root_parameter_effect_constants.Constants.Num32BitValues = + sizeof(CasSharpenConstants) / sizeof(uint32_t); + { + ID3D12RootSignature* guest_output_paint_root_signature = + util::CreateRootSignature(provider_, + guest_output_paint_root_signature_desc); + if (!guest_output_paint_root_signature) { + XELOGE( + "D3D12Presenter: Failed to create the guest output AMD FidelityFX " + "CAS presentation root signature"); + return false; + } + *(guest_output_paint_root_signatures_ + [kGuestOutputPaintRootSignatureIndexCasSharpen] + .ReleaseAndGetAddressOf()) = guest_output_paint_root_signature; + } + // CAS, resampling. + guest_output_paint_root_parameter_effect_constants.Constants.Num32BitValues = + sizeof(CasResampleConstants) / sizeof(uint32_t); + { + ID3D12RootSignature* guest_output_paint_root_signature = + util::CreateRootSignature(provider_, + guest_output_paint_root_signature_desc); + if (!guest_output_paint_root_signature) { + XELOGE( + "D3D12Presenter: Failed to create the guest output resampling AMD " + "FidelityFX CAS presentation root signature"); + return false; + } + *(guest_output_paint_root_signatures_ + [kGuestOutputPaintRootSignatureIndexCasResample] + .ReleaseAndGetAddressOf()) = guest_output_paint_root_signature; + } + + // Guest output painting pipelines. + D3D12_GRAPHICS_PIPELINE_STATE_DESC guest_output_paint_pipeline_desc = {}; + guest_output_paint_pipeline_desc.VS.pShaderBytecode = + shaders::guest_output_triangle_strip_rect_vs; + guest_output_paint_pipeline_desc.VS.BytecodeLength = + sizeof(shaders::guest_output_triangle_strip_rect_vs); + guest_output_paint_pipeline_desc.BlendState.RenderTarget[0] + .RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + guest_output_paint_pipeline_desc.SampleMask = UINT_MAX; + guest_output_paint_pipeline_desc.RasterizerState.FillMode = + D3D12_FILL_MODE_SOLID; + guest_output_paint_pipeline_desc.RasterizerState.CullMode = + D3D12_CULL_MODE_NONE; + guest_output_paint_pipeline_desc.RasterizerState.DepthClipEnable = TRUE; + guest_output_paint_pipeline_desc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + guest_output_paint_pipeline_desc.NumRenderTargets = 1; + guest_output_paint_pipeline_desc.SampleDesc.Count = 1; + for (size_t i = 0; i < size_t(GuestOutputPaintEffect::kCount); ++i) { + GuestOutputPaintEffect guest_output_paint_effect = + GuestOutputPaintEffect(i); + switch (guest_output_paint_effect) { + case GuestOutputPaintEffect::kBilinear: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_bilinear_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_bilinear_ps); + break; + case GuestOutputPaintEffect::kBilinearDither: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_bilinear_dither_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_bilinear_dither_ps); + break; + case GuestOutputPaintEffect::kCasSharpen: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_ffx_cas_sharpen_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_ffx_cas_sharpen_ps); + break; + case GuestOutputPaintEffect::kCasSharpenDither: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_ffx_cas_sharpen_dither_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_ffx_cas_sharpen_dither_ps); + break; + case GuestOutputPaintEffect::kCasResample: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_ffx_cas_resample_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_ffx_cas_resample_ps); + break; + case GuestOutputPaintEffect::kCasResampleDither: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_ffx_cas_resample_dither_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_ffx_cas_resample_dither_ps); + break; + case GuestOutputPaintEffect::kFsrEasu: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_ffx_fsr_easu_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_ffx_fsr_easu_ps); + break; + case GuestOutputPaintEffect::kFsrRcas: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_ffx_fsr_rcas_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_ffx_fsr_rcas_ps); + break; + case GuestOutputPaintEffect::kFsrRcasDither: + guest_output_paint_pipeline_desc.PS.pShaderBytecode = + shaders::guest_output_ffx_fsr_rcas_dither_ps; + guest_output_paint_pipeline_desc.PS.BytecodeLength = + sizeof(shaders::guest_output_ffx_fsr_rcas_dither_ps); + break; + default: + // Not supported by this implementation. + continue; + } + guest_output_paint_pipeline_desc.pRootSignature = + guest_output_paint_root_signatures_ + [GetGuestOutputPaintRootSignatureIndex(guest_output_paint_effect)] + .Get(); + if (CanGuestOutputPaintEffectBeIntermediate(guest_output_paint_effect)) { + guest_output_paint_pipeline_desc.RTVFormats[0] = + kGuestOutputIntermediateFormat; + if (FAILED(device->CreateGraphicsPipelineState( + &guest_output_paint_pipeline_desc, + IID_PPV_ARGS(&guest_output_paint_intermediate_pipelines_[i])))) { + XELOGE( + "D3D12Presenter: Failed to create the guest output painting " + "pipeline for effect {} writing to an intermediate texture", + i); + return false; + } + } + if (CanGuestOutputPaintEffectBeFinal(guest_output_paint_effect)) { + guest_output_paint_pipeline_desc.RTVFormats[0] = kSwapChainFormat; + if (FAILED(device->CreateGraphicsPipelineState( + &guest_output_paint_pipeline_desc, + IID_PPV_ARGS(&guest_output_paint_final_pipelines_[i])))) { + XELOGE( + "D3D12Presenter: Failed to create the guest output painting " + "pipeline for effect {} writing to a swap chain buffer", + i); + return false; + } + } + } + + // Initialize connection-independent parts of the painting context. + + ID3D12CommandQueue* direct_queue = provider_.GetDirectQueue(); + + // Paint submission trackers. + if (!paint_context_.paint_submission_tracker.Initialize(device, + direct_queue) || + !paint_context_.present_submission_tracker.Initialize(device, + direct_queue)) { + return false; + } + + // Paint command allocators and command list. + for (Microsoft::WRL::ComPtr& + paint_command_allocator_ref : paint_context_.command_allocators) { + if (FAILED(device->CreateCommandAllocator( + D3D12_COMMAND_LIST_TYPE_DIRECT, + IID_PPV_ARGS(&paint_command_allocator_ref)))) { + XELOGE( + "D3D12Presenter: Failed to create a command allocator for drawing to " + "a swap chain"); + return false; + } + } + if (FAILED(device->CreateCommandList( + 0, D3D12_COMMAND_LIST_TYPE_DIRECT, + paint_context_.command_allocators[0].Get(), nullptr, + IID_PPV_ARGS(&paint_context_.command_list)))) { + XELOGE( + "D3D12Presenter: Failed to create the command list for drawing to a " + "swap chain"); + return false; + } + // Command lists are created in an open state. + paint_context_.command_list->Close(); + + // RTV descriptor heap. + D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc; + rtv_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + rtv_heap_desc.NumDescriptors = PaintContext::kRTVCount; + rtv_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + rtv_heap_desc.NodeMask = 0; + if (FAILED(device->CreateDescriptorHeap( + &rtv_heap_desc, IID_PPV_ARGS(&paint_context_.rtv_heap)))) { + XELOGE( + "D3D12Presenter: Failed to create an RTV descriptor heap with {} " + "descriptors", + rtv_heap_desc.NumDescriptors); + return false; + } + + // CBV/SRV/UAV descriptor heap. + D3D12_DESCRIPTOR_HEAP_DESC view_heap_desc; + view_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + view_heap_desc.NumDescriptors = PaintContext::kViewCount; + view_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + view_heap_desc.NodeMask = 0; + if (FAILED(device->CreateDescriptorHeap( + &view_heap_desc, IID_PPV_ARGS(&paint_context_.view_heap)))) { + XELOGE( + "D3D12Presenter: Failed to create a shader-visible CBV/SRV/UAV " + "descriptor heap with {} descriptors", + view_heap_desc.NumDescriptors); + return false; + } + + if (!guest_output_resource_refresher_submission_tracker_.Initialize( + device, direct_queue)) { + return false; + } + + if (!ui_submission_tracker_.Initialize(device, direct_queue)) { + return false; + } + + return InitializeCommonSurfaceIndependent(); +} + +} // namespace d3d12 +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/d3d12/d3d12_presenter.h b/src/xenia/ui/d3d12/d3d12_presenter.h new file mode 100644 index 000000000..81d9c4ff5 --- /dev/null +++ b/src/xenia/ui/d3d12/d3d12_presenter.h @@ -0,0 +1,332 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_D3D12_D3D12_PRESENTER_H_ +#define XENIA_UI_D3D12_D3D12_PRESENTER_H_ + +#include +#include +#include + +#include "xenia/base/math.h" +#include "xenia/ui/d3d12/d3d12_provider.h" +#include "xenia/ui/d3d12/d3d12_submission_tracker.h" +#include "xenia/ui/presenter.h" +#include "xenia/ui/surface.h" + +namespace xe { +namespace ui { +namespace d3d12 { + +class D3D12UIDrawContext final : public UIDrawContext { + public: + D3D12UIDrawContext(Presenter& presenter, uint32_t render_target_width, + uint32_t render_target_height, + ID3D12GraphicsCommandList* command_list, + UINT64 submission_index_current, + UINT64 submission_index_completed) + : UIDrawContext(presenter, render_target_width, render_target_height), + command_list_(command_list), + submission_index_current_(submission_index_current), + submission_index_completed_(submission_index_completed) {} + + ID3D12GraphicsCommandList* command_list() const { + return command_list_.Get(); + } + UINT64 submission_index_current() const { return submission_index_current_; } + UINT64 submission_index_completed() const { + return submission_index_completed_; + } + + private: + Microsoft::WRL::ComPtr command_list_; + UINT64 submission_index_current_; + UINT64 submission_index_completed_; +}; + +class D3D12Presenter final : public Presenter { + public: + static constexpr DXGI_FORMAT kGuestOutputFormat = + DXGI_FORMAT_R10G10B10A2_UNORM; + static constexpr D3D12_RESOURCE_STATES kGuestOutputInternalState = + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + static constexpr DXGI_FORMAT kGuestOutputIntermediateFormat = + DXGI_FORMAT_R10G10B10A2_UNORM; + + // The format used internally by Windows composition. + static constexpr DXGI_FORMAT kSwapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM; + + // The callback must use the main direct queue of the provider. + class D3D12GuestOutputRefreshContext final + : public GuestOutputRefreshContext { + public: + D3D12GuestOutputRefreshContext(bool& is_8bpc_out_ref, + ID3D12Resource* resource) + : GuestOutputRefreshContext(is_8bpc_out_ref), resource_(resource) {} + + // kGuestOutputFormat, supports UAV. The initial state in the callback is + // kGuestOutputInternalState, and the callback must also transition it back + // to kGuestOutputInternalState before finishing. + ID3D12Resource* resource_uav_capable() const { return resource_.Get(); } + + private: + Microsoft::WRL::ComPtr resource_; + }; + + static std::unique_ptr Create( + HostGpuLossCallback host_gpu_loss_callback, + const D3D12Provider& provider) { + auto presenter = std::unique_ptr( + new D3D12Presenter(host_gpu_loss_callback, provider)); + if (!presenter->InitializeSurfaceIndependent()) { + return nullptr; + } + return presenter; + } + + ~D3D12Presenter(); + + const D3D12Provider& provider() const { return provider_; } + + Surface::TypeFlags GetSupportedSurfaceTypes() const override; + + bool CaptureGuestOutput(RawImage& image_out) override; + + void AwaitUISubmissionCompletionFromUIThread(UINT64 submission_index) { + ui_submission_tracker_.AwaitSubmissionCompletion(submission_index); + } + + protected: + SurfacePaintConnectResult ConnectOrReconnectPaintingToSurfaceFromUIThread( + Surface& new_surface, uint32_t new_surface_width, + uint32_t new_surface_height, bool was_paintable, + bool& is_vsync_implicit_out) override; + void DisconnectPaintingFromSurfaceFromUIThreadImpl() override; + + bool RefreshGuestOutputImpl( + uint32_t mailbox_index, uint32_t frontbuffer_width, + uint32_t frontbuffer_height, + std::function refresher, + bool& is_8bpc_out) override; + + PaintResult PaintAndPresentImpl(bool execute_ui_drawers) override; + + private: + struct GuestOutputPaintRectangleConstants { + union { + struct { + float x; + float y; + }; + float offset[2]; + }; + union { + struct { + float width; + float height; + }; + float size[2]; + }; + }; + + enum class GuestOutputPaintRootParameter : UINT { + kSource, + kRectangle, + kEffectConstants, + + kCount, + }; + + enum GuestOutputPaintRootSignatureIndex : size_t { + kGuestOutputPaintRootSignatureIndexBilinear, + kGuestOutputPaintRootSignatureIndexCasSharpen, + kGuestOutputPaintRootSignatureIndexCasResample, + kGuestOutputPaintRootSignatureIndexFsrEasu, + kGuestOutputPaintRootSignatureIndexFsrRcas, + + kGuestOutputPaintRootSignatureCount, + }; + + static constexpr GuestOutputPaintRootSignatureIndex + GetGuestOutputPaintRootSignatureIndex(GuestOutputPaintEffect effect) { + switch (effect) { + case GuestOutputPaintEffect::kBilinear: + case GuestOutputPaintEffect::kBilinearDither: + return kGuestOutputPaintRootSignatureIndexBilinear; + case GuestOutputPaintEffect::kCasSharpen: + case GuestOutputPaintEffect::kCasSharpenDither: + return kGuestOutputPaintRootSignatureIndexCasSharpen; + case GuestOutputPaintEffect::kCasResample: + case GuestOutputPaintEffect::kCasResampleDither: + return kGuestOutputPaintRootSignatureIndexCasResample; + case GuestOutputPaintEffect::kFsrEasu: + return kGuestOutputPaintRootSignatureIndexFsrEasu; + case GuestOutputPaintEffect::kFsrRcas: + case GuestOutputPaintEffect::kFsrRcasDither: + return kGuestOutputPaintRootSignatureIndexFsrRcas; + default: + assert_unhandled_case(effect); + return kGuestOutputPaintRootSignatureCount; + } + } + + struct PaintContext { + explicit PaintContext() = default; + PaintContext(const PaintContext& paint_context) = delete; + PaintContext& operator=(const PaintContext& paint_context) = delete; + + static constexpr uint32_t kSwapChainBufferCount = 3; + + enum RTVIndex : UINT { + // Swap chain buffers - updated when creating the swap chain + // (connection-specific). + kRTVIndexSwapChainBuffer0, + + // Intermediate textures - the last usage is + // guest_output_intermediate_texture_paint_last_usage_. + kRTVIndexGuestOutputIntermediate0 = + kRTVIndexSwapChainBuffer0 + kSwapChainBufferCount, + + kRTVCount = + kRTVIndexGuestOutputIntermediate0 + kGuestOutputMailboxSize - 1, + }; + + enum ViewIndex : UINT { + // Guest output textures - indices are the same as in + // guest_output_resource_paint_refs, and the last usage is tied to them. + kViewIndexGuestOutput0Srv, + + // Intermediate textures - the last usage is + // guest_output_intermediate_texture_paint_last_usage_. + kViewIndexGuestOutputIntermediate0Srv = + kViewIndexGuestOutput0Srv + kGuestOutputMailboxSize, + + kViewCount = kViewIndexGuestOutputIntermediate0Srv + + kMaxGuestOutputPaintEffects - 1, + }; + + void AwaitSwapChainUsageCompletion() { + // Presentation engine usage. + present_submission_tracker.AwaitAllSubmissionsCompletion(); + // Paint (render target) usage. While the presentation fence is signaled + // on the same queue, and presentation happens after painting, awaiting + // anyway for safety just to make less assumptions in the architecture. + paint_submission_tracker.AwaitAllSubmissionsCompletion(); + } + + void DestroySwapChain(); + + // Connection-independent. + + // Signaled before presenting. + D3D12SubmissionTracker paint_submission_tracker; + // Signaled after presenting. + D3D12SubmissionTracker present_submission_tracker; + + std::array, + kSwapChainBufferCount> + command_allocators; + Microsoft::WRL::ComPtr command_list; + + // Descriptor heaps for views of the current resources related to the guest + // output and to painting, updated either during painting or during + // connection lifetime management if outdated after awaiting usage + // completion. + // RTV heap. + Microsoft::WRL::ComPtr rtv_heap; + // Shader-visible CBV/SRV/UAV heap. + Microsoft::WRL::ComPtr view_heap; + + // Refreshed and cleaned up during guest output painting. The first is the + // paint submission index in which the guest output texture (and its + // descriptors) was last used, the second is the reference to the texture, + // which may be null. The indices are not mailbox indices here, rather, if + // the reference is not in this array yet, the most outdated reference, if + // needed, is replaced with the new one, awaiting the completion of the last + // paint usage. + std::array>, + kGuestOutputMailboxSize> + guest_output_resource_paint_refs; + + // Current intermediate textures for guest output painting, refreshed when + // painting guest output. While not in use, they are in + // D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE. + std::array, + kMaxGuestOutputPaintEffects - 1> + guest_output_intermediate_textures; + UINT64 guest_output_intermediate_texture_last_usage = 0; + + // Connection-specific. + + uint32_t swap_chain_width = 0; + uint32_t swap_chain_height = 0; + bool swap_chain_allows_tearing = false; + Microsoft::WRL::ComPtr swap_chain; + std::array, kSwapChainBufferCount> + swap_chain_buffers; + }; + + explicit D3D12Presenter(HostGpuLossCallback host_gpu_loss_callback, + const D3D12Provider& provider) + : Presenter(host_gpu_loss_callback), provider_(provider) {} + + bool dxgi_supports_tearing() const { return dxgi_supports_tearing_; } + + bool InitializeSurfaceIndependent(); + + const D3D12Provider& provider_; + + // Whether DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING is supported by DXGI (depends in + // particular on the Windows 10 version and hardware support), primarily for + // variable refresh rate support. + bool dxgi_supports_tearing_ = false; + + // Static objects for guest output presentation, used only when painting the + // main target (can be destroyed only after awaiting main target usage + // completion). + std::array, + kGuestOutputPaintRootSignatureCount> + guest_output_paint_root_signatures_; + std::array, + size_t(GuestOutputPaintEffect::kCount)> + guest_output_paint_intermediate_pipelines_; + std::array, + size_t(GuestOutputPaintEffect::kCount)> + guest_output_paint_final_pipelines_; + + // The first is the refresher submission tracker fence value at which the + // guest output texture was last refreshed, the second is the reference to the + // texture, which may be null. The indices are the mailbox indices. + std::array>, + kGuestOutputMailboxSize> + guest_output_resources_; + // The guest output resources are protected by two submission trackers - the + // refresher ones (for writing to them via the guest_output_resources_ + // references) and the paint one (for presenting it via the + // paint_context_.guest_output_resource_paint_refs references taken from + // guest_output_resources_). + D3D12SubmissionTracker guest_output_resource_refresher_submission_tracker_; + + // UI submission tracker with the submission index that can be given to UI + // drawers (accessible from the UI thread only, at any time). + D3D12SubmissionTracker ui_submission_tracker_; + + // Accessible only by painting and by surface connection lifetime management + // (ConnectOrReconnectPaintingToSurfaceFromUIThread, + // DisconnectPaintingFromSurfaceFromUIThreadImpl) by the thread doing it, as + // well as by presenter initialization and shutdown. + PaintContext paint_context_; +}; + +} // namespace d3d12 +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_D3D12_D3D12_PRESENTER_H_ diff --git a/src/xenia/ui/d3d12/d3d12_provider.cc b/src/xenia/ui/d3d12/d3d12_provider.cc index 57345824d..3c3825091 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.cc +++ b/src/xenia/ui/d3d12/d3d12_provider.cc @@ -15,7 +15,8 @@ #include "xenia/base/cvar.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" -#include "xenia/ui/d3d12/d3d12_context.h" +#include "xenia/ui/d3d12/d3d12_immediate_drawer.h" +#include "xenia/ui/d3d12/d3d12_presenter.h" DEFINE_bool(d3d12_debug, false, "Enable Direct3D 12 and DXGI debug layer.", "D3D12"); @@ -77,6 +78,14 @@ D3D12Provider::~D3D12Provider() { dxgi_factory_->Release(); } + if (cvars::d3d12_debug && pfn_dxgi_get_debug_interface1_) { + Microsoft::WRL::ComPtr dxgi_debug; + if (SUCCEEDED( + pfn_dxgi_get_debug_interface1_(0, IID_PPV_ARGS(&dxgi_debug)))) { + dxgi_debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL); + } + } + if (library_dxcompiler_ != nullptr) { FreeLibrary(library_dxcompiler_); } @@ -86,9 +95,6 @@ D3D12Provider::~D3D12Provider() { if (library_d3dcompiler_ != nullptr) { FreeLibrary(library_d3dcompiler_); } - if (library_dcomp_ != nullptr) { - FreeLibrary(library_dcomp_); - } if (library_d3d12_ != nullptr) { FreeLibrary(library_d3d12_); } @@ -120,9 +126,8 @@ bool D3D12Provider::Initialize() { // Load the core libraries. library_dxgi_ = LoadLibraryW(L"dxgi.dll"); library_d3d12_ = LoadLibraryW(L"D3D12.dll"); - library_dcomp_ = LoadLibraryW(L"dcomp.dll"); - if (!library_dxgi_ || !library_d3d12_ || !library_dcomp_) { - XELOGE("Failed to load dxgi.dll, D3D12.dll or dcomp.dll"); + if (!library_dxgi_ || !library_d3d12_) { + XELOGE("Failed to load dxgi.dll or D3D12.dll"); return false; } bool libraries_loaded = true; @@ -143,12 +148,8 @@ bool D3D12Provider::Initialize() { (pfn_d3d12_serialize_root_signature_ = PFN_D3D12_SERIALIZE_ROOT_SIGNATURE( GetProcAddress(library_d3d12_, "D3D12SerializeRootSignature"))) != nullptr; - libraries_loaded &= - (pfn_dcomposition_create_device_ = PFNDCompositionCreateDevice( - GetProcAddress(library_dcomp_, "DCompositionCreateDevice"))) != - nullptr; if (!libraries_loaded) { - XELOGE("Failed to get DXGI, Direct3D 12 or DirectComposition functions"); + XELOGE("Failed to get DXGI or Direct3D 12 functions"); return false; } @@ -470,23 +471,13 @@ bool D3D12Provider::Initialize() { return true; } -std::unique_ptr D3D12Provider::CreateContext( - Window* target_window) { - auto new_context = - std::unique_ptr(new D3D12Context(this, target_window)); - if (!new_context->Initialize()) { - return nullptr; - } - return std::unique_ptr(new_context.release()); +std::unique_ptr D3D12Provider::CreatePresenter( + Presenter::HostGpuLossCallback host_gpu_loss_callback) { + return D3D12Presenter::Create(host_gpu_loss_callback, *this); } -std::unique_ptr D3D12Provider::CreateOffscreenContext() { - auto new_context = - std::unique_ptr(new D3D12Context(this, nullptr)); - if (!new_context->Initialize()) { - return nullptr; - } - return std::unique_ptr(new_context.release()); +std::unique_ptr D3D12Provider::CreateImmediateDrawer() { + return D3D12ImmediateDrawer::Create(*this); } } // namespace d3d12 diff --git a/src/xenia/ui/d3d12/d3d12_provider.h b/src/xenia/ui/d3d12/d3d12_provider.h index 3ddaf507d..937acaae9 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.h +++ b/src/xenia/ui/d3d12/d3d12_provider.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2018 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -23,15 +23,17 @@ namespace d3d12 { class D3D12Provider : public GraphicsProvider { public: - ~D3D12Provider() override; + ~D3D12Provider(); static bool IsD3D12APIAvailable(); static std::unique_ptr Create(); - std::unique_ptr CreateContext( - Window* target_window) override; - std::unique_ptr CreateOffscreenContext() override; + std::unique_ptr CreatePresenter( + Presenter::HostGpuLossCallback host_gpu_loss_callback = + Presenter::FatalErrorHostGpuLossCallback) override; + + std::unique_ptr CreateImmediateDrawer() override; IDXGIFactory2* GetDXGIFactory() const { return dxgi_factory_; } // nullptr if PIX not attached. @@ -118,11 +120,6 @@ class D3D12Provider : public GraphicsProvider { return pfn_d3d12_serialize_root_signature_(desc, version, blob_out, error_blob_out); } - HRESULT CreateDCompositionDevice(IDXGIDevice* dxgi_device, const IID& iid, - void** dcomposition_device_out) const { - return pfn_dcomposition_create_device_(dxgi_device, iid, - dcomposition_device_out); - } HRESULT Disassemble(const void* src_data, size_t src_data_size, UINT flags, const char* comments, ID3DBlob** disassembly_out) const { if (!pfn_d3d_disassemble_) { @@ -156,22 +153,17 @@ class D3D12Provider : public GraphicsProvider { _COM_Outptr_ void** ppFactory); typedef HRESULT(WINAPI* PFNDXGIGetDebugInterface1)( UINT Flags, REFIID riid, _COM_Outptr_ void** pDebug); - typedef HRESULT(WINAPI* PFNDCompositionCreateDevice)( - _In_opt_ IDXGIDevice* dxgiDevice, _In_ REFIID iid, - _Outptr_ void** dcompositionDevice); HMODULE library_dxgi_ = nullptr; PFNCreateDXGIFactory2 pfn_create_dxgi_factory2_; - PFNDXGIGetDebugInterface1 pfn_dxgi_get_debug_interface1_; + // Needed during shutdown as well to report live objects, so may be nullptr. + PFNDXGIGetDebugInterface1 pfn_dxgi_get_debug_interface1_ = nullptr; HMODULE library_d3d12_ = nullptr; PFN_D3D12_GET_DEBUG_INTERFACE pfn_d3d12_get_debug_interface_; PFN_D3D12_CREATE_DEVICE pfn_d3d12_create_device_; PFN_D3D12_SERIALIZE_ROOT_SIGNATURE pfn_d3d12_serialize_root_signature_; - HMODULE library_dcomp_ = nullptr; - PFNDCompositionCreateDevice pfn_dcomposition_create_device_; - HMODULE library_d3dcompiler_ = nullptr; pD3DDisassemble pfn_d3d_disassemble_ = nullptr; @@ -182,9 +174,9 @@ class D3D12Provider : public GraphicsProvider { DxcCreateInstanceProc pfn_dxcompiler_dxc_create_instance_ = nullptr; IDXGIFactory2* dxgi_factory_ = nullptr; - IDXGraphicsAnalysis* graphics_analysis_ = nullptr; ID3D12Device* device_ = nullptr; ID3D12CommandQueue* direct_queue_ = nullptr; + IDXGraphicsAnalysis* graphics_analysis_ = nullptr; uint32_t descriptor_sizes_[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES]; diff --git a/src/xenia/ui/d3d12/d3d12_submission_tracker.cc b/src/xenia/ui/d3d12/d3d12_submission_tracker.cc new file mode 100644 index 000000000..d8f604f7f --- /dev/null +++ b/src/xenia/ui/d3d12/d3d12_submission_tracker.cc @@ -0,0 +1,126 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/d3d12/d3d12_submission_tracker.h" + +#include "xenia/base/assert.h" +#include "xenia/base/logging.h" + +namespace xe { +namespace ui { +namespace d3d12 { + +bool D3D12SubmissionTracker::Initialize(ID3D12Device* device, + ID3D12CommandQueue* queue) { + Shutdown(); + fence_completion_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (!fence_completion_event_) { + XELOGE( + "D3D12SubmissionTracker: Failed to create the fence completion event"); + Shutdown(); + return false; + } + // Continue where the tracker was left at the last shutdown. + if (FAILED(device->CreateFence(submission_current_ - 1, D3D12_FENCE_FLAG_NONE, + IID_PPV_ARGS(&fence_)))) { + XELOGE("D3D12SubmissionTracker: Failed to create the fence"); + Shutdown(); + return false; + } + queue_ = queue; + submission_signal_queued_ = submission_current_ - 1; + return true; +} + +void D3D12SubmissionTracker::Shutdown() { + AwaitAllSubmissionsCompletion(); + queue_.Reset(); + fence_.Reset(); + if (fence_completion_event_) { + CloseHandle(fence_completion_event_); + fence_completion_event_ = nullptr; + } +} + +bool D3D12SubmissionTracker::AwaitSubmissionCompletion( + UINT64 submission_index) { + if (!fence_ || !fence_completion_event_) { + // Not fully initialized yet or already shut down. + return false; + } + // The tracker itself can't give a submission index for a submission that + // hasn't even started being recorded yet, the client has provided a + // completely invalid value or has done overly optimistic math if such an + // index has been obtained somehow. + assert_true(submission_index <= submission_current_); + // Waiting for the current submission is fine if there was a refusal to + // submit, and the submission index wasn't incremented, but still need to + // release objects referenced in the dropped submission (while shutting down, + // for instance - in this case, waiting for the last successful submission, + // which could have also referenced the objects from the new submission - we + // can't know since the client has already overwritten its last usage index, + // would correctly ensure that GPU usage of the objects is not pending). + // Waiting for successful submissions, but failed signals, will result in a + // true race condition, however, but waiting for the closest successful signal + // is the best approximation - also retrying to signal in this case. + UINT64 fence_value = submission_index; + if (submission_index > submission_signal_queued_) { + TrySignalEnqueueing(); + fence_value = submission_signal_queued_; + } + if (fence_->GetCompletedValue() < fence_value) { + if (FAILED(fence_->SetEventOnCompletion(fence_value, + fence_completion_event_))) { + return false; + } + if (WaitForSingleObject(fence_completion_event_, INFINITE) != + WAIT_OBJECT_0) { + return false; + } + } + return fence_value == submission_index; +} + +void D3D12SubmissionTracker::SetQueue(ID3D12CommandQueue* new_queue) { + if (queue_.Get() == new_queue) { + return; + } + if (queue_) { + // Make sure the first signal on the new queue won't happen before the last + // signal, if pending, on the old one, as that would result first in too + // early submission completion indication, and then in rewinding. + AwaitAllSubmissionsCompletion(); + } + queue_ = new_queue; +} + +bool D3D12SubmissionTracker::NextSubmission() { + ++submission_current_; + assert_not_null(queue_); + assert_not_null(fence_); + return TrySignalEnqueueing(); +} + +bool D3D12SubmissionTracker::TrySignalEnqueueing() { + if (submission_signal_queued_ + 1 >= submission_current_) { + return true; + } + if (!queue_ || !fence_) { + return false; + } + if (FAILED(queue_->Signal(fence_.Get(), submission_current_ - 1))) { + return false; + } + submission_signal_queued_ = submission_current_ - 1; + return true; +} + +} // namespace d3d12 +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/d3d12/d3d12_submission_tracker.h b/src/xenia/ui/d3d12/d3d12_submission_tracker.h new file mode 100644 index 000000000..cf4e92836 --- /dev/null +++ b/src/xenia/ui/d3d12/d3d12_submission_tracker.h @@ -0,0 +1,93 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_D3D12_D3D12_SUBMISSION_TRACKER_H_ +#define XENIA_UI_D3D12_D3D12_SUBMISSION_TRACKER_H_ + +#include "xenia/ui/d3d12/d3d12_api.h" + +namespace xe { +namespace ui { +namespace d3d12 { + +// GPU > CPU fence wrapper, safely handling cases when the fence has not been +// initialized yet or has already been shut down, dropped submissions, and also +// transfers between queues so signals stay ordered. +// +// The current submission index can be associated with the usage of objects to +// release them when the GPU isn't potentially referencing them anymore, and +// should be incremented only +// +// 0 can be used as a "never referenced" submission index. +// +// The submission index timeline survives Shutdown / Initialize, so submission +// indices can be given to clients that are not aware of the lifetime of the +// tracker. +class D3D12SubmissionTracker { + public: + D3D12SubmissionTracker() = default; + D3D12SubmissionTracker(const D3D12SubmissionTracker& submission_tracker) = + delete; + D3D12SubmissionTracker& operator=( + const D3D12SubmissionTracker& submission_tracker) = delete; + ~D3D12SubmissionTracker() { Shutdown(); } + + // The queue may be null if it's going to be set dynamically. Will also take a + // reference to the queue. + bool Initialize(ID3D12Device* device, ID3D12CommandQueue* queue); + void Shutdown(); + + // Will perform an ownership transfer if the queue is different than the + // current one, and take a reference to the queue. + void SetQueue(ID3D12CommandQueue* new_queue); + + UINT64 GetCurrentSubmission() const { return submission_current_; } + // May be lower than a value awaited by AwaitSubmissionCompletion if it + // returned false. + UINT64 GetCompletedSubmission() const { + // If shut down already or haven't fully initialized yet, don't care, for + // simplicity of external code, as any downloads are unlikely in this case, + // but destruction can be simplified. + return fence_ ? fence_->GetCompletedValue() : (GetCurrentSubmission() - 1); + } + + // Returns whether the expected GPU signal has actually been reached (rather + // than some fallback condition) for cases when stronger completeness + // guarantees as needed (when downloading, as opposed to just destroying). + // If false is returned, it's also not guaranteed that GetCompletedSubmission + // will return a value >= submission_index. + bool AwaitSubmissionCompletion(UINT64 submission_index); + bool AwaitAllSubmissionsCompletion() { + return AwaitSubmissionCompletion(GetCurrentSubmission() - 1); + } + + // Call after a successful ExecuteCommandList. Unconditionally increments the + // current submission index, and tries to enqueue the fence signal. Returns + // true if enqueued successfully, but even if not, waiting for submissions + // without a successfully enqueued signal is handled in the tracker in a way + // that it won't be infinite, so there's no need for clients to revert updates + // to submission indices associated with GPU usage of objects. + bool NextSubmission(); + // If NextSubmission has failed, but it's important that the signal is + // enqueued, can be used to retry enqueueing the signal. + bool TrySignalEnqueueing(); + + private: + UINT64 submission_current_ = 1; + UINT64 submission_signal_queued_ = 0; + HANDLE fence_completion_event_ = nullptr; + Microsoft::WRL::ComPtr fence_; + Microsoft::WRL::ComPtr queue_; +}; + +} // namespace d3d12 +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_D3D12_D3D12_SUBMISSION_TRACKER_H_ diff --git a/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.cc b/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.cc index 0d6cf8b72..b50edfd6e 100644 --- a/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.cc +++ b/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -38,7 +38,7 @@ uint8_t* D3D12UploadBufferPool::Request( return nullptr; } if (buffer_out) { - *buffer_out = page->buffer_; + *buffer_out = page->buffer_.Get(); } if (offset_out) { *offset_out = offset; @@ -61,7 +61,7 @@ uint8_t* D3D12UploadBufferPool::RequestPartial( return nullptr; } if (buffer_out) { - *buffer_out = page->buffer_; + *buffer_out = page->buffer_.Get(); } if (offset_out) { *offset_out = offset; @@ -80,7 +80,7 @@ D3D12UploadBufferPool::CreatePageImplementation() { D3D12_RESOURCE_DESC buffer_desc; util::FillBufferResourceDesc(buffer_desc, page_size_, D3D12_RESOURCE_FLAG_NONE); - ID3D12Resource* buffer; + Microsoft::WRL::ComPtr buffer; if (FAILED(provider_.GetDevice()->CreateCommittedResource( &util::kHeapPropertiesUpload, provider_.GetHeapFlagCreateNotZeroed(), &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, @@ -97,24 +97,16 @@ D3D12UploadBufferPool::CreatePageImplementation() { buffer->Release(); return nullptr; } - D3D12Page* page = new D3D12Page(buffer, mapping); - // Owned by the page now. - buffer->Release(); - return page; + // Unmapping will be done implicitly when the resource is destroyed. + return new D3D12Page(buffer.Get(), mapping); } D3D12UploadBufferPool::D3D12Page::D3D12Page(ID3D12Resource* buffer, void* mapping) : buffer_(buffer), mapping_(mapping) { - buffer_->AddRef(); gpu_address_ = buffer_->GetGPUVirtualAddress(); } -D3D12UploadBufferPool::D3D12Page::~D3D12Page() { - // Unmapping is done implicitly when the buffer is destroyed. - buffer_->Release(); -} - } // namespace d3d12 } // namespace ui } // namespace xe diff --git a/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h b/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h index 2a8d2ac0a..c35443c5e 100644 --- a/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h +++ b/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -38,8 +38,7 @@ class D3D12UploadBufferPool : public GraphicsUploadBufferPool { // Creates a reference to the buffer. It must not be unmapped until this // D3D12Page is deleted. D3D12Page(ID3D12Resource* buffer, void* mapping); - ~D3D12Page() override; - ID3D12Resource* buffer_; + Microsoft::WRL::ComPtr buffer_; void* mapping_; D3D12_GPU_VIRTUAL_ADDRESS gpu_address_; }; diff --git a/src/xenia/ui/file_picker.h b/src/xenia/ui/file_picker.h index 6a2259007..c2e6e1174 100644 --- a/src/xenia/ui/file_picker.h +++ b/src/xenia/ui/file_picker.h @@ -16,6 +16,8 @@ #include #include +#include "xenia/ui/window.h" + namespace xe { namespace ui { @@ -68,7 +70,7 @@ class FilePicker { selected_files_ = std::move(selected_files); } - virtual bool Show(void* parent_window_handle = nullptr) = 0; + virtual bool Show(Window* parent_window = nullptr) = 0; private: Mode mode_; diff --git a/src/xenia/ui/file_picker_gtk.cc b/src/xenia/ui/file_picker_gtk.cc index 456582906..0b6ed5044 100644 --- a/src/xenia/ui/file_picker_gtk.cc +++ b/src/xenia/ui/file_picker_gtk.cc @@ -19,6 +19,7 @@ #include "xenia/base/filesystem.h" #include "xenia/base/platform_linux.h" #include "xenia/base/string.h" +#include "xenia/ui/window_gtk.h" namespace xe { namespace ui { @@ -28,7 +29,7 @@ class GtkFilePicker : public FilePicker { GtkFilePicker(); ~GtkFilePicker() override; - bool Show(void* parent_window_handle) override; + bool Show(Window* parent_window) override; private: }; @@ -41,7 +42,7 @@ GtkFilePicker::GtkFilePicker() = default; GtkFilePicker::~GtkFilePicker() = default; -bool GtkFilePicker::Show(void* parent_window_handle) { +bool GtkFilePicker::Show(Window* parent_window) { // TODO(benvanik): FileSaveDialog. assert_true(mode() == Mode::kOpen); // TODO(benvanik): folder dialogs. @@ -50,7 +51,10 @@ bool GtkFilePicker::Show(void* parent_window_handle) { gint res; dialog = gtk_file_chooser_dialog_new( - "Open File", (GtkWindow*)parent_window_handle, + "Open File", + parent_window + ? GTK_WINDOW(static_cast(parent_window)->window()) + : nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); diff --git a/src/xenia/ui/file_picker_win.cc b/src/xenia/ui/file_picker_win.cc index d2d7bf33d..88ce056bc 100644 --- a/src/xenia/ui/file_picker_win.cc +++ b/src/xenia/ui/file_picker_win.cc @@ -11,6 +11,7 @@ #include "xenia/base/platform_win.h" #include "xenia/base/string.h" #include "xenia/ui/file_picker.h" +#include "xenia/ui/window_win.h" // Microsoft headers after platform_win.h. #include @@ -23,7 +24,7 @@ class Win32FilePicker : public FilePicker { Win32FilePicker(); ~Win32FilePicker() override; - bool Show(void* parent_window_handle) override; + bool Show(Window* parent_window) override; private: }; @@ -109,7 +110,7 @@ Win32FilePicker::Win32FilePicker() = default; Win32FilePicker::~Win32FilePicker() = default; -bool Win32FilePicker::Show(void* parent_window_handle) { +bool Win32FilePicker::Show(Window* parent_window) { // TODO(benvanik): FileSaveDialog. assert_true(mode() == Mode::kOpen); // TODO(benvanik): folder dialogs. @@ -179,7 +180,9 @@ bool Win32FilePicker::Show(void* parent_window_handle) { } // Show the dialog modally. - hr = file_dialog->Show(static_cast(parent_window_handle)); + hr = file_dialog->Show( + parent_window ? static_cast(parent_window)->hwnd() + : nullptr); file_dialog->Unadvise(cookie); if (!SUCCEEDED(hr)) { return false; diff --git a/src/xenia/ui/graphics_context.cc b/src/xenia/ui/graphics_context.cc deleted file mode 100644 index 7f5ab07b6..000000000 --- a/src/xenia/ui/graphics_context.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/graphics_context.h" - -#include - -#include "xenia/base/cvar.h" -#include "xenia/ui/graphics_provider.h" - -DEFINE_bool(random_clear_color, false, "Randomize window clear color.", "UI"); - -namespace xe { -namespace ui { - -GraphicsContext::GraphicsContext(GraphicsProvider* provider, - Window* target_window) - : provider_(provider), target_window_(target_window) {} - -GraphicsContext::~GraphicsContext() = default; - -bool GraphicsContext::is_current() { return true; } - -bool GraphicsContext::MakeCurrent() { return true; } - -void GraphicsContext::ClearCurrent() {} - -void GraphicsContext::GetClearColor(float* rgba) { - if (cvars::random_clear_color) { - rgba[0] = rand() / float(RAND_MAX); // NOLINT(runtime/threadsafe_fn) - rgba[1] = 1.0f; - rgba[2] = 0.0f; - } else { - rgba[0] = 0.0f; - rgba[1] = 0.0f; - rgba[2] = 0.0f; - } - rgba[3] = 1.0f; -} - -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/graphics_context.h b/src/xenia/ui/graphics_context.h deleted file mode 100644 index 0ed5bd881..000000000 --- a/src/xenia/ui/graphics_context.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GRAPHICS_CONTEXT_H_ -#define XENIA_UI_GRAPHICS_CONTEXT_H_ - -#include -#include - -namespace xe { -namespace ui { - -class GraphicsProvider; -class ImmediateDrawer; -class Window; - -class RawImage { - public: - RawImage() = default; - ~RawImage() = default; - - size_t width = 0; - size_t height = 0; - size_t stride = 0; - std::vector data; -}; - -class GraphicsContext { - public: - virtual ~GraphicsContext(); - - GraphicsProvider* provider() const { return provider_; } - Window* target_window() const { return target_window_; } - bool is_offscreen() { return immediate_drawer() == nullptr; } - - virtual ImmediateDrawer* immediate_drawer() = 0; - - virtual bool is_current(); - virtual bool MakeCurrent(); - virtual void ClearCurrent(); - - // Returns true if the OS took away our context because we caused a TDR or - // some other outstanding error. When this happens, this context, as well as - // any other shared contexts are junk. - // This context must be made current in order for this call to work properly. - virtual bool WasLost() = 0; - - // Returns true if able to draw now (the target surface is available). - virtual bool BeginSwap() = 0; - virtual void EndSwap() = 0; - - virtual std::unique_ptr Capture() = 0; - - protected: - explicit GraphicsContext(GraphicsProvider* provider, Window* target_window); - - static void GetClearColor(float* rgba); - - GraphicsProvider* provider_ = nullptr; - Window* target_window_ = nullptr; -}; - -struct GraphicsContextLock { - explicit GraphicsContextLock(GraphicsContext* context) : context_(context) { - was_current_ = context_->is_current(); - if (!was_current_) { - context_->MakeCurrent(); - } - } - ~GraphicsContextLock() { - if (!was_current_) { - context_->ClearCurrent(); - } - } - - private: - bool was_current_ = false; - GraphicsContext* context_ = nullptr; -}; - -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GRAPHICS_CONTEXT_H_ diff --git a/src/xenia/ui/graphics_provider.h b/src/xenia/ui/graphics_provider.h index 8bbfe90d0..08c78cd1f 100644 --- a/src/xenia/ui/graphics_provider.h +++ b/src/xenia/ui/graphics_provider.h @@ -12,10 +12,12 @@ #include +#include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/presenter.h" + namespace xe { namespace ui { -class GraphicsContext; class Window; // Factory for graphics contexts. @@ -36,13 +38,13 @@ class GraphicsProvider { virtual ~GraphicsProvider() = default; - // Creates a new graphics context and swapchain for presenting to a window. - virtual std::unique_ptr CreateContext( - Window* target_window) = 0; + // It's safe to reinitialize the presenter in the host GPU loss callback if it + // was called from the UI thread as specified in the arguments. + virtual std::unique_ptr CreatePresenter( + Presenter::HostGpuLossCallback host_gpu_loss_callback = + Presenter::FatalErrorHostGpuLossCallback) = 0; - // Creates a new offscreen graphics context without a swapchain or immediate - // drawer. - virtual std::unique_ptr CreateOffscreenContext() = 0; + virtual std::unique_ptr CreateImmediateDrawer() = 0; protected: GraphicsProvider() = default; diff --git a/src/xenia/ui/graphics_upload_buffer_pool.cc b/src/xenia/ui/graphics_upload_buffer_pool.cc index 5eb04fba3..d68085b8d 100644 --- a/src/xenia/ui/graphics_upload_buffer_pool.cc +++ b/src/xenia/ui/graphics_upload_buffer_pool.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -38,6 +38,25 @@ void GraphicsUploadBufferPool::Reclaim(uint64_t completed_submission_index) { } } +void GraphicsUploadBufferPool::ChangeSubmissionTimeline() { + // Reclaim all submitted pages. + if (writable_last_) { + writable_last_->next_ = submitted_first_; + } else { + writable_first_ = submitted_first_; + } + writable_last_ = submitted_last_; + submitted_first_ = nullptr; + submitted_last_ = nullptr; + + // Mark all pages as never used yet in the new timeline. + Page* page = writable_first_; + while (page) { + page->last_submission_index_ = 0; + page = page->next_; + } +} + void GraphicsUploadBufferPool::ClearCache() { // Called from the destructor - must not call virtual functions here. current_page_flushed_ = 0; diff --git a/src/xenia/ui/graphics_upload_buffer_pool.h b/src/xenia/ui/graphics_upload_buffer_pool.h index 250eda70b..00f628af6 100644 --- a/src/xenia/ui/graphics_upload_buffer_pool.h +++ b/src/xenia/ui/graphics_upload_buffer_pool.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -32,6 +32,7 @@ class GraphicsUploadBufferPool { virtual ~GraphicsUploadBufferPool(); void Reclaim(uint64_t completed_submission_index); + void ChangeSubmissionTimeline(); void ClearCache(); // Should be called before submitting anything using this pool, unless the diff --git a/src/xenia/ui/graphics_util.cc b/src/xenia/ui/graphics_util.cc new file mode 100644 index 000000000..b69bc44b3 --- /dev/null +++ b/src/xenia/ui/graphics_util.cc @@ -0,0 +1,63 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/graphics_util.h" + +#include + +namespace xe { +namespace ui { + +int32_t FloatToD3D11Fixed16p8(float f32) { + // https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#3.2.4.1%20FLOAT%20-%3E%20Fixed%20Point%20Integer + // Early exit tests. + // n == NaN || n.unbiasedExponent < -f-1 -> 0 . 0 + if (!(std::abs(f32) >= 1.0f / 512.0f)) { + return 0; + } + // n >= (2^(i-1)-2^-f) -> 2^(i-1)-1 . 2^f-1 + if (f32 >= 32768.0f - 1.0f / 256.0f) { + return (1 << 23) - 1; + } + // n <= -2^(i-1) -> -2^(i-1) . 0 + if (f32 <= -32768.0f) { + return -32768 * 256; + } + uint32_t f32_bits = *reinterpret_cast(&f32); + // Copy float32 mantissa bits [22:0] into corresponding bits [22:0] of a + // result buffer that has at least 24 bits total storage (before reaching + // rounding step further below). This includes one bit for the hidden 1. + // Set bit [23] (float32 hidden bit). + // Clear bits [31:24]. + union { + int32_t s; + uint32_t u; + } result; + result.u = (f32_bits & ((1 << 23) - 1)) | (1 << 23); + // If the sign bit is set in the float32 number (negative), then take the 2's + // component of the entire set of bits. + if ((f32_bits >> 31) != 0) { + result.s = -result.s; + } + // Final calculation: extraBits = (mantissa - f) - n.unbiasedExponent + // (guaranteed to be >= 0). + int32_t exponent = int32_t((f32_bits >> 23) & 255) - 127; + uint32_t extra_bits = uint32_t(15 - exponent); + if (extra_bits) { + // Round the 32-bit value to a decimal that is extraBits to the left of + // the LSB end, using nearest-even. + result.u += (1 << (extra_bits - 1)) - 1 + ((result.u >> extra_bits) & 1); + // Shift right by extraBits (sign extending). + result.s >>= extra_bits; + } + return result.s; +} + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/graphics_util.h b/src/xenia/ui/graphics_util.h new file mode 100644 index 000000000..b3aa19a33 --- /dev/null +++ b/src/xenia/ui/graphics_util.h @@ -0,0 +1,30 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_GRAPHICS_UTIL_H_ +#define XENIA_UI_GRAPHICS_UTIL_H_ + +#include + +namespace xe { +namespace ui { + +// For estimating coverage extents from vertices. This may give results that are +// different than what the GPU will actually draw (this is the reference +// conversion with 1/2 ULP accuracy, but Direct3D 11 permits 0.6 ULP tolerance +// in floating point to fixed point conversion), but is enough to tie-break +// vertices at pixel centers (due to the half-pixel offset applied to integer +// coordinates incorrectly, for instance) with some error tolerance near 0.5, +// for use with the top-left rasterization rule later. +int32_t FloatToD3D11Fixed16p8(float f32); + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_GRAPHICS_UTIL_H_ diff --git a/src/xenia/ui/imgui_dialog.cc b/src/xenia/ui/imgui_dialog.cc index 2e2370bd9..4e940e798 100644 --- a/src/xenia/ui/imgui_dialog.cc +++ b/src/xenia/ui/imgui_dialog.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -11,20 +11,18 @@ #include "third_party/imgui/imgui.h" #include "xenia/base/assert.h" -#include "xenia/ui/window.h" +#include "xenia/ui/imgui_drawer.h" namespace xe { namespace ui { -ImGuiDialog::ImGuiDialog(Window* window) : window_(window) { - window_->AttachListener(this); - had_imgui_active_ = window_->is_imgui_input_enabled(); - window_->set_imgui_input_enabled(true); +ImGuiDialog::ImGuiDialog(ImGuiDrawer* imgui_drawer) + : imgui_drawer_(imgui_drawer) { + imgui_drawer_->AddDialog(this); } ImGuiDialog::~ImGuiDialog() { - window_->set_imgui_input_enabled(had_imgui_active_); - window_->DetachListener(this); + imgui_drawer_->RemoveDialog(this); for (auto fence : waiting_fences_) { fence->Signal(); } @@ -36,12 +34,9 @@ void ImGuiDialog::Then(xe::threading::Fence* fence) { void ImGuiDialog::Close() { has_close_pending_ = true; } -ImGuiIO& ImGuiDialog::GetIO() { return window_->imgui_drawer()->GetIO(); } - -void ImGuiDialog::OnPaint(UIEvent* e) { - // Keep imgui rendering every frame. - window_->Invalidate(); +ImGuiIO& ImGuiDialog::GetIO() { return imgui_drawer()->GetIO(); } +void ImGuiDialog::Draw() { // Draw UI. OnDraw(GetIO()); @@ -52,10 +47,13 @@ void ImGuiDialog::OnPaint(UIEvent* e) { } } -class MessageBoxDialog : public ImGuiDialog { +class MessageBoxDialog final : public ImGuiDialog { public: - MessageBoxDialog(Window* window, std::string title, std::string body) - : ImGuiDialog(window), title_(std::move(title)), body_(std::move(body)) {} + MessageBoxDialog(ImGuiDrawer* imgui_drawer, std::string title, + std::string body) + : ImGuiDialog(imgui_drawer), + title_(std::move(title)), + body_(std::move(body)) {} void OnDraw(ImGuiIO& io) override { if (!has_opened_) { @@ -84,9 +82,9 @@ class MessageBoxDialog : public ImGuiDialog { std::string body_; }; -ImGuiDialog* ImGuiDialog::ShowMessageBox(Window* window, std::string title, - std::string body) { - return new MessageBoxDialog(window, std::move(title), std::move(body)); +ImGuiDialog* ImGuiDialog::ShowMessageBox(ImGuiDrawer* imgui_drawer, + std::string title, std::string body) { + return new MessageBoxDialog(imgui_drawer, std::move(title), std::move(body)); } } // namespace ui diff --git a/src/xenia/ui/imgui_dialog.h b/src/xenia/ui/imgui_dialog.h index 3c9632c07..454659bea 100644 --- a/src/xenia/ui/imgui_dialog.h +++ b/src/xenia/ui/imgui_dialog.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -19,23 +19,25 @@ namespace xe { namespace ui { -class ImGuiDialog : private WindowListener { +class ImGuiDialog { public: ~ImGuiDialog(); // Shows a simple message box containing a text message. // Callers can want for the dialog to close with Wait(). // Dialogs retain themselves and will delete themselves when closed. - static ImGuiDialog* ShowMessageBox(Window* window, std::string title, - std::string body); + static ImGuiDialog* ShowMessageBox(ImGuiDrawer* imgui_drawer, + std::string title, std::string body); // A fence to signal when the dialog is closed. void Then(xe::threading::Fence* fence); - protected: - ImGuiDialog(Window* window); + void Draw(); - Window* window() const { return window_; } + protected: + ImGuiDialog(ImGuiDrawer* imgui_drawer); + + ImGuiDrawer* imgui_drawer() const { return imgui_drawer_; } ImGuiIO& GetIO(); // Closes the dialog and returns to any waiters. @@ -46,10 +48,7 @@ class ImGuiDialog : private WindowListener { virtual void OnDraw(ImGuiIO& io) {} private: - void OnPaint(UIEvent* e) override; - - Window* window_ = nullptr; - bool had_imgui_active_ = false; + ImGuiDrawer* imgui_drawer_ = nullptr; bool has_close_pending_ = false; std::vector waiting_fences_; }; diff --git a/src/xenia/ui/imgui_drawer.cc b/src/xenia/ui/imgui_drawer.cc index 276cea114..c3ab41906 100644 --- a/src/xenia/ui/imgui_drawer.cc +++ b/src/xenia/ui/imgui_drawer.cc @@ -2,17 +2,23 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/ui/imgui_drawer.h" +#include +#include + #include "third_party/imgui/imgui.h" #include "xenia/base/assert.h" +#include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" +#include "xenia/ui/imgui_dialog.h" +#include "xenia/ui/ui_event.h" #include "xenia/ui/window.h" namespace xe { @@ -26,18 +32,75 @@ const char kProggyTinyCompressedDataBase85[10950 + 1] = static_assert(sizeof(ImmediateVertex) == sizeof(ImDrawVert), "Vertex types must match"); -ImGuiDrawer::ImGuiDrawer(xe::ui::Window* window) - : window_(window), graphics_context_(window->context()) { +ImGuiDrawer::ImGuiDrawer(xe::ui::Window* window, size_t z_order) + : window_(window), z_order_(z_order) { Initialize(); } ImGuiDrawer::~ImGuiDrawer() { + SetPresenter(nullptr); + if (!dialogs_.empty()) { + window_->RemoveInputListener(this); + if (internal_state_) { + ImGui::SetCurrentContext(internal_state_); + if (ImGui::IsAnyMouseDown()) { + window_->ReleaseMouse(); + } + } + } if (internal_state_) { ImGui::DestroyContext(internal_state_); internal_state_ = nullptr; } } +void ImGuiDrawer::AddDialog(ImGuiDialog* dialog) { + assert_not_null(dialog); + // Check if already added. + if (std::find(dialogs_.cbegin(), dialogs_.cend(), dialog) != + dialogs_.cend()) { + return; + } + if (dialog_loop_next_index_ == SIZE_MAX && dialogs_.empty()) { + // First dialog added. dialog_loop_next_index_ == SIZE_MAX is also checked + // because in a situation of removing the only dialog, then adding a dialog, + // from within a dialog's Draw function, the removal would not cause the + // listener and the drawer to be removed (it's deferred in this case). + window_->AddInputListener(this, z_order_); + if (presenter_) { + presenter_->AddUIDrawerFromUIThread(this, z_order_); + } + } + dialogs_.push_back(dialog); +} + +void ImGuiDrawer::RemoveDialog(ImGuiDialog* dialog) { + assert_not_null(dialog); + auto it = std::find(dialogs_.cbegin(), dialogs_.cend(), dialog); + if (it == dialogs_.cend()) { + return; + } + if (dialog_loop_next_index_ != SIZE_MAX) { + // Actualize the next dialog index after the erasure from the vector. + size_t existing_index = size_t(std::distance(dialogs_.cbegin(), it)); + if (dialog_loop_next_index_ > existing_index) { + --dialog_loop_next_index_; + } + } + dialogs_.erase(it); + if (dialog_loop_next_index_ == SIZE_MAX && dialogs_.empty()) { + if (presenter_) { + presenter_->RemoveUIDrawerFromUIThread(this); + } + window_->RemoveInputListener(this); + // Clear all input since no input will be received anymore, and when the + // drawer becomes active again, it'd have an outdated input state otherwise + // which will be persistent until new events actualize individual input + // properties. + ClearInput(); + } +} + void ImGuiDrawer::Initialize() { // Setup ImGui internal state. // This will give us state we can swap to the ImGui globals when in use. @@ -51,9 +114,31 @@ void ImGuiDrawer::Initialize() { // Windows. io.IniFilename = nullptr; - SetupFont(); - - io.DeltaTime = 1.0f / 60.0f; + // Setup the font glyphs. + ImFontConfig font_config; + font_config.OversampleH = font_config.OversampleV = 1; + font_config.PixelSnapH = true; + static const ImWchar font_glyph_ranges[] = { + 0x0020, + 0x00FF, // Basic Latin + Latin Supplement + 0, + }; + io.Fonts->AddFontFromMemoryCompressedBase85TTF( + kProggyTinyCompressedDataBase85, 10.0f, &font_config, font_glyph_ranges); + // TODO(benvanik): jp font on other platforms? + // https://github.com/Koruri/kibitaki looks really good, but is 1.5MiB. + const char* jp_font_path = "C:\\Windows\\Fonts\\msgothic.ttc"; + if (std::filesystem::exists(jp_font_path)) { + ImFontConfig jp_font_config; + jp_font_config.MergeMode = true; + jp_font_config.OversampleH = jp_font_config.OversampleV = 1; + jp_font_config.PixelSnapH = true; + jp_font_config.FontNo = 0; + io.Fonts->AddFontFromFileTTF(jp_font_path, 12.0f, &jp_font_config, + io.Fonts->GetGlyphRangesJapanese()); + } else { + XELOGW("Unable to load Japanese font; JP characters will be boxes"); + } auto& style = ImGui::GetStyle(); style.ScrollbarRounding = 0; @@ -125,60 +210,121 @@ void ImGuiDrawer::Initialize() { io.KeyMap[ImGuiKey_X] = int(ui::VirtualKey::kX); io.KeyMap[ImGuiKey_Y] = int(ui::VirtualKey::kY); io.KeyMap[ImGuiKey_Z] = int(ui::VirtualKey::kZ); + + frame_time_tick_frequency_ = double(Clock::QueryHostTickFrequency()); + last_frame_time_ticks_ = Clock::QueryHostTickCount(); } -void ImGuiDrawer::SetupFont() { - auto& io = GetIO(); - - ImFontConfig font_config; - font_config.OversampleH = font_config.OversampleV = 1; - font_config.PixelSnapH = true; - static const ImWchar font_glyph_ranges[] = { - 0x0020, - 0x00FF, // Basic Latin + Latin Supplement - 0, - }; - io.Fonts->AddFontFromMemoryCompressedBase85TTF( - kProggyTinyCompressedDataBase85, 10.0f, &font_config, font_glyph_ranges); - - // TODO(benvanik): jp font on other platforms? - // https://github.com/Koruri/kibitaki looks really good, but is 1.5MiB. - const char* jp_font_path = "C:\\Windows\\Fonts\\msgothic.ttc"; - if (std::filesystem::exists(jp_font_path)) { - ImFontConfig jp_font_config; - jp_font_config.MergeMode = true; - jp_font_config.OversampleH = jp_font_config.OversampleV = 1; - jp_font_config.PixelSnapH = true; - jp_font_config.FontNo = 0; - io.Fonts->AddFontFromFileTTF(jp_font_path, 12.0f, &jp_font_config, - io.Fonts->GetGlyphRangesJapanese()); - } else { - XELOGW("Unable to load japanese font; jp characters will be boxes"); +void ImGuiDrawer::SetupFontTexture() { + if (font_texture_ || !immediate_drawer_) { + return; } - + ImGuiIO& io = GetIO(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - font_texture_ = graphics_context_->immediate_drawer()->CreateTexture( + font_texture_ = immediate_drawer_->CreateTexture( width, height, ImmediateTextureFilter::kLinear, true, reinterpret_cast(pixels)); - io.Fonts->TexID = reinterpret_cast(font_texture_.get()); } -void ImGuiDrawer::RenderDrawLists(ImDrawData* data) { - auto drawer = graphics_context_->immediate_drawer(); +void ImGuiDrawer::SetPresenter(Presenter* new_presenter) { + if (presenter_) { + if (presenter_ == new_presenter) { + return; + } + if (!dialogs_.empty()) { + presenter_->RemoveUIDrawerFromUIThread(this); + } + ImGuiIO& io = GetIO(); + } + presenter_ = new_presenter; + if (presenter_) { + if (!dialogs_.empty()) { + presenter_->AddUIDrawerFromUIThread(this, z_order_); + } + } +} + +void ImGuiDrawer::SetImmediateDrawer(ImmediateDrawer* new_immediate_drawer) { + if (immediate_drawer_ == new_immediate_drawer) { + return; + } + if (immediate_drawer_) { + GetIO().Fonts->TexID = static_cast(nullptr); + font_texture_.reset(); + } + immediate_drawer_ = new_immediate_drawer; + if (immediate_drawer_) { + SetupFontTexture(); + } +} + +void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) { + // Drawing of anything is initiated by the presenter. + assert_not_null(presenter_); + if (!immediate_drawer_) { + // A presenter has been attached, but an immediate drawer hasn't been + // attached yet. + return; + } + + if (dialogs_.empty()) { + return; + } + + ImGui::SetCurrentContext(internal_state_); - // Handle cases of screen coordinates != from framebuffer coordinates (e.g. - // retina displays). ImGuiIO& io = ImGui::GetIO(); - float fb_height = io.DisplaySize.y * io.DisplayFramebufferScale.y; - data->ScaleClipRects(io.DisplayFramebufferScale); - const float width = io.DisplaySize.x; - const float height = io.DisplaySize.y; - drawer->Begin(static_cast(width), static_cast(height)); + uint64_t current_frame_time_ticks = Clock::QueryHostTickCount(); + io.DeltaTime = + float(double(current_frame_time_ticks - last_frame_time_ticks_) / + frame_time_tick_frequency_); + if (!(io.DeltaTime > 0.0f) || + current_frame_time_ticks < last_frame_time_ticks_) { + // For safety as Dear ImGui doesn't allow non-positive DeltaTime. Using the + // same default value as in the official samples. + io.DeltaTime = 1.0f / 60.0f; + } + last_frame_time_ticks_ = current_frame_time_ticks; + + float physical_to_logical = + float(window_->GetMediumDpi()) / float(window_->GetDpi()); + io.DisplaySize.x = window_->GetActualPhysicalWidth() * physical_to_logical; + io.DisplaySize.y = window_->GetActualPhysicalHeight() * physical_to_logical; + + ImGui::NewFrame(); + + assert_true(dialog_loop_next_index_ == SIZE_MAX); + dialog_loop_next_index_ = 0; + while (dialog_loop_next_index_ < dialogs_.size()) { + dialogs_[dialog_loop_next_index_++]->Draw(); + } + dialog_loop_next_index_ = SIZE_MAX; + + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + if (draw_data) { + RenderDrawLists(draw_data, ui_draw_context); + } + + if (dialogs_.empty()) { + // All dialogs have removed themselves during the draw, detach. + presenter_->RemoveUIDrawerFromUIThread(this); + window_->RemoveInputListener(this); + } else { + // Repaint (and handle input) continuously if still active. + presenter_->RequestUIPaintFromUIThread(); + } +} + +void ImGuiDrawer::RenderDrawLists(ImDrawData* data, + UIDrawContext& ui_draw_context) { + ImGuiIO& io = ImGui::GetIO(); + + immediate_drawer_->Begin(ui_draw_context, io.DisplaySize.x, io.DisplaySize.y); for (int i = 0; i < data->CmdListsCount; ++i) { const auto cmd_list = data->CmdLists[i]; @@ -189,7 +335,7 @@ void ImGuiDrawer::RenderDrawLists(ImDrawData* data) { batch.vertex_count = cmd_list->VtxBuffer.size(); batch.indices = cmd_list->IdxBuffer.Data; batch.index_count = cmd_list->IdxBuffer.size(); - drawer->BeginDrawBatch(batch); + immediate_drawer_->BeginDrawBatch(batch); int index_offset = 0; for (int j = 0; j < cmd_list->CmdBuffer.size(); ++j) { @@ -201,19 +347,19 @@ void ImGuiDrawer::RenderDrawLists(ImDrawData* data) { draw.index_offset = index_offset; draw.texture = reinterpret_cast(cmd.TextureId); draw.scissor = true; - draw.scissor_rect[0] = static_cast(cmd.ClipRect.x); - draw.scissor_rect[1] = static_cast(height - cmd.ClipRect.w); - draw.scissor_rect[2] = static_cast(cmd.ClipRect.z - cmd.ClipRect.x); - draw.scissor_rect[3] = static_cast(cmd.ClipRect.w - cmd.ClipRect.y); - drawer->Draw(draw); + draw.scissor_left = cmd.ClipRect.x; + draw.scissor_top = cmd.ClipRect.y; + draw.scissor_right = cmd.ClipRect.z; + draw.scissor_bottom = cmd.ClipRect.w; + immediate_drawer_->Draw(draw); index_offset += cmd.ElemCount; } - drawer->EndDrawBatch(); + immediate_drawer_->EndDrawBatch(); } - drawer->End(); + immediate_drawer_->End(); } ImGuiIO& ImGuiDrawer::GetIO() { @@ -221,33 +367,25 @@ ImGuiIO& ImGuiDrawer::GetIO() { return ImGui::GetIO(); } -void ImGuiDrawer::RenderDrawLists() { - ImGui::SetCurrentContext(internal_state_); - auto draw_data = ImGui::GetDrawData(); - if (draw_data) { - RenderDrawLists(draw_data); - } -} +void ImGuiDrawer::OnKeyDown(KeyEvent& e) { OnKey(e, true); } -void ImGuiDrawer::OnKeyDown(KeyEvent* e) { OnKey(e, true); } +void ImGuiDrawer::OnKeyUp(KeyEvent& e) { OnKey(e, false); } -void ImGuiDrawer::OnKeyUp(KeyEvent* e) { OnKey(e, false); } - -void ImGuiDrawer::OnKeyChar(KeyEvent* e) { +void ImGuiDrawer::OnKeyChar(KeyEvent& e) { auto& io = GetIO(); // TODO(Triang3l): Accept the Unicode character. - unsigned int character = static_cast(e->virtual_key()); + unsigned int character = static_cast(e.virtual_key()); if (character > 0 && character < 0x10000) { io.AddInputCharacter(character); - e->set_handled(true); + e.set_handled(true); } } -void ImGuiDrawer::OnMouseDown(MouseEvent* e) { +void ImGuiDrawer::OnMouseDown(MouseEvent& e) { + UpdateMousePosition(e); auto& io = GetIO(); - io.MousePos = ImVec2(float(e->x()), float(e->y())); int button = -1; - switch (e->button()) { + switch (e.button()) { case xe::ui::MouseEvent::Button::kLeft: { button = 0; break; @@ -261,25 +399,23 @@ void ImGuiDrawer::OnMouseDown(MouseEvent* e) { break; } } - if (button >= 0 && button < std::size(io.MouseDown)) { - if (!ImGui::IsAnyMouseDown()) { - window_->CaptureMouse(); + if (!io.MouseDown[button]) { + if (!ImGui::IsAnyMouseDown()) { + window_->CaptureMouse(); + } + io.MouseDown[button] = true; } - io.MouseDown[button] = true; } } -void ImGuiDrawer::OnMouseMove(MouseEvent* e) { - auto& io = GetIO(); - io.MousePos = ImVec2(float(e->x()), float(e->y())); -} +void ImGuiDrawer::OnMouseMove(MouseEvent& e) { UpdateMousePosition(e); } -void ImGuiDrawer::OnMouseUp(MouseEvent* e) { +void ImGuiDrawer::OnMouseUp(MouseEvent& e) { + UpdateMousePosition(e); auto& io = GetIO(); - io.MousePos = ImVec2(float(e->x()), float(e->y())); int button = -1; - switch (e->button()) { + switch (e.button()) { case xe::ui::MouseEvent::Button::kLeft: { button = 0; break; @@ -293,24 +429,42 @@ void ImGuiDrawer::OnMouseUp(MouseEvent* e) { break; } } - if (button >= 0 && button < std::size(io.MouseDown)) { - io.MouseDown[button] = false; - if (!ImGui::IsAnyMouseDown()) { - window_->ReleaseMouse(); + if (io.MouseDown[button]) { + io.MouseDown[button] = false; + if (!ImGui::IsAnyMouseDown()) { + window_->ReleaseMouse(); + } } } } -void ImGuiDrawer::OnMouseWheel(MouseEvent* e) { +void ImGuiDrawer::OnMouseWheel(MouseEvent& e) { + UpdateMousePosition(e); auto& io = GetIO(); - io.MousePos = ImVec2(float(e->x()), float(e->y())); - io.MouseWheel += float(e->dy() / 120.0f); + io.MouseWheel += float(e.scroll_y()) / float(MouseEvent::kScrollPerDetent); } -void ImGuiDrawer::OnKey(KeyEvent* e, bool is_down) { +void ImGuiDrawer::ClearInput() { auto& io = GetIO(); - VirtualKey virtual_key = e->virtual_key(); + if (ImGui::IsAnyMouseDown()) { + window_->ReleaseMouse(); + } + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + std::memset(io.MouseDown, 0, sizeof(io.MouseDown)); + io.MouseWheel = 0.0f; + io.MouseWheelH = 0.0f; + io.KeyCtrl = false; + io.KeyShift = false; + io.KeyAlt = false; + io.KeySuper = false; + std::memset(io.KeysDown, 0, sizeof(io.KeysDown)); + io.ClearInputCharacters(); +} + +void ImGuiDrawer::OnKey(KeyEvent& e, bool is_down) { + auto& io = GetIO(); + VirtualKey virtual_key = e.virtual_key(); if (size_t(virtual_key) < xe::countof(io.KeysDown)) { io.KeysDown[size_t(virtual_key)] = is_down; } @@ -333,5 +487,13 @@ void ImGuiDrawer::OnKey(KeyEvent* e, bool is_down) { } } +void ImGuiDrawer::UpdateMousePosition(const MouseEvent& e) { + auto& io = GetIO(); + float physical_to_logical = + float(window_->GetMediumDpi()) / float(window_->GetDpi()); + io.MousePos.x = e.x() * physical_to_logical; + io.MousePos.y = e.y() * physical_to_logical; +} + } // namespace ui } // namespace xe diff --git a/src/xenia/ui/imgui_drawer.h b/src/xenia/ui/imgui_drawer.h index 2c370b2aa..fee5c4242 100644 --- a/src/xenia/ui/imgui_drawer.h +++ b/src/xenia/ui/imgui_drawer.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,10 +10,14 @@ #ifndef XENIA_UI_IMGUI_DRAWER_H_ #define XENIA_UI_IMGUI_DRAWER_H_ +#include +#include #include #include #include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/presenter.h" +#include "xenia/ui/window.h" #include "xenia/ui/window_listener.h" struct ImDrawData; @@ -23,43 +27,73 @@ struct ImGuiIO; namespace xe { namespace ui { -class GraphicsContext; +class ImGuiDialog; class Window; -class ImGuiDrawer : public WindowListener { +class ImGuiDrawer : public WindowInputListener, public UIDrawer { public: - ImGuiDrawer(Window* window); + ImGuiDrawer(Window* window, size_t z_order); ~ImGuiDrawer(); - void SetupDefaultInput() {} - ImGuiIO& GetIO(); - void RenderDrawLists(); + + void AddDialog(ImGuiDialog* dialog); + void RemoveDialog(ImGuiDialog* dialog); + + // SetPresenter may be called from the destructor. + void SetPresenter(Presenter* new_presenter); + void SetImmediateDrawer(ImmediateDrawer* new_immediate_drawer); + void SetPresenterAndImmediateDrawer(Presenter* new_presenter, + ImmediateDrawer* new_immediate_drawer) { + SetPresenter(new_presenter); + SetImmediateDrawer(new_immediate_drawer); + } + + void Draw(UIDrawContext& ui_draw_context) override; protected: - void Initialize(); - void SetupFont(); - - void RenderDrawLists(ImDrawData* data); - - void OnKeyDown(KeyEvent* e) override; - void OnKeyUp(KeyEvent* e) override; - void OnKeyChar(KeyEvent* e) override; - void OnMouseDown(MouseEvent* e) override; - void OnMouseMove(MouseEvent* e) override; - void OnMouseUp(MouseEvent* e) override; - void OnMouseWheel(MouseEvent* e) override; - - static ImGuiDrawer* current_drawer_; - - Window* window_ = nullptr; - GraphicsContext* graphics_context_ = nullptr; - - ImGuiContext* internal_state_ = nullptr; - std::unique_ptr font_texture_; + void OnKeyDown(KeyEvent& e) override; + void OnKeyUp(KeyEvent& e) override; + void OnKeyChar(KeyEvent& e) override; + void OnMouseDown(MouseEvent& e) override; + void OnMouseMove(MouseEvent& e) override; + void OnMouseUp(MouseEvent& e) override; + void OnMouseWheel(MouseEvent& e) override; + // For now, no need for OnDpiChanged because redrawing is done continuously. private: - void OnKey(KeyEvent* e, bool is_down); + void Initialize(); + + void SetupFontTexture(); + + void RenderDrawLists(ImDrawData* data, UIDrawContext& ui_draw_context); + + void ClearInput(); + void OnKey(KeyEvent& e, bool is_down); + void UpdateMousePosition(const MouseEvent& e); + + Window* window_; + size_t z_order_; + + ImGuiContext* internal_state_ = nullptr; + + // All currently-attached dialogs that get drawn. + std::vector dialogs_; + // Using an index, not an iterator, because after the erasure, the adjustment + // must be done for the vector element indices that would be in the iterator + // range that would be invalidated. + // SIZE_MAX if not currently in the dialog loop. + size_t dialog_loop_next_index_ = SIZE_MAX; + + Presenter* presenter_ = nullptr; + + ImmediateDrawer* immediate_drawer_ = nullptr; + // Resources specific to an immediate drawer - must be destroyed before + // detaching the presenter. + std::unique_ptr font_texture_; + + double frame_time_tick_frequency_; + uint64_t last_frame_time_ticks_; }; } // namespace ui diff --git a/src/xenia/ui/immediate_drawer.cc b/src/xenia/ui/immediate_drawer.cc new file mode 100644 index 000000000..fb00be77f --- /dev/null +++ b/src/xenia/ui/immediate_drawer.cc @@ -0,0 +1,108 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/immediate_drawer.h" + +#include + +#include "xenia/base/assert.h" +#include "xenia/ui/graphics_util.h" +#include "xenia/ui/presenter.h" + +namespace xe { +namespace ui { + +void ImmediateDrawer::SetPresenter(Presenter* new_presenter) { + if (presenter_ == new_presenter) { + return; + } + // Changing the presenter while drawing would make the state inconsistent. + assert_null(ui_draw_context_); + if (presenter_) { + OnLeavePresenter(); + } + presenter_ = new_presenter; + if (presenter_) { + OnEnterPresenter(); + } +} + +void ImmediateDrawer::Begin(UIDrawContext& ui_draw_context, + float coordinate_space_width, + float coordinate_space_height) { + assert_true(&ui_draw_context.presenter() == presenter_); + ui_draw_context_ = &ui_draw_context; + // In case of non-positive values (or NaNs) - use render target coordinates + // according to the contract of the function, and also for safety because + // there will be division by the coordinate space size in several places. + if (!(coordinate_space_width > 0.0f) || !(coordinate_space_height > 0.0f)) { + coordinate_space_width = float(ui_draw_context.render_target_width()); + coordinate_space_height = float(ui_draw_context.render_target_height()); + } + coordinate_space_width_ = coordinate_space_width; + coordinate_space_height_ = coordinate_space_height; +} + +void ImmediateDrawer::End() { ui_draw_context_ = nullptr; } + +bool ImmediateDrawer::ScissorToRenderTarget(const ImmediateDraw& immediate_draw, + uint32_t& out_left, + uint32_t& out_top, + uint32_t& out_width, + uint32_t& out_height) { + uint32_t render_target_width = ui_draw_context()->render_target_width(); + uint32_t render_target_height = ui_draw_context()->render_target_height(); + if (!immediate_draw.scissor) { + out_left = 0; + out_top = 0; + out_width = render_target_width; + out_height = render_target_height; + return render_target_width && render_target_height; + } + float render_target_width_float = float(render_target_width); + float render_target_height_float = float(render_target_height); + // Scale to render target coordinates, drop NaNs (by doing + // std::max(0.0f, variable) in this argument order), and clamp to the render + // target size, below which the values are representable as 16p8 fixed-point. + float scale_x = render_target_width / coordinate_space_width(); + float scale_y = render_target_height / coordinate_space_height(); + float x0_float = + std::min(render_target_width_float, + std::max(0.0f, immediate_draw.scissor_left * scale_x)); + float y0_float = + std::min(render_target_height_float, + std::max(0.0f, immediate_draw.scissor_top * scale_y)); + // Also make sure the size is non-negative. + float x1_float = + std::min(render_target_width_float, + std::max(x0_float, immediate_draw.scissor_right * scale_x)); + float y1_float = + std::min(render_target_height_float, + std::max(y0_float, immediate_draw.scissor_bottom * scale_y)); + // Top-left - include .5 (0.128 treated as 0 covered, 0.129 as 0 not covered). + int32_t x0 = (FloatToD3D11Fixed16p8(x0_float) + 127) >> 8; + int32_t y0 = (FloatToD3D11Fixed16p8(y0_float) + 127) >> 8; + // Bottom-right - exclude .5. + int32_t x1 = (FloatToD3D11Fixed16p8(x1_float) + 127) >> 8; + int32_t y1 = (FloatToD3D11Fixed16p8(y1_float) + 127) >> 8; + assert_true(x0 >= 0); + assert_true(y0 >= 0); + assert_true(x1 >= x0); + assert_true(y1 >= y0); + assert_true(x1 <= int32_t(render_target_width)); + assert_true(y1 <= int32_t(render_target_height)); + out_left = uint32_t(x0); + out_top = uint32_t(y0); + out_width = uint32_t(x1 - x0); + out_height = uint32_t(y1 - y0); + return x1 > x0 && y1 > y0; +} + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/immediate_drawer.h b/src/xenia/ui/immediate_drawer.h index 544f16aa1..48bca319c 100644 --- a/src/xenia/ui/immediate_drawer.h +++ b/src/xenia/ui/immediate_drawer.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -12,11 +12,11 @@ #include +#include "xenia/ui/presenter.h" + namespace xe { namespace ui { -class GraphicsContext; - // Describes the filter applied when sampling textures. enum class ImmediateTextureFilter { kNearest, @@ -80,21 +80,38 @@ struct ImmediateDraw { // True to enable scissoring using the region defined by scissor_rect. bool scissor = false; - // Scissoring region in framebuffer pixels as (x, y, w, h). - int scissor_rect[4] = {0}; + // Scissoring region in the coordinate space (if right < left or bottom < top, + // not drawing). + float scissor_left = 0.0f; + float scissor_top = 0.0f; + float scissor_right = 0.0f; + float scissor_bottom = 0.0f; }; class ImmediateDrawer { public: + ImmediateDrawer(const ImmediateDrawer& immediate_drawer) = delete; + ImmediateDrawer& operator=(const ImmediateDrawer& immediate_drawer) = delete; + virtual ~ImmediateDrawer() = default; + void SetPresenter(Presenter* new_presenter); + // Creates a new texture with the given attributes and R8G8B8A8 data. virtual std::unique_ptr CreateTexture( uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool is_repeated, const uint8_t* data) = 0; - // Begins drawing in immediate mode using the given projection matrix. - virtual void Begin(int render_target_width, int render_target_height) = 0; + // Begins drawing in immediate mode using the given projection matrix. The + // presenter that is currently attached to the immediate drawer, as the + // implementation may hold presenter-specific information such as UI + // submission indices. Pass 0 or a negative value as the coordinate space + // width or height to use raw render target pixel coordinates (or this will + // just be used as a safe fallback when with a non-zero-sized surface the + // coordinate space size becomes zero somehow). + virtual void Begin(UIDrawContext& ui_draw_context, + float coordinate_space_width, + float coordinate_space_height); // Starts a draw batch. virtual void BeginDrawBatch(const ImmediateDrawBatch& batch) = 0; // Draws one set of a batch. @@ -102,13 +119,33 @@ class ImmediateDrawer { // Ends a draw batch. virtual void EndDrawBatch() = 0; // Ends drawing in immediate mode and flushes contents. - virtual void End() = 0; + virtual void End(); protected: - ImmediateDrawer(GraphicsContext* graphics_context) - : graphics_context_(graphics_context) {} + ImmediateDrawer() = default; - GraphicsContext* graphics_context_ = nullptr; + Presenter* presenter() const { return presenter_; } + virtual void OnLeavePresenter() {} + virtual void OnEnterPresenter() {} + + // Available between Begin and End. + UIDrawContext* ui_draw_context() const { return ui_draw_context_; } + float coordinate_space_width() const { return coordinate_space_width_; } + float coordinate_space_height() const { return coordinate_space_height_; } + + // Converts and clamps the scissor in the immediate draw to render target + // coordinates. Returns whether the scissor contains any render target pixels + // (but a valid scissor is written even if false is returned). + bool ScissorToRenderTarget(const ImmediateDraw& immediate_draw, + uint32_t& out_left, uint32_t& out_top, + uint32_t& out_width, uint32_t& out_height); + + private: + Presenter* presenter_ = nullptr; + + UIDrawContext* ui_draw_context_ = nullptr; + float coordinate_space_width_; + float coordinate_space_height_; }; } // namespace ui diff --git a/src/xenia/ui/menu_item.cc b/src/xenia/ui/menu_item.cc index 5563b8760..225ca6444 100644 --- a/src/xenia/ui/menu_item.cc +++ b/src/xenia/ui/menu_item.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -62,9 +62,11 @@ void MenuItem::RemoveChild(MenuItem* child_item) { MenuItem* MenuItem::child(size_t index) { return children_[index].get(); } -void MenuItem::OnSelected(UIEvent* e) { +void MenuItem::OnSelected() { if (callback_) { callback_(); + // Note that this MenuItem might have been destroyed by the callback. + // Must not do anything with *this in this function from now on. } } diff --git a/src/xenia/ui/menu_item.h b/src/xenia/ui/menu_item.h index 8b61f040a..01bcbe5c5 100644 --- a/src/xenia/ui/menu_item.h +++ b/src/xenia/ui/menu_item.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -48,14 +48,18 @@ class MenuItem { const std::string& text() { return text_; } const std::string& hotkey() { return hotkey_; } + // If the menu is currently attached to a Window, changes to it (such as the + // elements and the enabled / disabled state) may be not reflected + // immediately - call Window::CompleteMainMenuItemsUpdate when the + // modifications are done. + void AddChild(MenuItem* child_item); void AddChild(std::unique_ptr child_item); void AddChild(MenuItemPtr child_item); void RemoveChild(MenuItem* child_item); MenuItem* child(size_t index); - virtual void EnableMenuItem(Window& window) = 0; - virtual void DisableMenuItem(Window& window) = 0; + virtual void SetEnabled(bool enabled) {} protected: MenuItem(Type type, const std::string& text, const std::string& hotkey, @@ -64,13 +68,17 @@ class MenuItem { virtual void OnChildAdded(MenuItem* child_item) {} virtual void OnChildRemoved(MenuItem* child_item) {} - virtual void OnSelected(UIEvent* e); + // This MenuItem may be destroyed as a result of the callback, don't do + // anything with it after the call. + void OnSelected(); Type type_; MenuItem* parent_item_; std::vector children_; std::string text_; std::string hotkey_; + + private: std::function callback_; }; diff --git a/src/xenia/ui/microprofile_drawer.cc b/src/xenia/ui/microprofile_drawer.cc index fb6588348..9680a492c 100644 --- a/src/xenia/ui/microprofile_drawer.cc +++ b/src/xenia/ui/microprofile_drawer.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,9 +10,10 @@ #include "xenia/ui/microprofile_drawer.h" #include +#include #include "xenia/base/math.h" -#include "xenia/ui/window.h" +#include "xenia/base/profiling.h" namespace xe { namespace ui { @@ -124,10 +125,8 @@ const uint8_t kFontData[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }; -MicroprofileDrawer::MicroprofileDrawer(xe::ui::Window* window) - : window_(window), - graphics_context_(window->context()), - vertices_(kMaxVertices) { +MicroprofileDrawer::MicroprofileDrawer(ImmediateDrawer* immediate_drawer) + : immediate_drawer_(immediate_drawer), vertices_(kMaxVertices) { SetupFont(); } @@ -171,21 +170,21 @@ void MicroprofileDrawer::SetupFont() { } } - font_texture_ = graphics_context_->immediate_drawer()->CreateTexture( + font_texture_ = immediate_drawer_->CreateTexture( kFontTextureWidth, kFontTextureHeight, ImmediateTextureFilter::kNearest, false, reinterpret_cast(unpacked)); } -MicroprofileDrawer::~MicroprofileDrawer() = default; - -void MicroprofileDrawer::Begin() { - graphics_context_->immediate_drawer()->Begin(window_->scaled_width(), - window_->scaled_height()); +void MicroprofileDrawer::Begin(UIDrawContext& ui_draw_context, + uint32_t coordinate_space_width, + uint32_t coordinate_space_height) { + immediate_drawer_->Begin(ui_draw_context, float(coordinate_space_width), + float(coordinate_space_height)); } void MicroprofileDrawer::End() { Flush(); - graphics_context_->immediate_drawer()->End(); + immediate_drawer_->End(); } ImmediateVertex* MicroprofileDrawer::BeginVertices( @@ -203,7 +202,6 @@ ImmediateVertex* MicroprofileDrawer::BeginVertices( void MicroprofileDrawer::EndVertices() {} void MicroprofileDrawer::Flush() { - auto drawer = graphics_context_->immediate_drawer(); if (!vertex_count_) { return; } @@ -211,15 +209,15 @@ void MicroprofileDrawer::Flush() { ImmediateDrawBatch batch; batch.vertices = vertices_.data(); batch.vertex_count = vertex_count_; - drawer->BeginDrawBatch(batch); + immediate_drawer_->BeginDrawBatch(batch); ImmediateDraw draw; draw.primitive_type = current_primitive_type_; draw.count = vertex_count_; draw.texture = font_texture_.get(); - drawer->Draw(draw); + immediate_drawer_->Draw(draw); - drawer->EndDrawBatch(); + immediate_drawer_->EndDrawBatch(); vertex_count_ = 0; } @@ -323,8 +321,8 @@ void MicroprofileDrawer::DrawLine2D(uint32_t count, float* vertices, EndVertices(); } -void MicroprofileDrawer::DrawText(int x, int y, uint32_t color, - const char* text, int text_length) { +void MicroprofileDrawer::DrawTextString(int x, int y, uint32_t color, + const char* text, int text_length) { if (!text_length) { return; } diff --git a/src/xenia/ui/microprofile_drawer.h b/src/xenia/ui/microprofile_drawer.h index 1f285e1bc..5313935dd 100644 --- a/src/xenia/ui/microprofile_drawer.h +++ b/src/xenia/ui/microprofile_drawer.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,6 +10,7 @@ #ifndef XENIA_UI_MICROPROFILE_DRAWER_H_ #define XENIA_UI_MICROPROFILE_DRAWER_H_ +#include #include #include @@ -18,9 +19,6 @@ namespace xe { namespace ui { -class GraphicsContext; -class Window; - class MicroprofileDrawer { public: enum class BoxType { @@ -28,15 +26,17 @@ class MicroprofileDrawer { kFlat = 1, // MicroProfileBoxTypeFlat }; - MicroprofileDrawer(Window* window); - ~MicroprofileDrawer(); + // Initially hidden. + MicroprofileDrawer(ImmediateDrawer* immediate_drawer); - void Begin(); + void Begin(UIDrawContext& ui_draw_context, uint32_t coordinate_space_width, + uint32_t coordinate_space_height); void End(); void DrawBox(int x0, int y0, int x1, int y1, uint32_t color, BoxType type); void DrawLine2D(uint32_t count, float* vertices, uint32_t color); - void DrawText(int x, int y, uint32_t color, const char* text, - int text_length); + // The name DrawTextString collides with DrawText in Windows. + void DrawTextString(int x, int y, uint32_t color, const char* text, + int text_length); protected: void SetupFont(); @@ -46,8 +46,7 @@ class MicroprofileDrawer { void EndVertices(); void Flush(); - Window* window_ = nullptr; - GraphicsContext* graphics_context_ = nullptr; + ImmediateDrawer* immediate_drawer_; std::vector vertices_; int vertex_count_ = 0; diff --git a/src/xenia/ui/premake5.lua b/src/xenia/ui/premake5.lua index 540c27154..3b9e482c4 100644 --- a/src/xenia/ui/premake5.lua +++ b/src/xenia/ui/premake5.lua @@ -18,3 +18,8 @@ project("xenia-ui") filter("platforms:Android-*") -- Exports JNI functions. wholelib("On") + + filter("platforms:Windows") + links({ + "DXGI", + }) diff --git a/src/xenia/ui/presenter.cc b/src/xenia/ui/presenter.cc new file mode 100644 index 000000000..aef8f7309 --- /dev/null +++ b/src/xenia/ui/presenter.cc @@ -0,0 +1,1446 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/presenter.h" + +#include +#include + +#include "xenia/base/assert.h" +#include "xenia/base/cvar.h" +#include "xenia/base/logging.h" +#include "xenia/base/platform.h" +#include "xenia/ui/window.h" + +#if XE_PLATFORM_WIN32 +#include "xenia/ui/window_win.h" +#endif + +// On Windows, InvalidateRect causes WM_PAINT to be sent quite quickly, so +// presenting from the thread refreshing the guest output is not absolutely +// necessary, but still may be nice for bypassing the scheduling and the +// message queue. +// On GTK, the frame rate of draw signals is limited to the display refresh rate +// internally, so for the lowest latency especially in case the refresh rates +// differ significantly on the guest and the host (like 30/60 Hz presented to +// 144 Hz), drawing from the guest output refreshing thread is highly desirable. +// Presenting directly from the GPU emulation thread also makes debugging GPU +// emulation easier with external tools, as presenting in most cases happens +// exactly between emulation frames. +DEFINE_bool( + host_present_from_non_ui_thread, true, + "Allow the GPU emulation thread to present the guest output to the host " + "surface directly instead of requesting the UI thread to do so through the " + "host window system.", + "Display"); + +DEFINE_bool( + present_render_pass_clear, true, + "On graphics backends where this is supported, use the clear render pass " + "load operation in presentation instead of clear commands clearing only " + "the letterbox area.", + "Display"); + +DEFINE_bool( + present_letterbox, true, + "Maintain aspect ratio when stretching by displaying bars around the image " + "when there's no more overscan area to crop out.", + "Display"); +// https://github.com/MonoGame/MonoGame/issues/4697#issuecomment-217779403 +// Using the value from DirectXTK (5% cropped out from each side, thus 90%), +// which is not exactly the Xbox One title-safe area, but close, and within the +// action-safe area: +// https://github.com/microsoft/DirectXTK/blob/1e80a465c6960b457ef9ab6716672c1443a45024/Src/SimpleMath.cpp#L144 +// XNA TitleSafeArea is 80%, but it's very conservative, designed for CRT, and +// is the title-safe area rather than the action-safe area. +// 90% is also exactly the fraction of 16:9 height in 16:10. +DEFINE_int32( + present_safe_area_x, 90, + "Percentage of the image width that can be kept when presenting to " + "maintain aspect ratio without letterboxing or stretching.", + "Display"); +DEFINE_int32( + present_safe_area_y, 90, + "Percentage of the image height that can be kept when presenting to " + "maintain aspect ratio without letterboxing or stretching.", + "Display"); + +namespace xe { +namespace ui { + +void Presenter::FatalErrorHostGpuLossCallback( + [[maybe_unused]] bool is_responsible, + [[maybe_unused]] bool statically_from_ui_thread) { + xe::FatalError("Graphics device lost (probably due to an internal error)"); +} + +Presenter::~Presenter() { + // No intrusive lifetime management must be performed from UI drawers - defer + // it if needed. + assert_false(is_executing_ui_drawers_); + +#if XE_PLATFORM_WIN32 + if (dxgi_ui_tick_thread_.joinable()) { + { + std::scoped_lock dxgi_ui_tick_lock(dxgi_ui_tick_mutex_); + dxgi_ui_tick_thread_shutdown_ = true; + } + dxgi_ui_tick_control_condition_.notify_all(); + dxgi_ui_tick_thread_.join(); + } +#endif // XE_PLATFORM + + if (window_) { + Window* old_window = window_; + // Null the pointer to prevent an infinite loop between SetPresenter and + // SetWindowSurfaceFromUIThread calling each other. + window_ = nullptr; + old_window->SetPresenter(nullptr); + } +} + +void Presenter::SetWindowSurfaceFromUIThread(Window* new_window, + Surface* new_surface) { + // No intrusive lifetime management must be performed from UI drawers - defer + // it if needed. + assert_false(is_executing_ui_drawers_); + + // There can't be a valid surface pointer without a window, as a surface is + // created and owned by the window. + assert_false(new_surface && !new_window); + + if (window_ == new_window && (!window_ || surface_ == new_surface)) { + // Nothing has changed (or a recursive SetWindowSurfaceFromUIThread > + // SetPresenter > SetWindowSurfaceFromUIThread call). + return; + } + + // Disconnect from the current surface. + if (surface_) { + // Take ownership of painting, and also stop accepting paint requests from + // the guest output thread - the window (which is required for making them) + // may be going away, and there will be a forced paint when the connection + // becomes available. + SetPaintModeFromUIThread(PaintMode::kNone); + DisconnectPaintingFromSurfaceFromUIThread( + SurfacePaintConnectionState::kUnconnectedRetryAtStateChange); + surface_ = nullptr; + UpdateSurfaceMonitorFromUIThread(true); + } + + if (window_ != new_window) { + // The window pointer may be accessed by the guest output thread if painting + // is possible (or was possible, but the paint attempt has resulted in the + // implementation reporting that the connection has become outdated). + // However, a painting connection is currently not established at all, so + // it's safe to modify the window pointer here. + + // Detach from the old window if attaching to a different one or just + // detaching. SetPresenter for the new window might have been called without + // it having been called with nullptr for the old window. + if (window_) { + Window* old_window = window_; + // Null the pointer to prevent an infinite loop between SetPresenter and + // SetWindowSurfaceFromUIThread calling each other. + window_ = nullptr; + old_window->SetPresenter(nullptr); + } + + // Attach to the new one. + // This function is called from SetPresenter - don't need to notify the + // window of this, as it itself has triggered this. + window_ = new_window; + } + + if (new_surface) { + assert_true(paint_mode_ == PaintMode::kNone); + surface_ = new_surface; + UpdateSurfaceMonitorFromUIThread(true); + assert_true(surface_paint_connection_state_ == + SurfacePaintConnectionState::kUnconnectedRetryAtStateChange); + bool request_repaint; + UpdateSurfacePaintConnectionFromUIThread(&request_repaint, true); + // Request to paint as soon as possible in the UI thread if connected + // successfully. + if (request_repaint) { + RequestPaintOrConnectionRecoveryViaWindow(true); + } + } +} + +void Presenter::OnSurfaceMonitorUpdateFromUIThread( + bool old_monitor_potentially_disconnected) { + // No intrusive lifetime management must be performed from UI drawers - defer + // it if needed. + assert_false(is_executing_ui_drawers_); + + if (!surface_) { + return; + } + + UpdateSurfaceMonitorFromUIThread(old_monitor_potentially_disconnected); +} + +void Presenter::OnSurfaceResizeFromUIThread() { + // No intrusive lifetime management must be performed from UI drawers - defer + // it if needed. + assert_false(is_executing_ui_drawers_); + + if (!surface_) { + return; + } + + // Let the UI thread take ownership of painting (so the connection can be + // updated) in a smooth way - downgrade to kUIThreadOnRequest rather than + // kNone, because a forced repaint may not be necessary if, for example, the + // size internally turns out to be the same after the update, and in this case + // the current image may be kept - but the new one must not be missed either + // if it becomes available during the resize. + if (paint_mode_ == PaintMode::kGuestOutputThreadImmediately) { + SetPaintModeFromUIThread(PaintMode::kUIThreadOnRequest); + } + + bool request_repaint; + UpdateSurfacePaintConnectionFromUIThread(&request_repaint, true); + + // Request to repaint as soon as possible in the UI thread if needed. + if (request_repaint) { + RequestPaintOrConnectionRecoveryViaWindow(true); + } +} + +void Presenter::PaintFromUIThread(bool force_paint) { + // If there is no surface, this will be a no-op, nothing outdated, nothing to + // paint. However, an explicit monitor check is needed because UI framerate + // limiting may be tied to signals from the OS for the monitor - but painting + // may still occur, for instance, if drawing to a composition surface in the + // OS (which will still be live even if the window goes outside any monitor). + // But a surface check still won't cause harm, for simplicity. + if (!InSurfaceOnMonitorFromUIThread()) { + return; + } + + // Defer changes to the paint mode as well as window paint requests, and do + // them in this function so they're consistent with the assumptions made here. + assert_false(is_in_ui_thread_paint_); + is_in_ui_thread_paint_ = true; + request_guest_output_paint_after_current_ui_thread_paint_ = false; + request_ui_paint_after_current_ui_thread_paint_ = false; + + // Actualize the connection if the UI needs to be drawn if there was some + // explicit paint request (the guest output has been refreshed, and the guest + // output thread was asked not to present directly due as the UI needs to be + // drawn, or some surface state change has happened so the guest output needs + // to be displayed as soon as possible without waiting for the guest to + // refresh it, or the guest output thread has been notified that the + // connection has become outdated and has requested the UI thread to + // reconnect). + bool draw_ui = !ui_drawers_.empty(); + bool do_paint = force_paint || draw_ui; + // Reset ui_thread_paint_requested_ unconditionally also, regardless of + // whether the UI needs to be drawn - the flag may be set to try reconnecting, + // for example. + if (ui_thread_paint_requested_.exchange(false, std::memory_order_relaxed)) { + do_paint = true; + } + PaintResult paint_result = PaintResult::kNotPresented; + bool request_repaint_at_tick = false; + bool request_repaint_immediately = false; + if (do_paint) { + // Take ownership of painting if it's currently owned by the guest output + // thread (downgrade from kGuestOutputThreadImmediately to + // kUIThreadOnRequest - not to kNone so if during this paint a new guest + // output frame is generated, the notification will still be sent to the UI + // thread rather than dropped, so the frame won't be skipped). This is + // needed to be able not only to paint, but also to try to recover from an + // outdated surface. + if (paint_mode_ == PaintMode::kGuestOutputThreadImmediately) { + SetPaintModeFromUIThread(PaintMode::kUIThreadOnRequest); + } + // Try to recover from the connection becoming outdated in the previous + // paint. + if (surface_paint_connection_state_ == + SurfacePaintConnectionState::kConnectedOutdated) { + UpdateSurfacePaintConnectionFromUIThread(nullptr, false); + } + // If still paintable or recovered successfully, paint. + if (surface_paint_connection_state_ == + SurfacePaintConnectionState::kConnectedPaintable) { + // The paint mode might have been set to kNone when the connection was + // marked as outdated last time. Or, if wasn't reconnecting, there was + // some other incorrect situation that caused the paint mode to be set to + // kNone for an active connection. Make sure that the current paint mode + // is consistent with painting from the UI thread. + SetPaintModeFromUIThread(PaintMode::kUIThreadOnRequest); + + // Limit the frame rate of the UI, usually to the monitor refresh rate, + // in a way so that the UI won't be stealing all the remaining GPU + // resources if it's repainted continuously, and the window system itself + // doesn't limit the frame rate. + WaitForUITickFromUIThread(); + + paint_result = PaintAndPresent(draw_ui); + if (surface_paint_connection_state_ == + SurfacePaintConnectionState::kConnectedOutdated) { + // Request another PaintFromUIThread which will try to recover from the + // outdated connection in the next frame (not immediately, so the + // windowing system has some time to prepare what may be required to + // recover from it, such as to send a resize event). + request_repaint_immediately = true; + } + } + // If can't paint anymore, notify the paint mode refresh below (which is not + // guaranteed to have access to have ownership of painting as it's taken + // here only conditionally, thus can't know whether the connection is + // actually in a paintable state). + if (surface_paint_connection_state_ != + SurfacePaintConnectionState::kConnectedPaintable) { + SetPaintModeFromUIThread(PaintMode::kNone); + } + } + + // Transfer the ownership of painting back to the guest output thread if it + // was taken or if needed for any reason (however, it's taken conditionally - + // no guarantees that the actual connection state is accessible here, so only + // checking whether the mode is not kNone currently, not the connection + // state), and overall synchronize the state taking into account both what has + // been done in this function and what could have been done by the UI drawer + // callbacks. + if (paint_mode_ != PaintMode::kNone) { + SetPaintModeFromUIThread(GetDesiredPaintModeFromUIThread(true)); + } + is_in_ui_thread_paint_ = false; + + // Check if the device has been lost. There's no point in requesting repaint + // if it has happened anyway, it won't be possible to satisfy such request + // with the current Presenter. + if (paint_result == PaintResult::kGpuLostExternally || + paint_result == PaintResult::kGpuLostResponsible) { + if (host_gpu_loss_callback_) { + host_gpu_loss_callback_(paint_result == PaintResult::kGpuLostResponsible, + true); + } + // The loss callback might have destroyed the presenter, must not do + // anything with `this` anymore. + return; + } + + // Request refresh if needed. + // Can't check the exact paintability as the connection state may currently + // be owned by the guest output thread, so check conservatively via + // paint_mode_. + if (paint_mode_ != PaintMode::kNone) { + // Immediately paint the guest output if requested explicitly or if the UI + // has hidden itself. + if (request_guest_output_paint_after_current_ui_thread_paint_ || + (draw_ui && ui_drawers_.empty())) { + request_repaint_immediately = true; + } + if (request_ui_paint_after_current_ui_thread_paint_ && + !ui_drawers_.empty()) { + request_repaint_at_tick = true; + } + } + if (request_repaint_at_tick || request_repaint_immediately) { + RequestPaintOrConnectionRecoveryViaWindow(request_repaint_immediately); + } +} + +bool Presenter::RefreshGuestOutput( + uint32_t frontbuffer_width, uint32_t frontbuffer_height, + uint32_t screen_width, uint32_t screen_height, + std::function refresher) { + GuestOutputProperties& writable_properties = + guest_output_properties_[guest_output_mailbox_writable_]; + writable_properties.frontbuffer_width = frontbuffer_width; + writable_properties.frontbuffer_height = frontbuffer_height; + writable_properties.screen_width = screen_width; + writable_properties.screen_height = screen_height; + writable_properties.is_8bpc = false; + bool is_active = writable_properties.IsActive(); + if (is_active) { + if (!RefreshGuestOutputImpl(guest_output_mailbox_writable_, + frontbuffer_width, frontbuffer_height, + refresher, writable_properties.is_8bpc)) { + // If failed to refresh, don't send the currently writable image to the + // mailbox as it may be in an undefined state. Don't disable the guest + // output either though because the failure may be something transient. + return false; + } + guest_output_active_last_refresh_ = true; + } else { + // Request presenting a blank image if there was a true image previously, + // but not now. + if (!guest_output_active_last_refresh_) { + return false; + } + guest_output_active_last_refresh_ = false; + } + + // Make the new image the next to present on the host (the "ready" one), + // replacing the one already specified as the next (dropping it instead of + // enqueueing the new image after it) to achieve the lowest latency (also, + // after switching from UI thread painting to doing it in the guest output + // thread, will immediately recover to having the latest frame always sent to + // the host present call on the CPU and all frames reaching a present call). + uint32_t last_acquired_and_ready = + guest_output_mailbox_acquired_and_ready_.load(std::memory_order_relaxed); + // Desired acquired = current acquired (changed only by the consumers). + // Desired ready = current writable. + // memory_order_acq_rel to acquire the new writable image and to release the + // current one (to let the consumers take it, from ready to acquired). + while (!guest_output_mailbox_acquired_and_ready_.compare_exchange_weak( + last_acquired_and_ready, + (last_acquired_and_ready & 3) | (guest_output_mailbox_writable_ << 2), + std::memory_order_acq_rel, std::memory_order_relaxed)) { + } + // Now, it's known that `ready == writable` on the host presentation side. + // Take the next `writable` with this assumption about its current value in + // mind. + uint32_t last_acquired = last_acquired_and_ready & 3; + if (last_acquired == guest_output_mailbox_writable_) { + // The new image has already been acquired by the time the compare-exchange + // loop has finished (acquired == ready == currently writable). + // It's a valid situation from the ownership perspective, and the semantics + // of the weak compare-exchange explicitly permit spurious `false` results. + // (3 - a - b) % 3 cannot be used here, as (3 - a - a) % 3 results in `a` - + // the same index. + // Take any free image. Preferably using + 1, not ^ 1, so if the guest needs + // to await any GPU work referencing the image, it will wait for the frame 3 + // frames ago, not 2, if this happens repeatedly. + guest_output_mailbox_writable_ = (guest_output_mailbox_writable_ + 1) % 3; + } else { + // Take the image other than the last acquired one and the new one, + // currently not accessible to the host presentation. + guest_output_mailbox_writable_ = + (3 - last_acquired - guest_output_mailbox_writable_) % 3; + } + + // Trigger the presentation on the host. + PaintResult paint_result = PaintResult::kNotPresented; + { + std::lock_guard paint_mode_mutex_lock(paint_mode_mutex_); + switch (paint_mode_) { + case PaintMode::kNone: + // Neither painting nor window paint requesting is accessible. + break; + case PaintMode::kUIThreadOnRequest: + // Only window paint requesting is accessible. + RequestPaintOrConnectionRecoveryViaWindow(true); + break; + case PaintMode::kGuestOutputThreadImmediately: + // Both painting and window paint requesting are accessible. + if (surface_paint_connection_state_ == + SurfacePaintConnectionState::kConnectedPaintable) { + paint_result = PaintAndPresent(false); + if (surface_paint_connection_state_ == + SurfacePaintConnectionState::kConnectedOutdated) { + RequestPaintOrConnectionRecoveryViaWindow(true); + } + } + break; + } + } + // Handle GPU loss when not in the middle of the function anymore, and + // lifecycle management from the GPU loss callback is fine on the UI thread. + if (host_gpu_loss_callback_) { + if (paint_result == PaintResult::kGpuLostResponsible) { + host_gpu_loss_callback_(true, false); + } else if (paint_result == PaintResult::kGpuLostExternally) { + host_gpu_loss_callback_(false, false); + } + } + + return is_active; +} + +void Presenter::SetGuestOutputPaintConfigFromUIThread( + const GuestOutputPaintConfig& new_config) { + // For simplicity, this may be called externally repeatedly. + // Lock the mutex only when something has been modified, and also don't + // request UI thread guest output redraws when not needed. + bool modified = false; + bool request_repaint = false; + if (guest_output_paint_config_.GetEffect() != new_config.GetEffect()) { + modified = true; + request_repaint = true; + } + if (guest_output_paint_config_.GetFsrSharpnessReduction() != + new_config.GetFsrSharpnessReduction()) { + modified = true; + if (new_config.GetEffect() == GuestOutputPaintConfig::Effect::kFsr) { + request_repaint = true; + } + } + if (guest_output_paint_config_.GetCasAdditionalSharpness() != + new_config.GetCasAdditionalSharpness()) { + modified = true; + if (new_config.GetEffect() == GuestOutputPaintConfig::Effect::kCas || + new_config.GetEffect() == GuestOutputPaintConfig::Effect::kFsr) { + request_repaint = true; + } + } + if (guest_output_paint_config_.GetDither() != new_config.GetDither()) { + modified = true; + request_repaint = true; + } + if (modified) { + { + std::unique_lock config_lock( + guest_output_paint_config_mutex_); + guest_output_paint_config_ = new_config; + } + // Coarsely check the availability of painting and of the window (for + // calling RequestPaint) via paint_mode_ because the actual painting + // connection state may currently be owned not by the UI thread. + if (request_repaint && paint_mode_ != PaintMode::kNone) { + if (is_in_ui_thread_paint_) { + // Defer until the end of the current paint if called from, for + // instance, a UI drawer. + request_guest_output_paint_after_current_ui_thread_paint_ = true; + } else { + RequestPaintOrConnectionRecoveryViaWindow(true); + } + } + } +} + +void Presenter::AddUIDrawerFromUIThread(UIDrawer* drawer, size_t z_order) { + assert_not_null(drawer); + // Obtain whether the iterator list was empty before erasing in case of + // replacing with a new entry with a different Z order happens. + bool drawers_were_empty = ui_drawers_.empty(); + uint64_t drawer_last_draw = UINT64_MAX; + // Check if already added. + for (auto it_existing = ui_drawers_.begin(); it_existing != ui_drawers_.end(); + ++it_existing) { + if (it_existing->second.drawer != drawer) { + continue; + } + if (it_existing->first == z_order) { + return; + } + // Keep the same last draw index to prevent the drawer from being executed + // twice if increasing its Z order during the drawer loop. + drawer_last_draw = it_existing->second.last_draw; + // If removing the drawer that is the next in the current drawer loop, skip + // it (in a multimap, only one element iterator is invalidated). + if (is_executing_ui_drawers_ && ui_draw_next_iterator_ == it_existing) { + ++ui_draw_next_iterator_; + } + ui_drawers_.erase(it_existing); + break; + } + auto it_new = + ui_drawers_.emplace(z_order, UIDrawerReference(drawer, drawer_last_draw)); + // If adding to the Z layer currently being processed (for drawing, from the + // lowest to the highest), or to layers in between the current and the + // previously next, make sure the new drawer is executed too. + if (is_executing_ui_drawers_ && z_order >= ui_draw_current_z_order_ && + (ui_draw_next_iterator_ == ui_drawers_.end() || + z_order < ui_draw_next_iterator_->first)) { + ui_draw_next_iterator_ = it_new; + } + HandleUIDrawersChangeFromUIThread(drawers_were_empty); +} + +void Presenter::RemoveUIDrawerFromUIThread(UIDrawer* drawer) { + assert_not_null(drawer); + for (auto it_existing = ui_drawers_.begin(); it_existing != ui_drawers_.end(); + ++it_existing) { + if (it_existing->second.drawer != drawer) { + continue; + } + // If removing the drawer that is the next in the current drawer loop, skip + // it (in a multimap, only one element iterator is invalidated). + if (is_executing_ui_drawers_ && ui_draw_next_iterator_ == it_existing) { + ++ui_draw_next_iterator_; + } + ui_drawers_.erase(it_existing); + HandleUIDrawersChangeFromUIThread(false); + return; + } +} + +void Presenter::RequestUIPaintFromUIThread() { + if (is_in_ui_thread_paint_) { + // The paint request will be done once in the end of PaintFromUIThread + // according to the actual state at the moment that happens. It's common for + // drawers to call this (even every frame), and no need to do too many OS + // paint request calls. + request_ui_paint_after_current_ui_thread_paint_ = true; + return; + } + // The connection state may be owned by the guest output thread now rather + // than the UI thread, check whether it's not pointless to make the request + // coarsely via paint_mode_. + if (!ui_drawers_.empty() && paint_mode_ != PaintMode::kNone) { + // The window must be present, otherwise the conditions wouldn't have been + // met. + window_->RequestPaint(); + } +} + +bool Presenter::InitializeCommonSurfaceIndependent() { + // Initialize UI frame rate limiting. +#if XE_PLATFORM_WIN32 + dxgi_ui_tick_thread_ = std::thread(&Presenter::DXGIUITickThread, this); +#endif // XE_PLATFORM + + return true; +} + +std::unique_lock Presenter::ConsumeGuestOutput( + uint32_t& mailbox_index_or_max_if_inactive_out, + GuestOutputProperties* properties_out, + GuestOutputPaintConfig* paint_config_out) { + if (paint_config_out) { + // Get the up-to-date guest output paint configuration settings set by the + // UI thread. + std::unique_lock config_lock(guest_output_paint_config_mutex_); + *paint_config_out = guest_output_paint_config_; + } + + // Lock the mutex to make sure the image that will be acquired now is owned + // exclusively by the calling thread for the time while this mutex is still + // locked (it needs to be held by the consumer while working with anything + // that depends on the image now being acquired or its index in the mailbox). + std::unique_lock consumer_lock( + guest_output_mailbox_consumer_mutex_); + // Acquire the up-to-date ready guest image (may be new, in this case the last + // acquired one will be released, or still the same or no refresh has happened + // since the last consumption). + // memory_order_relaxed here because the ready index from this load will be + // used directly only if it's the same as during the last consumption - thus + // the image has already been acquired previously, no need for + // memory_order_acquire (if the image has been acquired by a different + // consumer though, the consumer mutex performs memory access ordering). + uint32_t old_acquired_and_ready = + guest_output_mailbox_acquired_and_ready_.load(std::memory_order_relaxed); + // Desired acquired = current ready. + // Desired ready = current ready (changed only by the producer). + uint32_t desired_acquired_and_ready = + (old_acquired_and_ready & ~uint32_t(3)) | (old_acquired_and_ready >> 2); + // Either the same image as during the last consumption, or a new one, is + // satisfying. However, if it's new, using memory_order_acq_rel to acquire the + // new ready image (to make it acquired) and to release the old acquired image + // (to let the producer take it as writable). + while (old_acquired_and_ready != desired_acquired_and_ready && + !guest_output_mailbox_acquired_and_ready_.compare_exchange_weak( + old_acquired_and_ready, desired_acquired_and_ready, + std::memory_order_acq_rel, std::memory_order_relaxed)) { + desired_acquired_and_ready = + (old_acquired_and_ready & ~uint32_t(3)) | (old_acquired_and_ready >> 2); + } + uint32_t mailbox_index = desired_acquired_and_ready & 3; + // Give the current acquired image to the caller, or UINT32_MAX if it's + // inactive. + const GuestOutputProperties& properties = + guest_output_properties_[mailbox_index]; + mailbox_index_or_max_if_inactive_out = + properties.IsActive() ? mailbox_index : UINT32_MAX; + if (properties_out) { + *properties_out = properties; + } + return std::move(consumer_lock); +} + +Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow( + const GuestOutputProperties& properties, uint32_t host_rt_width, + uint32_t host_rt_height, uint32_t max_rt_width, uint32_t max_rt_height, + const GuestOutputPaintConfig& config) const { + GuestOutputPaintFlow flow = {}; + + // FIXME(Triang3l): Configuration variables racing with per-game config + // loading. + + assert_not_zero(max_rt_width); + assert_not_zero(max_rt_height); + + // Initialize one clear rectangle for the case of drawing no guest output, for + // consistency with fewer state dependencies. + flow.letterbox_clear_rectangle_count = 1; + flow.letterbox_clear_rectangles[0].width = host_rt_width; + flow.letterbox_clear_rectangles[0].height = host_rt_height; + + // For safety such as division by zero prevention. + if (!properties.IsActive() || !host_rt_width || !host_rt_height || + !surface_width_in_paint_connection_ || + !surface_height_in_paint_connection_) { + return flow; + } + + flow.properties = properties; + + // Multiplication-division rounding to the nearest. + auto rescale_unsigned = [](uint32_t value, uint32_t new_scale, + uint32_t old_scale) -> uint32_t { + return uint32_t((uint64_t(value) * new_scale + (old_scale >> 1)) / + old_scale); + }; + auto rescale_signed = [](int32_t value, uint32_t new_scale, + uint32_t old_scale) -> int32_t { + // Plus old_scale / 2 for positive values, minus old_scale / 2 for + // negative values for consistent rounding for both positive and + // negative values (as the `/` operator rounds towards zero). + // Example: + // (-3 - 1) / 3 == -1 + // (-2 - 1) / 3 == -1 + // (-1 - 1) / 3 == 0 + // --- + // (0 + 1) / 3 == 0 + // (1 + 1) / 3 == 0 + // (2 + 1) / 3 == 1 + return int32_t((int64_t(value) * new_scale + + int32_t(old_scale >> 1) * (value < 0 ? -1 : 1)) / + old_scale); + }; + + // Final output location and dimensions. + // All host location calculations are DPI-independent, conceptually depending + // only on the aspect ratios, not the absolute values. + uint32_t output_width, output_height; + if (uint64_t(surface_width_in_paint_connection_) * properties.screen_height > + uint64_t(properties.screen_width) * surface_height_in_paint_connection_) { + // The window is wider that the source - crop along Y to preserve the aspect + // ratio while stretching throughout the entire surface's width, then limit + // the Y cropping via letterboxing or stretching along X. + uint32_t present_safe_area; + if (cvars::present_safe_area_y > 0 && cvars::present_safe_area_y < 100) { + present_safe_area = uint32_t(cvars::present_safe_area_y); + } else { + present_safe_area = 100; + } + // Scale the desired width by the H:W aspect ratio (inverse of W:H) to get + // the height. + output_height = + rescale_unsigned(surface_width_in_paint_connection_, + properties.screen_height, properties.screen_width); + bool letterbox = false; + if (output_height * present_safe_area > + surface_height_in_paint_connection_ * 100) { + // Don't crop out more than the safe area margin - letterbox or stretch. + output_height = rescale_unsigned(surface_height_in_paint_connection_, 100, + present_safe_area); + letterbox = true; + } + if (letterbox && cvars::present_letterbox) { + output_width = rescale_unsigned( + properties.screen_width, surface_height_in_paint_connection_ * 100, + properties.screen_height * present_safe_area); + // output_width might have been rounded up already by rescale_unsigned, so + // rounding down in this division. + flow.output_x = (int32_t(surface_width_in_paint_connection_) - + int32_t(output_width)) / + 2; + } else { + output_width = surface_width_in_paint_connection_; + flow.output_x = 0; + } + // output_height might have been rounded up already by rescale_unsigned, so + // rounding down in this division. + flow.output_y = (int32_t(surface_height_in_paint_connection_) - + int32_t(output_height)) / + 2; + } else { + // The window is taller that the source - crop along X to preserve the + // aspect ratio while stretching throughout the entire surface's height, + // then limit the X cropping via letterboxing or stretching along Y. + uint32_t present_safe_area; + if (cvars::present_safe_area_x > 0 && cvars::present_safe_area_x < 100) { + present_safe_area = uint32_t(cvars::present_safe_area_x); + } else { + present_safe_area = 100; + } + // Scale the desired height by the W:H aspect ratio to get the width. + output_width = + rescale_unsigned(surface_height_in_paint_connection_, + properties.screen_width, properties.screen_height); + bool letterbox = false; + if (output_width * present_safe_area > + surface_width_in_paint_connection_ * 100) { + // Don't crop out more than the safe area margin - letterbox or stretch. + output_width = rescale_unsigned(surface_width_in_paint_connection_, 100, + present_safe_area); + letterbox = true; + } + if (letterbox && cvars::present_letterbox) { + output_height = rescale_unsigned( + properties.screen_height, surface_width_in_paint_connection_ * 100, + properties.screen_width * present_safe_area); + // output_height might have been rounded up already by rescale_unsigned, + // so rounding down in this division. + flow.output_y = (int32_t(surface_height_in_paint_connection_) - + int32_t(output_height)) / + 2; + } else { + output_height = surface_height_in_paint_connection_; + flow.output_y = 0; + } + // output_width might have been rounded up already by rescale_unsigned, so + // rounding down in this division. + flow.output_x = + (int32_t(surface_width_in_paint_connection_) - int32_t(output_width)) / + 2; + } + + // Convert the location from surface pixels (which have 1:1 aspect ratio + // relatively to the physical display) to render target pixels (the render + // target size may be arbitrary with any aspect ratio, but if it's different + // than the surface size, the OS is expected to stretch it to the surface + // boundaries), preserving the aspect ratio. + if (host_rt_width != surface_width_in_paint_connection_) { + flow.output_x = rescale_signed(flow.output_x, host_rt_width, + surface_width_in_paint_connection_); + output_width = rescale_unsigned(output_width, host_rt_width, + surface_width_in_paint_connection_); + } + if (host_rt_height != surface_height_in_paint_connection_) { + flow.output_y = rescale_signed(flow.output_y, host_rt_height, + surface_height_in_paint_connection_); + output_height = rescale_unsigned(output_height, host_rt_height, + surface_height_in_paint_connection_); + } + + // The out-of-bounds checks are needed for correct letterbox calculations. + // Though this normally shouldn't happen, but in case of rounding issues with + // extreme values. + int32_t output_right = flow.output_x + int32_t(output_width); + int32_t output_bottom = flow.output_y + int32_t(output_height); + if (!output_width || !output_height || output_right <= 0 || + output_bottom <= 0 || flow.output_x >= int32_t(host_rt_width) || + flow.output_y >= int32_t(host_rt_height)) { + return flow; + } + + // The output image may have a part of it outside the final render target (if + // using the overscan area to stretch the image to the entire surface while + // preserving the guest aspect ratio if it differs from the host one, for + // instance). While the final render target size is known to be within the + // host render target / image size limit, the intermediate images may be + // larger than that as they include the overscan area that will be outside the + // screen. Make sure the intermediate images can't be larger than the maximum + // render target size. + uint32_t output_width_clamped = std::min(output_width, max_rt_width); + uint32_t output_height_clamped = std::min(output_height, max_rt_height); + + if (config.GetEffect() == GuestOutputPaintConfig::Effect::kCas || + config.GetEffect() == GuestOutputPaintConfig::Effect::kFsr) { + // FidelityFX Super Resolution and Contrast Adaptive Sharpening only work + // good for up to 2x2 upscaling due to the way they fetch texels. + // CAS is primarily a sharpening filter, not an upscaling one (its upscaling + // eliminates reduces blurriness, but doesn't preserve the shapes of edges, + // and executing it multiple times will only result in oversharpening. So, + // using it for scales only of up to 2x2, then simply stretching with + // bilinear filtering. + // EASU of FSR, however, preserves edges, it's not supposed to blur them or + // to make them jagged, so it can be executed multiple times - running + // multiple EASU passes for scale factors of over 2x2. + // Just one EASU pass rather than multiple for scaling to factors bigger + // than 2x2 (especially significantly bigger, such as 1152x640 to 3840x2160, + // or 3.333x3.375) results in blurry edges and an overall noisy look, + // multiple passes improve visual stability. + std::pair ffx_last_size; + if (flow.effect_count) { + ffx_last_size = flow.effect_output_sizes[flow.effect_count - 1]; + } else { + ffx_last_size.first = properties.frontbuffer_width; + ffx_last_size.second = properties.frontbuffer_height; + } + if (config.GetEffect() == GuestOutputPaintConfig::Effect::kFsr && + (ffx_last_size.first < output_width_clamped || + ffx_last_size.second < output_height_clamped)) { + // AMD FidelityFX Super Resolution - upsample along at least one axis. + // Using the output size clamped to the maximum render target size here as + // EASU will always write to intermediate images, and RCAS supports only + // 1:1. + uint32_t easu_max_passes = config.GetFsrMaxUpsamplingPasses(); + uint32_t easu_pass_count = 0; + while (easu_pass_count < easu_max_passes && + (ffx_last_size.first < output_width_clamped || + ffx_last_size.second < output_height_clamped)) { + ffx_last_size.first = + std::min(ffx_last_size.first * uint32_t(2), output_width_clamped); + ffx_last_size.second = + std::min(ffx_last_size.second * uint32_t(2), output_height_clamped); + assert_true(flow.effect_count < flow.effects.size()); + flow.effect_output_sizes[flow.effect_count] = ffx_last_size; + flow.effects[flow.effect_count++] = GuestOutputPaintEffect::kFsrEasu; + ++easu_pass_count; + } + assert_true(flow.effect_count < flow.effects.size()); + flow.effect_output_sizes[flow.effect_count] = ffx_last_size; + flow.effects[flow.effect_count++] = GuestOutputPaintEffect::kFsrRcas; + } else { + // AMD FidelityFX Contrast Adaptive Sharpening - sharpen or downsample, or + // upsample up to 2x2 if CAS is specified to be used for upscaling too. + // Using the unclamped output size as CAS may be the last pass - if a + // bilinear pass is needed afterwards, and the CAS pass will be writing to + // an intermediate image, the CAS pass output size will be clamped while + // adding the bilinear stretch. + std::pair pre_cas_size = ffx_last_size; + ffx_last_size.first = + std::min(ffx_last_size.first * uint32_t(2), output_width); + ffx_last_size.second = + std::min(ffx_last_size.second * uint32_t(2), output_height); + assert_true(flow.effect_count < flow.effects.size()); + flow.effect_output_sizes[flow.effect_count] = ffx_last_size; + flow.effects[flow.effect_count++] = + ffx_last_size == pre_cas_size ? GuestOutputPaintEffect::kCasSharpen + : GuestOutputPaintEffect::kCasResample; + } + } + + std::pair* last_pre_bilinear_effect_size = + flow.effect_count ? &flow.effect_output_sizes[flow.effect_count - 1] + : nullptr; + if (!last_pre_bilinear_effect_size || + last_pre_bilinear_effect_size->first != output_width || + last_pre_bilinear_effect_size->second != output_height) { + // If not using FidelityFX, or it has reached its upscaling capabilities, + // but more is needed, stretch via bilinear filtering. + // Clamp the output size of the last effect to the maximum render target + // size because it will go to an intermediate image now. + if (last_pre_bilinear_effect_size) { + // RCAS only works for 1:1, clamping must be done explicitly for FSR. + assert_false(flow.effects[flow.effect_count - 1] == + GuestOutputPaintEffect::kFsrRcas && + (last_pre_bilinear_effect_size->first > max_rt_width || + last_pre_bilinear_effect_size->second > max_rt_height)); + last_pre_bilinear_effect_size->first = + std::min(last_pre_bilinear_effect_size->first, max_rt_width); + last_pre_bilinear_effect_size->second = + std::min(last_pre_bilinear_effect_size->second, max_rt_height); + } + assert_true(flow.effect_count < flow.effects.size()); + flow.effect_output_sizes[flow.effect_count] = + std::make_pair(output_width, output_height); + flow.effects[flow.effect_count++] = GuestOutputPaintEffect::kBilinear; + } + + assert_not_zero(flow.effect_count); + + if (config.GetDither()) { + // Dithering must be applied only to the final effect since resampling and + // sharpening filters may considering the dithering noise features and + // amplify it. + GuestOutputPaintEffect& last_effect = flow.effects[flow.effect_count - 1]; + switch (last_effect) { + case GuestOutputPaintEffect::kBilinear: + // Dithering has no effect for 1:1 copying of a 8bpc image. + if (!properties.is_8bpc || flow.effect_count > 1 || + output_width != properties.frontbuffer_width || + output_height != properties.frontbuffer_height) { + last_effect = GuestOutputPaintEffect::kBilinearDither; + } + break; + case GuestOutputPaintEffect::kCasSharpen: + last_effect = GuestOutputPaintEffect::kCasSharpenDither; + break; + case GuestOutputPaintEffect::kCasResample: + last_effect = GuestOutputPaintEffect::kCasResampleDither; + break; + case GuestOutputPaintEffect::kFsrRcas: + last_effect = GuestOutputPaintEffect::kFsrRcasDither; + break; + default: + break; + } + } + +#ifndef NDEBUG + for (size_t i = 0; i + 1 < flow.effect_count; ++i) { + assert_true(CanGuestOutputPaintEffectBeIntermediate(flow.effects[i])); + } + assert_true( + CanGuestOutputPaintEffectBeFinal(flow.effects[flow.effect_count - 1])); +#endif + + // Calculate the letterbox geometry. + if (flow.effect_count) { + flow.letterbox_clear_rectangle_count = 0; + uint32_t letterbox_mid_top = uint32_t(std::max(flow.output_y, int32_t(0))); + // Top. + if (letterbox_mid_top) { + assert_true(flow.letterbox_clear_rectangle_count < + flow.letterbox_clear_rectangles.size()); + GuestOutputPaintFlow::ClearRectangle& letterbox_clear_rectangle_top = + flow.letterbox_clear_rectangles + [flow.letterbox_clear_rectangle_count++]; + letterbox_clear_rectangle_top.x = 0; + letterbox_clear_rectangle_top.y = 0; + letterbox_clear_rectangle_top.width = host_rt_width; + letterbox_clear_rectangle_top.height = letterbox_mid_top; + } + uint32_t letterbox_mid_bottom = + std::min(uint32_t(output_bottom), host_rt_height); + uint32_t letterbox_mid_height = letterbox_mid_bottom - letterbox_mid_top; + // Middle-left. + if (flow.output_x > 0) { + assert_true(flow.letterbox_clear_rectangle_count < + flow.letterbox_clear_rectangles.size()); + GuestOutputPaintFlow::ClearRectangle& letterbox_clear_rectangle_left = + flow.letterbox_clear_rectangles + [flow.letterbox_clear_rectangle_count++]; + letterbox_clear_rectangle_left.x = 0; + letterbox_clear_rectangle_left.y = letterbox_mid_top; + letterbox_clear_rectangle_left.width = uint32_t(flow.output_x); + letterbox_clear_rectangle_left.height = letterbox_mid_height; + } + // Middle-right. + if (uint32_t(output_right) < host_rt_width) { + assert_true(flow.letterbox_clear_rectangle_count < + flow.letterbox_clear_rectangles.size()); + GuestOutputPaintFlow::ClearRectangle& letterbox_clear_rectangle_right = + flow.letterbox_clear_rectangles + [flow.letterbox_clear_rectangle_count++]; + letterbox_clear_rectangle_right.x = uint32_t(output_right); + letterbox_clear_rectangle_right.y = letterbox_mid_top; + letterbox_clear_rectangle_right.width = + host_rt_width - uint32_t(output_right); + letterbox_clear_rectangle_right.height = letterbox_mid_height; + } + // Bottom. + if (letterbox_mid_bottom < host_rt_height) { + assert_true(flow.letterbox_clear_rectangle_count < + flow.letterbox_clear_rectangles.size()); + GuestOutputPaintFlow::ClearRectangle& letterbox_clear_rectangle_top = + flow.letterbox_clear_rectangles + [flow.letterbox_clear_rectangle_count++]; + letterbox_clear_rectangle_top.x = 0; + letterbox_clear_rectangle_top.y = letterbox_mid_bottom; + letterbox_clear_rectangle_top.width = host_rt_width; + letterbox_clear_rectangle_top.height = + host_rt_height - letterbox_mid_bottom; + } + } + + return flow; +} + +void Presenter::ExecuteUIDrawersFromUIThread(UIDrawContext& ui_draw_context) { + // May be called by the implementations only when requested. + assert_true(is_in_ui_thread_paint_); + // Drawers can add or remove drawers (including themselves), need to ensure + // iterator validity in this case. + assert_false(is_executing_ui_drawers_); + ui_draw_next_iterator_ = ui_drawers_.begin(); + is_executing_ui_drawers_ = true; + while (ui_draw_next_iterator_ != ui_drawers_.end()) { + // The current iterator may be invalidated, and ui_draw_next_iterator_ may + // be changed, during the execution of the drawer if the list of the drawers + // is modified by it - don't assume that after the call + // ui_draw_next_iterator_ will be the same as + // std::next(ui_draw_next_iterator_) before it. + auto it_current = ui_draw_next_iterator_++; + // Don't draw twice if already drawn in this frame (may happen if the Z + // order of a drawer was increased from below the current one to above it by + // one of the drawers). + if (it_current->second.last_draw != ui_draw_current_) { + ui_draw_current_z_order_ = it_current->first; + it_current->second.last_draw = ui_draw_current_; + it_current->second.drawer->Draw(ui_draw_context); + } + } + is_executing_ui_drawers_ = false; + ++ui_draw_current_; +} + +void Presenter::SetPaintModeFromUIThread(PaintMode new_mode) { + // Can be modified only from the UI thread, so can skip locking if it's the + // same. + if (paint_mode_ == new_mode) { + return; + } + { + std::lock_guard lock(paint_mode_mutex_); + paint_mode_ = new_mode; + } + UpdateUITicksNeededFromUIThread(); +} + +Presenter::PaintMode Presenter::GetDesiredPaintModeFromUIThread( + bool is_paintable) const { + if (!is_paintable) { + // The only case when kNone can be returned, for surface connection updates + // when it's known that the UI thread currently has access to the connection + // lifecycle. + return PaintMode::kNone; + } + if (!cvars::host_present_from_non_ui_thread) { + return PaintMode::kUIThreadOnRequest; + } + if (surface_paint_connection_has_implicit_vsync_) { + // Don't be causing host vertical sync CPU waits in the thread generating + // the guest output. + return PaintMode::kUIThreadOnRequest; + } + if (!ui_drawers_.empty()) { + // The UI can be drawn only by the UI thread, and it needs to be drawn - + // paint in the UI thread. + return PaintMode::kUIThreadOnRequest; + } + // Only the guest output needs to be drawn - let the guest output thread + // present immediately for a lower latency. + return PaintMode::kGuestOutputThreadImmediately; +} + +void Presenter::DisconnectPaintingFromSurfaceFromUIThread( + SurfacePaintConnectionState new_state) { + assert_false(IsConnectedSurfacePaintConnectionState(new_state)); + if (IsConnectedSurfacePaintConnectionState(surface_paint_connection_state_)) { + DisconnectPaintingFromSurfaceFromUIThreadImpl(); + } + surface_paint_connection_state_ = new_state; + surface_paint_connection_has_implicit_vsync_ = false; + surface_width_in_paint_connection_ = 0; + surface_height_in_paint_connection_ = 0; +} + +void Presenter::UpdateSurfacePaintConnectionFromUIThread( + bool* repaint_needed_out, bool update_paint_mode_to_desired) { + assert_not_null(surface_); + + // Validate that painting lifecycle is accessible by the UI thread currently, + // not given to the guest output thread. The mode can be modified only by + // the UI thread, so no need to lock the mutex. + assert_true(paint_mode_ != PaintMode::kGuestOutputThreadImmediately); + + // Initialize repaint_needed_out for failure cases. + if (repaint_needed_out) { + *repaint_needed_out = false; + } + + // If the connection state is kUnconnectedSurfaceReportedUnusable, the + // implementation has reported that the surface is not usable by the presenter + // at all, and it's pointless to retry connecting to it. + if (surface_paint_connection_state_ != + SurfacePaintConnectionState::kUnconnectedSurfaceReportedUnusable) { + uint32_t surface_width = 0, surface_height = 0; + bool surface_area_available = + surface_->GetSize(surface_width, surface_height); + if (!surface_area_available) { + // The surface is currently zero-area (or has become zero-area), try again + // when it's resized. + DisconnectPaintingFromSurfaceFromUIThread( + SurfacePaintConnectionState::kUnconnectedRetryAtStateChange); + } else { + bool is_reconnect = IsConnectedSurfacePaintConnectionState( + surface_paint_connection_state_); + bool is_vsync_implicit = false; + SurfacePaintConnectResult connect_result = + ConnectOrReconnectPaintingToSurfaceFromUIThread( + *surface_, surface_width, surface_height, + surface_paint_connection_state_ == + SurfacePaintConnectionState::kConnectedPaintable, + is_vsync_implicit); + switch (connect_result) { + case SurfacePaintConnectResult::kSuccess: + if (repaint_needed_out) { + *repaint_needed_out = true; + } + // Fallthrough to common success handling. + case SurfacePaintConnectResult::kSuccessUnchanged: + // Don't know yet what the first result was (success or suboptimal). + surface_paint_connection_was_optimal_at_successful_paint_ = false; + surface_paint_connection_state_ = + SurfacePaintConnectionState::kConnectedPaintable; + surface_paint_connection_has_implicit_vsync_ = is_vsync_implicit; + surface_width_in_paint_connection_ = surface_width; + surface_height_in_paint_connection_ = surface_height; + if (!is_reconnect) { + *repaint_needed_out = true; + } + break; + case SurfacePaintConnectResult::kFailure: + surface_paint_connection_state_ = + SurfacePaintConnectionState::kUnconnectedRetryAtStateChange; + break; + case SurfacePaintConnectResult::kFailureSurfaceUnusable: + surface_paint_connection_state_ = + SurfacePaintConnectionState::kUnconnectedSurfaceReportedUnusable; + break; + } + } + } + + if (update_paint_mode_to_desired) { + SetPaintModeFromUIThread(GetDesiredPaintModeFromUIThread( + surface_paint_connection_state_ == + SurfacePaintConnectionState::kConnectedPaintable)); + } +} + +bool Presenter::RequestPaintOrConnectionRecoveryViaWindow( + bool force_ui_thread_paint_tick) { + // Can be called from any thread if an existing window_ is available in it, + // and it's known to have a Surface that will be the same throughout this + // call - not doing any checks whether this request can be satisfied + // theoretically. For safety, check whether the window exists unconditionally. + assert_not_null(window_); + assert_not_null(surface_); + if (ui_thread_paint_requested_.exchange(true, std::memory_order_relaxed)) { + // Invalidation pending already, no need to do it twice. + return false; + } + if (force_ui_thread_paint_tick) { + ForceUIThreadPaintTick(); + } + window_->RequestPaint(); + return true; +} + +void Presenter::UpdateSurfaceMonitorFromUIThread( + bool old_monitor_potentially_disconnected) { + // For dropping the monitor when the window is closing and is losing its + // surface, the existence of `surface_` (which implies that `window_` exists + // too) must be the condition for a non-null monitor, not just the existence + // of `window_`. +#if XE_PLATFORM_WIN32 + HMONITOR surface_new_win32_monitor = nullptr; + if (surface_) { + HWND hwnd = static_cast(window_)->hwnd(); + // The HWND may be non-existent if the window has been closed and destroyed + // (the HWND, not the xe::ui::Window) already. + if (hwnd) { + surface_new_win32_monitor = + MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL); + } + } + if (old_monitor_potentially_disconnected || + surface_win32_monitor_ != surface_new_win32_monitor) { + surface_win32_monitor_ = surface_new_win32_monitor; + if (dxgi_ui_tick_factory_ && !dxgi_ui_tick_factory_->IsCurrent()) { + // If a monitor has been newly connected, it won't appear in the old + // factory, need to recreate it. + { + Microsoft::WRL::ComPtr old_factory_output_to_release; + { + std::scoped_lock dxgi_ui_tick_lock(dxgi_ui_tick_mutex_); + old_factory_output_to_release = std::move(dxgi_ui_tick_output_); + } + } + dxgi_ui_tick_factory_.Reset(); + } + if (!dxgi_ui_tick_factory_) { + if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_ui_tick_factory_)))) { + XELOGE("Presenter: Failed to create a DXGI factory"); + } + } + Microsoft::WRL::ComPtr new_dxgi_output; + if (dxgi_ui_tick_factory_ && surface_new_win32_monitor) { + new_dxgi_output = GetDXGIOutputForMonitor(dxgi_ui_tick_factory_.Get(), + surface_new_win32_monitor); + } + // If the adapter was recreated, and the old output was released before its + // destruction, notifying is still required - the vertical blank wait thread + // might have entered the condition variable wait already as the output was + // null. + bool signal_dxgi_ui_tick_control; + { + std::unique_lock dxgi_ui_tick_lock(dxgi_ui_tick_mutex_); + bool dxgi_output_was_null = (dxgi_ui_tick_output_ == nullptr); + dxgi_ui_tick_output_ = new_dxgi_output; + signal_dxgi_ui_tick_control = + dxgi_output_was_null && AreDXGIUITicksWaitable(dxgi_ui_tick_lock); + } + if (signal_dxgi_ui_tick_control) { + dxgi_ui_tick_control_condition_.notify_all(); + } + } +#endif // XE_PLATFORM +} + +bool Presenter::InSurfaceOnMonitorFromUIThread() const { + if (!surface_) { + return false; + } +#if XE_PLATFORM_WIN32 + return surface_win32_monitor_ != nullptr; +#else + return true; +#endif // XE_PLATFORM +} + +Presenter::PaintResult Presenter::PaintAndPresent(bool execute_ui_drawers) { + assert_false(execute_ui_drawers && !is_in_ui_thread_paint_); + assert_true(surface_paint_connection_state_ == + SurfacePaintConnectionState::kConnectedPaintable); + PaintResult result = PaintAndPresentImpl(execute_ui_drawers); + switch (result) { + case PaintResult::kPresented: + surface_paint_connection_was_optimal_at_successful_paint_ = true; + break; + case PaintResult::kPresentedSuboptimal: + // Make outdated if previously optimal, now suboptimal, but don't cause + // the connection to become outdated if it has been suboptimal from the + // very beginning. + if (surface_paint_connection_was_optimal_at_successful_paint_) { + surface_paint_connection_state_ = + SurfacePaintConnectionState::kConnectedOutdated; + } + break; + case PaintResult::kNotPresentedConnectionOutdated: + surface_paint_connection_state_ = + SurfacePaintConnectionState::kConnectedOutdated; + break; + default: + // Another issue not directly related to the surface connection. + break; + } + return result; +} + +void Presenter::HandleUIDrawersChangeFromUIThread(bool drawers_were_empty) { + if (is_in_ui_thread_paint_) { + // Defer the refresh so no dangerous lifecycle-related changes happen during + // drawing. + if (!ui_drawers_.empty()) { + request_ui_paint_after_current_ui_thread_paint_ = true; + } + return; + } + + if (paint_mode_ == PaintMode::kNone) { + // Not connected, no point in refreshing (checking a more conservative + // paint_mode_ because the actual connection state may currently be owned by + // the guest output thread instead) or in toggling the ownership (the rest + // of the function can assume it's not kNone). + return; + } + + if (ui_drawers_.empty() != drawers_were_empty) { + // Require the UI thread to paint if it needs the UI, or let the guest + // output thread paint immediately if not. + SetPaintModeFromUIThread(GetDesiredPaintModeFromUIThread(true)); + // Make sure the ticks for limiting the UI frame rate are sent. + UpdateUITicksNeededFromUIThread(); + } + + // Request painting so the changes to the UI drawer list are reflected as + // quickly as possible. + // RequestUIPaintFromUIThread is not enough, because a paint request is also + // needed if disabling the UI, to force paint a frame without the UI as soon + // as possible - it can't be dropped if ui_drawers_ is empty. + // The coarse painting availability (and thus the availability of `window_`, + // which is required for the paint mode to be anything else than kNone) has + // already been checked above. + ForceUIThreadPaintTick(); + window_->RequestPaint(); +} + +void Presenter::UpdateUITicksNeededFromUIThread() { +#if XE_PLATFORM_WIN32 + bool new_needed = AreUITicksNeededFromUIThread(); + if (dxgi_ui_ticks_needed_ == new_needed) { + return; + } + bool signal_dxgi_ui_tick_control; + { + std::unique_lock dxgi_ui_tick_lock(dxgi_ui_tick_mutex_); + dxgi_ui_ticks_needed_ = new_needed; + signal_dxgi_ui_tick_control = AreDXGIUITicksWaitable(dxgi_ui_tick_lock); + } + if (signal_dxgi_ui_tick_control) { + dxgi_ui_tick_control_condition_.notify_all(); + } +#endif +} + +void Presenter::WaitForUITickFromUIThread() { +#if XE_PLATFORM_WIN32 + if (!AreUITicksNeededFromUIThread()) { + return; + } + std::unique_lock dxgi_ui_tick_lock(dxgi_ui_tick_mutex_); + uint64_t last_vblank_before_wait = dxgi_ui_tick_last_vblank_; + while (true) { + // Guest output present requests should interrupt the wait as quickly as + // possible as they should be fulfilled as early as possible. + if (dxgi_ui_tick_force_requested_) { + dxgi_ui_tick_force_requested_ = false; + return; + } + if (!AreDXGIUITicksWaitable(dxgi_ui_tick_lock)) { + return; + } + if (dxgi_ui_tick_last_vblank_ > dxgi_ui_tick_last_draw_) { + // If there have been multiple vblanks during the wait for some reason, + // next time draw the UI immediately. + dxgi_ui_tick_last_draw_ = std::min(last_vblank_before_wait + uint64_t(1), + dxgi_ui_tick_last_vblank_); + return; + } + dxgi_ui_tick_signal_condition_.wait(dxgi_ui_tick_lock); + } +#endif // XE_PLATFORM +} + +void Presenter::ForceUIThreadPaintTick() { +#if XE_PLATFORM_WIN32 + std::scoped_lock dxgi_ui_tick_lock(dxgi_ui_tick_mutex_); + dxgi_ui_tick_force_requested_ = true; +#endif // XE_PLATFORM +} + +#if XE_PLATFORM_WIN32 +Microsoft::WRL::ComPtr Presenter::GetDXGIOutputForMonitor( + IDXGIFactory1* factory, HMONITOR monitor) { + Microsoft::WRL::ComPtr adapter; + for (UINT adapter_index = 0; SUCCEEDED(factory->EnumAdapters( + adapter_index, adapter.ReleaseAndGetAddressOf())); + ++adapter_index) { + Microsoft::WRL::ComPtr output; + for (UINT output_index = 0; + SUCCEEDED(adapter->EnumOutputs(output_index, &output)); + ++output_index) { + DXGI_OUTPUT_DESC output_desc; + if (SUCCEEDED(output->GetDesc(&output_desc)) && + output_desc.Monitor == monitor) { + return std::move(output); + } + } + } + return nullptr; +} + +void Presenter::DXGIUITickThread() { + std::unique_lock dxgi_ui_tick_lock(dxgi_ui_tick_mutex_); + while (true) { + if (dxgi_ui_tick_thread_shutdown_) { + return; + } + if (!AreDXGIUITicksWaitable(dxgi_ui_tick_lock)) { + dxgi_ui_tick_control_condition_.wait(dxgi_ui_tick_lock); + continue; + } + // Wait for vertical blank, with the mutex unlocked (holding a new reference + // to the current output while it's happening) so subscribers can still do + // early-out checks. + bool wait_succeeded; + { + Microsoft::WRL::ComPtr dxgi_output = dxgi_ui_tick_output_; + dxgi_ui_tick_lock.unlock(); + wait_succeeded = SUCCEEDED(dxgi_ui_tick_output_->WaitForVBlank()); + } + dxgi_ui_tick_lock.lock(); + if (wait_succeeded) { + ++dxgi_ui_tick_last_vblank_; + } else { + // Lost the ability to wait for a vertical blank on this output, notify + // the waiting threads, and wait for a new one. + dxgi_ui_tick_output_.Reset(); + } + dxgi_ui_tick_signal_condition_.notify_all(); + } +} +#endif // XE_PLATFORM + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/presenter.h b/src/xenia/ui/presenter.h new file mode 100644 index 000000000..9e89c8573 --- /dev/null +++ b/src/xenia/ui/presenter.h @@ -0,0 +1,1034 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_PRESENTER_H_ +#define XENIA_UI_PRESENTER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenia/base/assert.h" +#include "xenia/base/byte_order.h" +#include "xenia/base/cvar.h" +#include "xenia/base/math.h" +#include "xenia/base/platform.h" +#include "xenia/ui/surface.h" +#include "xenia/ui/ui_drawer.h" + +#if XE_PLATFORM_WIN32 +// Must be included before DXGI for things like NOMINMAX, and also needed for +// Windows handle types. +#include "xenia/base/platform_win.h" + +#include +#include +#endif // XE_PLATFORM + +// For implementation use. +DECLARE_bool(present_render_pass_clear); + +namespace xe { +namespace ui { + +class Presenter; +class Window; +class Win32Window; + +class UIDrawContext { + public: + UIDrawContext(const UIDrawContext& context) = delete; + UIDrawContext& operator=(const UIDrawContext& context) = delete; + virtual ~UIDrawContext() = default; + + Presenter& presenter() const { return presenter_; } + + // It's assumed that the render target size will be either equal to the size + // of the surface, or the render target will be stretched to cover the entire + // surface (not in the corner of the surface). + uint32_t render_target_width() const { return render_target_width_; } + uint32_t render_target_height() const { return render_target_height_; } + + protected: + explicit UIDrawContext(Presenter& presenter, uint32_t render_target_width, + uint32_t render_target_height) + : presenter_(presenter), + render_target_width_(render_target_width), + render_target_height_(render_target_height) {} + + private: + Presenter& presenter_; + uint32_t render_target_width_; + uint32_t render_target_height_; +}; + +struct RawImage { + uint32_t width = 0; + uint32_t height = 0; + size_t stride = 0; + // R8 G8 B8 X8. The last row is not required to be padded to the stride. + std::vector data; +}; + +// The presenter displays up to two layers of content on a host surface: +// - Guest output image, focusing on lowering latency and maintaining stable +// frame pacing, with various scaling and sharpening methods and letterboxing; +// - Xenia's internal UI (such as the profiler and Dear ImGui). +// +// The guest output image may be refreshed from any thread generating it +// (usually the GPU emulation thread), as long as there are no multiple threads +// doing that simultaneously (since that would functionally be a race condition +// even if refreshing is performed in a critical section). +// +// The UI overlays are managed entirely by the UI thread. +// +// Painting on the host surface may occur in two places: +// - If there are no UI overlays, painting of the guest output may be performed +// immediately from the thread refreshing it, to bypass the OS scheduling and +// event handling. This is especially important on platforms where the native +// surface paint event has a frame rate limit (such as the display refresh +// rate), and the limit may differ greatly from the guest frame rate (such as +// presenting a 30 or 60 FPS guest to a 144 Hz host surface). +// - If the UI overlays (owned by the UI thread) are present, painting of both +// the guest output (is available) and the UI is done exclusively from the +// platform paint event handler. The guest output without UI overlays may also +// be painted from the platform paint callback in certain cases, such as when +// an additional paint beyond the guest's frame rate may be needed (like when +// resizing the window), or when painting from the thread refreshing the guest +// output is undesirable (for instance, if it will result in waiting for host +// vertical sync in that thread too early if host vertical sync can't be +// disabled on the platform, blocking the next frame of GPU emulation). +// +// The composition of the guest and the UI is done by Xenia manually, as opposed +// to using platform functionality such as DirectComposition, in order to have +// more predictability of GPU queue scheduling, but primarily to be able to take +// advantage of independent host presentation where it's available, so variable +// refresh rate may be used where possible, and latency may be significantly +// reduced. Also, at least on some configurations (checked on Windows 11 21H2 on +// Nvidia GeForce GTX 1070 with driver version 472.12), when in borderless +// fullscreen, any composition causes the DXGI Present to wait for vertical sync +// on the GPU even if the sync interval 0 is specified. +// +// An intermediate image with the size requested by the guest is used for guest +// output in all cases. Even though it adds some GPU overhead, especially in the +// 1:1 size case, using it solves multiple issues: +// - Presentation may be done more often than by the guest. +// - There is clear separation between pre-scaling and mid- / post-scaling +// operations. The gamma ramp, for instance, may be applied before scaling, +// with one lookup per pixel rather than four with fetch4. +// - A simpler compute shader may be used instead of setting up the whole +// graphics pipeline for copying in the GPU command processor in all cases, +// while Direct3D 12 does not allow UAVs for swap chain buffers. +// +// The presenter limits the frame rate of the UI overlay (when possible) to a +// value that's ideally the refresh rate of the monitor containing the window if +// the platform's paint event doesn't have an internal limiter. However, where +// possible, the arrival of a new guest output image will interrupt the UI tick +// wait. +// +// Because the UI overlays preclude the possibility of presenting directly from +// the thread refreshing the guest output, and on some platforms, result in the +// frame rate limiting of paint events manifesting itself, there must be no +// persistent UI overlays that haven't been explicitly requested by the user. +// However, for temporary (primarily non-modal) UI elements such as various +// timed notifications, using the Presenter should be preferred to implementing +// them via overlaying native windows on top of the presentation surface on +// platforms where the concept of independent presentation exists, as multiple +// windows will result in native composition disabling it. +// +// The painting connection between the Presenter and the Surface can be managed +// only by the UI thread. However, the thread refreshing the guest output may +// still mark the current connection as outdated and ask the UI thread (by +// requesting painting) to try to recover - but the guest output refresh thread +// must not try to reconnect by itself, as methods of the Surface are available +// only to the UI thread. +class Presenter { + public: + // May be actually called on the UI thread even if statically_from_ui_thread + // is false, such as when the guest output is refreshed by the UI thread. + using HostGpuLossCallback = + std::function; + static void FatalErrorHostGpuLossCallback(bool is_responsible, + bool statically_from_ui_thread); + + class GuestOutputRefreshContext { + public: + GuestOutputRefreshContext(const GuestOutputRefreshContext& context) = + delete; + GuestOutputRefreshContext& operator=( + const GuestOutputRefreshContext& context) = delete; + virtual ~GuestOutputRefreshContext() = default; + + // Sets whether the source actually has no more than 8 bits of precision + // (though the image provided by the refresher may still have a higher + // storage precision). If never called, assuming it's false. + void SetIs8bpc(bool is_8bpc) { is_8bpc_out_ref_ = is_8bpc; } + + protected: + GuestOutputRefreshContext(bool& is_8bpc_out_ref) + : is_8bpc_out_ref_(is_8bpc_out_ref) { + is_8bpc_out_ref = false; + } + + private: + bool& is_8bpc_out_ref_; + }; + + class GuestOutputPaintConfig { + public: + enum class Effect { + kBilinear, + kCas, + // AMD FidelityFX Super Resolution upsampling, Contrast Adaptive + // Sharpening otherwise. + kFsr, + }; + + // This value is used as a lerp factor. + static constexpr float kCasAdditionalSharpnessMin = 0.0f; + static constexpr float kCasAdditionalSharpnessMax = 1.0f; + static constexpr float kCasAdditionalSharpnessDefault = 0.0f; + static_assert(kCasAdditionalSharpnessDefault >= + kCasAdditionalSharpnessMin && + kCasAdditionalSharpnessDefault <= kCasAdditionalSharpnessMax); + + // EASU (as well as CAS) is designed for scaling by factors of up to 2x2. + // Some sensible limit for unusual cases, when the game for some reason + // presents a very small back buffer. + // This is enough for 480p > 960p > 1920p > 3840p > 7680p (bigger than 8K, + // or 4320p). + static constexpr uint32_t kFsrMaxUpscalingPassesMax = 4; + + static constexpr float kFsrSharpnessReductionMin = 0.0f; + // "Values above 2.0 won't make a visible difference." + // https://raw.githubusercontent.com/GPUOpen-Effects/FidelityFX-FSR/master/docs/FidelityFX-FSR-Overview-Integration.pdf + static constexpr float kFsrSharpnessReductionMax = 2.0f; + static constexpr float kFsrSharpnessReductionDefault = 0.2f; + static_assert(kFsrSharpnessReductionDefault >= kFsrSharpnessReductionMin && + kFsrSharpnessReductionDefault <= kFsrSharpnessReductionMax); + + // In the sharpness setters, min / max with a constant as the first argument + // also drops NaNs. + + Effect GetEffect() const { return effect_; } + void SetEffect(Effect new_effect) { effect_ = new_effect; } + + float GetCasAdditionalSharpness() const { + return cas_additional_sharpness_; + } + void SetCasAdditionalSharpness(float new_cas_additional_sharpness) { + cas_additional_sharpness_ = std::min( + kCasAdditionalSharpnessMax, + std::max(kCasAdditionalSharpnessMin, new_cas_additional_sharpness)); + } + + uint32_t GetFsrMaxUpsamplingPasses() const { + return fsr_max_upsampling_passes_; + } + void SetFsrMaxUpsamplingPasses(uint32_t new_fsr_max_upsampling_passes) { + fsr_max_upsampling_passes_ = + std::min(kFsrMaxUpscalingPassesMax, + std::max(uint32_t(1), new_fsr_max_upsampling_passes)); + } + + // In stops. + float GetFsrSharpnessReduction() const { return fsr_sharpness_reduction_; } + void SetFsrSharpnessReduction(float new_fsr_sharpness_reduction) { + fsr_sharpness_reduction_ = std::min( + kFsrSharpnessReductionMax, + std::max(kFsrSharpnessReductionMin, new_fsr_sharpness_reduction)); + } + + // Very tiny effect, but highly noticeable, for instance, on the sky in the + // 4D5307E6 main menu (prominently in Custom Games, especially with FSR - + // banding around the clouds can be clearly seen without dithering with 8bpc + // final host output). + bool GetDither() const { return dither_; } + void SetDither(bool new_dither) { dither_ = new_dither; } + + private: + // Tools, rather than the emulator itself, must use kBilinear as the image + // must be as close to the original front buffer as possible. + Effect effect_ = Effect::kBilinear; + float cas_additional_sharpness_ = kCasAdditionalSharpnessDefault; + uint32_t fsr_max_upsampling_passes_ = kFsrMaxUpscalingPassesMax; + float fsr_sharpness_reduction_ = kFsrSharpnessReductionDefault; + bool dither_ = false; + }; + + Presenter(const Presenter& presenter) = delete; + Presenter& operator=(const Presenter& presenter) = delete; + virtual ~Presenter(); + + virtual Surface::TypeFlags GetSupportedSurfaceTypes() const = 0; + + // For calling from the Window for the Presenter attached to it. + // May be called from the destructor of the presenter through the window. + void SetWindowSurfaceFromUIThread(Window* new_window, Surface* new_surface); + void OnSurfaceMonitorUpdateFromUIThread( + bool old_monitor_potentially_disconnected); + void OnSurfaceResizeFromUIThread(); + + // For calling from the platform paint event handler. Refreshes the surface + // connection if needed, and also paints if possible and if needed (if there + // are no UI overlays, and the guest output is presented directly from the + // thread refreshing it, the paint may be skipped unless there has been an + // explicit request previously or force_paint is true). If painting happens, + // both the guest output and the UI overlays (if any are active) are drawn. + // The background / letterbox of the painted context will be black - windows + // should preferably have a black background before a Presenter is attached to + // them too. + void PaintFromUIThread(bool force_paint = false); + + // Pass 0 as width or height to disable guest output until the next refresh + // with an actual size. The callback will receive a backend-specific context, + // and will not be called in case of an error such as the wrong size, or if + // guest output is disabled. Returns whether the callback was called and it + // returned true. The callback must submit all updating work to the host GPU + // before successfully returning, and also signal all the GPU synchronization + // primitives required by the GuestOutputRefreshContext implementation. + bool RefreshGuestOutput( + uint32_t frontbuffer_width, uint32_t frontbuffer_height, + uint32_t screen_width, uint32_t screen_height, + std::function refresher); + // The implementation must be callable from any thread, including from + // multiple at the same time, and it should acquire the latest guest output + // image via ConsumeGuestOutput. + virtual bool CaptureGuestOutput(RawImage& image_out) = 0; + const GuestOutputPaintConfig& GetGuestOutputPaintConfigFromUIThread() const { + return guest_output_paint_config_; + } + // For simplicity, may be called repeatedly even if no changes have been made. + void SetGuestOutputPaintConfigFromUIThread( + const GuestOutputPaintConfig& new_config); + + void AddUIDrawerFromUIThread(UIDrawer* drawer, size_t z_order); + void RemoveUIDrawerFromUIThread(UIDrawer* drawer); + + // Requests (re)painting with the UI if there's UI to draw. + void RequestUIPaintFromUIThread(); + + protected: + enum class PaintResult { + kPresented, + kPresentedSuboptimal, + // Refused for internal reasons or a host API side failure, but still may + // try to present without resetting the graphics provider in the future. + kNotPresented, + kNotPresentedConnectionOutdated, + kGpuLostExternally, + kGpuLostResponsible, + }; + + enum class SurfacePaintConnectResult { + // Redrawing not necessary, nothing changed. Must not be returned for a new + // connection (when was previously disconnected from the surface). + kSuccessUnchanged, + kSuccess, + kFailure, + kFailureSurfaceUnusable, + }; + + static constexpr uint32_t kGuestOutputMailboxSize = 3; + + struct GuestOutputProperties { + // At least any value being 0 here means the guest output is disabled for + // this frame. + uint32_t frontbuffer_width; + uint32_t frontbuffer_height; + // Guest screen size (primarily for the target aspect ratio, which may be + // different than that of the frontbuffer). + uint32_t screen_width; + uint32_t screen_height; + bool is_8bpc; + + GuestOutputProperties() { SetToInactive(); } + + bool IsActive() const { + return frontbuffer_width && frontbuffer_height && screen_width && + screen_height; + } + + void SetToInactive() { + frontbuffer_width = 0; + frontbuffer_height = 0; + screen_width = 0; + screen_height = 0; + is_8bpc = false; + } + }; + + enum class GuestOutputPaintEffect { + kBilinear, + kBilinearDither, + kCasSharpen, + kCasSharpenDither, + kCasResample, + kCasResampleDither, + kFsrEasu, + kFsrRcas, + kFsrRcasDither, + + kCount, + }; + + static constexpr bool CanGuestOutputPaintEffectBeIntermediate( + GuestOutputPaintEffect effect) { + switch (effect) { + case GuestOutputPaintEffect::kBilinear: + // Dithering is never performed in intermediate passes because it may be + // interpreted as features by the subsequent passes. + case GuestOutputPaintEffect::kBilinearDither: + case GuestOutputPaintEffect::kCasSharpenDither: + case GuestOutputPaintEffect::kCasResampleDither: + case GuestOutputPaintEffect::kFsrRcasDither: + return false; + default: + // The result of any other effect can be stretched with bilinear + // filtering to the final resolution. + return true; + }; + } + + static constexpr bool CanGuestOutputPaintEffectBeFinal( + GuestOutputPaintEffect effect) { + switch (effect) { + case GuestOutputPaintEffect::kFsrEasu: + return false; + default: + return true; + }; + } + + // The longest path is kFsrMaxUpscalingPassesMax + optionally RCAS + + // optionally bilinear, when upscaling by more than + // 2^kFsrMaxUpscalingPassesMax along any direction. + // Non-FSR paths are either only bilinear, only CAS, or (when upscaling by + // more than 2 along any direction) CAS followed by bilinear. + static constexpr size_t kMaxGuestOutputPaintEffects = + GuestOutputPaintConfig::kFsrMaxUpscalingPassesMax + 2; + + struct GuestOutputPaintFlow { + // Letterbox on up to 4 sides. + static constexpr size_t kMaxClearRectangles = 4; + + struct ClearRectangle { + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + }; + + GuestOutputProperties properties; + + // If 0, don't display the guest output. + size_t effect_count; + std::array effects; + std::array, kMaxGuestOutputPaintEffects> + effect_output_sizes; + + // Offset of the rectangle for final drawing to the host window with + // letterboxing. + int32_t output_x; + int32_t output_y; + + // If there is guest output (effect_count is not 0), contains the letterbox + // rectangles around the guest output. + size_t letterbox_clear_rectangle_count; + std::array letterbox_clear_rectangles; + + void GetEffectInputSize(size_t effect_index, uint32_t& width_out, + uint32_t& height_out) const { + assert_true(effect_index < effect_count); + if (!effect_index) { + width_out = properties.frontbuffer_width; + height_out = properties.frontbuffer_height; + return; + } + const std::pair& intermediate_size = + effect_output_sizes[effect_index - 1]; + width_out = intermediate_size.first; + height_out = intermediate_size.second; + } + + void GetEffectOutputOffset(size_t effect_index, int32_t& x_out, + int32_t& y_out) const { + assert_true(effect_index < effect_count); + if (effect_index + 1 < effect_count) { + x_out = 0; + y_out = 0; + return; + } + x_out = output_x; + y_out = output_y; + } + }; + + struct BilinearConstants { + int32_t output_offset[2]; + float output_size_inv[2]; + + void Initialize(const GuestOutputPaintFlow& flow, size_t effect_index) { + flow.GetEffectOutputOffset(effect_index, output_offset[0], + output_offset[1]); + const std::pair& output_size = + flow.effect_output_sizes[effect_index]; + output_size_inv[0] = 1.0f / float(output_size.first); + output_size_inv[1] = 1.0f / float(output_size.second); + } + }; + + static constexpr float CalculateCasPostSetupSharpness(float sharpness) { + // CasSetup const1.x. + return -1.0f / (8.0f - 3.0f * sharpness); + } + + struct CasSharpenConstants { + int32_t output_offset[2]; + float sharpness_post_setup; + + void Initialize(const GuestOutputPaintFlow& flow, size_t effect_index, + const GuestOutputPaintConfig& config) { + flow.GetEffectOutputOffset(effect_index, output_offset[0], + output_offset[1]); + sharpness_post_setup = + CalculateCasPostSetupSharpness(config.GetCasAdditionalSharpness()); + } + }; + + struct CasResampleConstants { + int32_t output_offset[2]; + // Input size / output size. + float input_output_size_ratio[2]; + float sharpness_post_setup; + + void Initialize(const GuestOutputPaintFlow& flow, size_t effect_index, + const GuestOutputPaintConfig& config) { + flow.GetEffectOutputOffset(effect_index, output_offset[0], + output_offset[1]); + uint32_t input_width, input_height; + flow.GetEffectInputSize(effect_index, input_width, input_height); + const std::pair& output_size = + flow.effect_output_sizes[effect_index]; + input_output_size_ratio[0] = + float(input_width) / float(output_size.first); + input_output_size_ratio[1] = + float(input_height) / float(output_size.second); + sharpness_post_setup = + CalculateCasPostSetupSharpness(config.GetCasAdditionalSharpness()); + } + }; + + struct FsrEasuConstants { + // No output offset because the EASU pass is always done to an intermediate + // framebuffer. + float input_output_size_ratio[2]; + float input_size_inv[2]; + + void Initialize(const GuestOutputPaintFlow& flow, size_t effect_index) { + uint32_t input_width, input_height; + flow.GetEffectInputSize(effect_index, input_width, input_height); + const std::pair& output_size = + flow.effect_output_sizes[effect_index]; + input_output_size_ratio[0] = + float(input_width) / float(output_size.first); + input_output_size_ratio[1] = + float(input_height) / float(output_size.second); + input_size_inv[0] = 1.0f / float(input_width); + input_size_inv[1] = 1.0f / float(input_height); + } + }; + + struct FsrRcasConstants { + int32_t output_offset[2]; + float sharpness_post_setup; + + static float CalculatePostSetupSharpness(float sharpness_reduction_stops) { + // FsrRcasCon const0.x. + return std::exp2f(-sharpness_reduction_stops); + } + + void Initialize(const GuestOutputPaintFlow& flow, size_t effect_index, + const GuestOutputPaintConfig& config) { + flow.GetEffectOutputOffset(effect_index, output_offset[0], + output_offset[1]); + sharpness_post_setup = + CalculatePostSetupSharpness(config.GetFsrSharpnessReduction()); + } + }; + + explicit Presenter(HostGpuLossCallback host_gpu_loss_callback) + : host_gpu_loss_callback_(host_gpu_loss_callback) {} + + // Must be called by the implementation's initialization, before the presenter + // is used for anything. + bool InitializeCommonSurfaceIndependent(); + + // ConnectOrReconnect and Disconnect are callable only by the UI thread and + // only when it has access to painting (PaintMode is not + // kGuestOutputThreadImmediately). + // Called only for a non-zero-area surface potentially supporting painting via + // the presenter. In case of a failure, internally no resources referencing + // the surface must be held by the implementation anymore - the implementation + // must be left in the same state as after + // DisconnectPaintingFromSurfaceFromUIThreadImpl. If the call is successful, + // the implementation must write to is_vsync_implicit_out whether the + // connection will now have vertical sync forced by the host window system, + // which may cause undesirable waits on the CPU when beginning or ending + // frames. + virtual SurfacePaintConnectResult + ConnectOrReconnectPaintingToSurfaceFromUIThread( + Surface& new_surface, uint32_t new_surface_width, + uint32_t new_surface_height, bool was_paintable, + bool& is_vsync_implicit_out) = 0; + // Releases resources referencing the surface in the implementation if they + // are held by it. Call through DisconnectPaintingFromSurfaceFromUIThread to + // ensure the implementation is only called while the connection is active. + virtual void DisconnectPaintingFromSurfaceFromUIThreadImpl() = 0; + + // The returned lock interlocks multiple consumers (but not the producer and + // the consumer) and must be held while accessing implementation-specific + // objects that depend on the image or its index in the mailbox (unless there + // are other locking mechanisms involved for the resources, such as reference + // counting for the guest output images, which doesn't have to be atomic + // though for the reason described later in this paragraph, or assumptions + // like of main target painting being possible only in at most one thread at + // once). While this lock is held, the currently acquired image index can't be + // changed (by other consumers advancing the acquired image index to the new + // ready image index), so the image with the index given by this function + // can't be released and be made writable or given to a different consumer + // (thus it's owned exclusively by the consumer who has called this function). + // The properties are returned by copy rather than returning a pointer to them + // or asking the consumer to pull them for the current mailbox index, so there + // are less things to take into consideration while leaving the guest output + // consumer critical section earlier (as if a pointer was returned, the data + // behind it could be overwritten at any time after leaving the consumer + // critical section) if the implementation has its own synchronization + // mechanisms that allow for doing so as described earlier. Returns UINT32_MAX + // as the mailbox index if the image is inactive (if it's active, it has + // proper properties though). + [[nodiscard]] std::unique_lock ConsumeGuestOutput( + uint32_t& mailbox_index_or_max_if_inactive_out, + GuestOutputProperties* properties_out, + GuestOutputPaintConfig* paint_config_out); + // The properties are passed explicitly, not taken from the current acquired + // image, so it can be called for a copy of the acquired image's properties + // outside the consumer lock if the implementation has its own synchronization + // (like reference counting for the guest output images) that makes it + // possible to leave the consumer critical section earlier. Also, the guest + // output paint configuration is passed explicitly too so calling this + // function multiple times is safer. + GuestOutputPaintFlow GetGuestOutputPaintFlow( + const GuestOutputProperties& properties, uint32_t host_rt_width, + uint32_t host_rt_height, uint32_t max_rt_width, uint32_t max_rt_height, + const GuestOutputPaintConfig& config) const; + // is_8bpc_out_ref is where to write whether the source actually has no more + // than 8 bits of precision per channel (though the image provided by the + // refresher may still have a higher storage precision) - if not written, it + // will be assumed to be false. + virtual bool RefreshGuestOutputImpl( + uint32_t mailbox_index, uint32_t frontbuffer_width, + uint32_t frontbuffer_height, + std::function refresher, + bool& is_8bpc_out_ref) = 0; + + // For guest output capturing (for debugging use thus - shouldn't be adding + // any noise like dithering that's not present in the original image), + // converting a 10bpc RGB pixel to 8bpc that can be stored in common image + // formats. + static uint32_t Packed10bpcRGBTo8bpcBytes(uint32_t rgb10) { + // Conversion almost according to the Direct3D 10+ rules (unorm > float > + // unorm), but with one multiplication rather than separate division and + // multiplication - the results are the same for unorm10 to unorm8. + if constexpr (std::endian::native == std::endian::big) { + return (uint32_t(float(rgb10 & 0x3FF) * (255.0f / 1023.0f) + 0.5f) + << 24) | + (uint32_t(float((rgb10 >> 10) & 0x3FF) * (255.0f / 1023.0f) + 0.5f) + << 16) | + (uint32_t(float((rgb10 >> 20) & 0x3FF) * (255.0f / 1023.0f) + 0.5f) + << 8) | + uint32_t(0xFF); + } + return uint32_t(float(rgb10 & 0x3FF) * (255.0f / 1023.0f) + 0.5f) | + (uint32_t(float((rgb10 >> 10) & 0x3FF) * (255.0f / 1023.0f) + 0.5f) + << 8) | + (uint32_t(float((rgb10 >> 20) & 0x3FF) * (255.0f / 1023.0f) + 0.5f) + << 16) | + (uint32_t(0xFF) << 24); + } + + // Paints and presents the guest output if available (or just solid black + // color), and if requested, the UI on top of it. + // + // May be called from the non-UI thread, but only to paint the guest output + // (no UI drawing, with execute_ui_drawers disabled). + // + // Call via PaintAndPresent. + virtual PaintResult PaintAndPresentImpl(bool execute_ui_drawers) = 0; + + // For calling from the painting implementations if requested. + void ExecuteUIDrawersFromUIThread(UIDrawContext& ui_draw_context); + + private: + enum class PaintMode { + // Don't paint at all. + // Painting lifecycle is accessible only by the UI thread. + // window_->RequestPaint() must not be called in this mode at all regardless + // of whether the Window object exists because the Window object in this + // case may correspond to a window without a paintable Surface (in a closed + // state, or in the middle of a surface change), and non-UI threads (such as + // the guest output thread) may result in a race condition internally inside + // Window::RequestPaint during the access to the Window's state, such as the + // availability of the Surface that can handle the paint (therefore, if + // there's no Surface, this is the only valid mode). + kNone, + // Guest output refreshing notifies the `window_`, which must be valid and + // safe to call RequestPaint for, that painting should be done in the UI + // thread (including the UI if needed). Painting is possible, and painting + // lifecycle is accessible, only by the UI thread. + kUIThreadOnRequest, + // Paint immediately in the guest output thread for lower latency. The + // `window_`, however, may be notified that the surface painting connection + // has become outdated (via RequestPaint, as in this case the UI thread will + // need to repaint as sooner as possible after reconnecting anyway), and + // change the surface connection state accordingly (only to + // kConnectedOutdated). + // Painting is possible only by the guest output thread, lifecycle + // management cannot be done from the UI thread until it takes over. + kGuestOutputThreadImmediately, + }; + + enum class SurfacePaintConnectionState { + // No surface at all, or couldn't connect with the current state of the + // surface (such as because the surface was zero-sized because the window + // was minimized, for example). Or, the connection has become outdated, and + // the attempt to reconnect at kRetryConnectingSoon has failed. Try to + // reconnect if anything changes in the state of the surface, such as its + // size. + kUnconnectedRetryAtStateChange, + // Can't connect to the current existing surface (the surface has been lost + // or it's completely incompatible). No point in retrying connecting until + // the surface is replaced. + kUnconnectedSurfaceReportedUnusable, + // Everything is fine, can paint. The connection might have become + // suboptimal though, and haven't tried refreshing yet, but still usable for + // painting nonetheless. + kConnectedPaintable, + // The implementation still holds resources associated with the connection, + // but presentation has reported that it has become outdated, try + // reconnecting as soon as possible (at the next paint attempt, requesting + // it if needed). This is the only state that the guest output thread may + // transition the connection to (from kConnectedPaintable only) if it has + // access to painting (the paint mode is kGuestOutputThreadImmediately). + kConnectedOutdated, + }; + + static constexpr bool IsConnectedSurfacePaintConnectionState( + SurfacePaintConnectionState connection_state) { + return connection_state == + SurfacePaintConnectionState::kConnectedPaintable || + connection_state == SurfacePaintConnectionState::kConnectedOutdated; + } + + struct UIDrawerReference { + UIDrawer* drawer; + uint64_t last_draw; + + explicit UIDrawerReference(UIDrawer* drawer, + uint64_t last_draw = UINT64_MAX) + : drawer(drawer), last_draw(last_draw) {} + }; + + void SetPaintModeFromUIThread(PaintMode new_mode); + // Based on conditions like whether UI needs to be drawn and whether vertical + // sync is implicit - see the implementation for the requirements. + // is_paintable is an explicit parameter because this function may be called + // in two scenarios: + // - After connection updates - painting connection is owned by the UI thread, + // so the actual state can be obtained and passed here so kNone can be + // returned. + // - When merely toggling something local to the UI thread - only to toggle + // between the two threads, but not to switch from or to kNone (make sure + // it's not kNone before calling), pass `true` in this case. + PaintMode GetDesiredPaintModeFromUIThread(bool is_paintable) const; + + // Callable only by the UI thread and only when it has access to painting + // (PaintMode is not kGuestOutputThreadImmediately). + // This can be called to a surface after having not been connected to any (in + // this case, surface_paint_connection_state_ must be + // kUnconnectedRetryAtStateChange, not kUnconnectedNoUsableSurface, otherwise + // the call will be dropped), or to handle surface state changes such as + // resizing. However, this must not be called to change directly from one + // surface to another - need to disconnect prior to that, because the + // implementation may assume that the surface is still the same, and may try + // to, for instance, resize the buffers for the existing surface. + void UpdateSurfacePaintConnectionFromUIThread( + bool* repaint_needed_out, bool update_paint_mode_to_desired); + // Callable only by the UI thread and only when it has access to painting + // (PaintMode is not kGuestOutputThreadImmediately). + // See DisconnectPaintingFromSurfaceFromUIThreadImpl for more information. + void DisconnectPaintingFromSurfaceFromUIThread( + SurfacePaintConnectionState new_state); + + // Can be called from any thread if an existing window_ safe to RequestPaint + // (not closed) is available in it, so doesn't check the surface painting + // connection state. Returns whether the window_->RequestPaint() call has been + // made. + bool RequestPaintOrConnectionRecoveryViaWindow( + bool force_ui_thread_paint_tick); + + // Platform-specific function refreshing the monitor the current window + // surface is on, through the Surface or its Window. A reference to the + // monitor is held only when a Surface is available, so it's automatically + // dropped when the Window loses its Surface when it's being closed (but the + // Window object keeps being attached to the Presenter), for instance. + void UpdateSurfaceMonitorFromUIThread( + bool old_monitor_potentially_disconnected); + // Platform-specific function returning whether the surface the presenter is + // currently attached it is actually visible on any monitor. UI thread + // painting may be dropped if this returns false - need to request painting if + // the surface appears on a monitor again. May be using the state cached at + // window / surface state changes, not the actual state from the platform. + bool InSurfaceOnMonitorFromUIThread() const; + + // Calls PaintAndPresentImpl and does post-paint checks that are safe to do on + // both the UI thread and the guest output thread. See the information about + // PaintAndPresentImpl for details. + // A kPresentedSuboptimal result is returned as is, but the connection may or + // may not be made outdated if that happens - though if it's + // kPresentedSuboptimal rather than kNotPresentedConnectionOutdated, the image + // has been successfully sent to the OS presentation at least. + PaintResult PaintAndPresent(bool execute_ui_drawers); + + void HandleUIDrawersChangeFromUIThread(bool drawers_were_empty); + + bool AreUITicksNeededFromUIThread() const { + // UI drawing should be done, and painting needs to be possible (coarsely + // checking because the actual connection state, including outdated, may be + // currently unavailable from the UI thread). + // There's no need to limit the frame rate manually if there is vertical + // sync in the presentation already as that might result in inconsistent + // frame pacing and potentially skipped vertical sync intervals. + return !ui_drawers_.empty() && paint_mode_ != PaintMode::kNone && + !surface_paint_connection_has_implicit_vsync_; + } + void UpdateUITicksNeededFromUIThread(); + void WaitForUITickFromUIThread(); + // May be called from any thread. + void ForceUIThreadPaintTick(); + + // Must be called only in the end of entry points - reinitialization of the + // presenter may be done by the handler if it was called from the UI thread + // (even if the UI thread argument is false - such as when the guest output is + // refreshed on the UI thread). + HostGpuLossCallback host_gpu_loss_callback_; + + // May be accessed by the guest output thread if the paint mode is not kNone, + // to request painting (for kUIThreadOnRequest) or reconnection (for + // kGuestOutputThreadImmediately) in the UI thread. Set the paint mode to + // kNone before modifying (that naturally has to be done anyway by + // disconnecting painting). + Window* window_ = nullptr; + + // The surface of the `window_` the presenter is currently attached to. + Surface* surface_ = nullptr; + + // Mutex protecting paint_mode_ (and, in the guest output thread, objects + // related to painting themselves). + // + // The UI thread (as the mode is modifiable only by it) can use it as + // "barriers", like: + // 1) If needed, lock and disable guest output thread access to painting. + // 2) Interact with the painting connection. + // 3) If needed, lock and re-enable guest output thread access to the + // painting. + // + // On the other hand, the guest output thread _must_ hold it all the time it's + // painting, to ensure the mode stays the same while it's painting. + std::mutex paint_mode_mutex_; + // UI thread: writable, guest output thread: read-only. + PaintMode paint_mode_ = PaintMode::kNone; + + // These fields can be accessed _exclusively_ by either the UI thread or the + // guest output thread, depending on paint_mode_. + // If it's kGuestOutputThreadImmediately, they can be accessed _only_ by the + // guest output thread (though the UI thread can still read, but not modify, + // fields that are writable by the UI thread and readadable by both). + // Otherwise, they can be accessed _only_ by the UI thread. + // The connection state may be changed from the guest output thread, but only + // from kConnectedPaintable to kConnectedOutdated. + SurfacePaintConnectionState surface_paint_connection_state_ = + SurfacePaintConnectionState::kUnconnectedRetryAtStateChange; + // If the surface connection was optimal at the last paint attempt, but now + // has become suboptimal, need to try to reconnect. But only in this case - if + // the connection has been suboptimal from the very beginning don't try to + // reconnect every frame. + bool surface_paint_connection_was_optimal_at_successful_paint_ = false; + + // Modifiable only by the UI thread (therefore can be accessed by the UI + // thread regardless of the paint mode) while (re)connecting painting to the + // surface. + bool surface_paint_connection_has_implicit_vsync_ = false; + // Modifiable only by the UI thread, can be read by the thread that's + // painting. + uint32_t surface_width_in_paint_connection_ = 0; + uint32_t surface_height_in_paint_connection_ = 0; + + // Can be set by both the UI thread and the guest output thread before doing + // window_->RequestPaint() - whether an extra painting (preceded by + // reconnection if needed, and painting) was requested, primarily after some + // state change that may effect the surface painting connection, resulting in + // the need to refresh it as soon as possible. + // + // Relaxed memory order is enough, everything that may influence painting is + // either local to the UI thread or protected with barriers elsewhere. + // + // There's no need to bother about resetting this variable when losing + // connection as the next successful reconnection should be followed by a + // repaint request anyway. + std::atomic ui_thread_paint_requested_{false}; + + std::mutex guest_output_paint_config_mutex_; + // UI thread: writable, guest output thread: read-only. + GuestOutputPaintConfig guest_output_paint_config_; + + // Single-producer-multiple-consumers (lock-free SPSC + consumer lock) mailbox + // for presenting of the most up-to-date guest output image without long + // interlocking between guest output refreshing and painting. + static_assert(kGuestOutputMailboxSize == 3); + // The "acquired" image (in bits 0:1) is the one that is currently being read, + // or was last read, by a consumer of the guest output. The index of it can be + // modified only by the consumer and stays the same while it's processing the + // image. + // The "ready" image (in bits 2:3) is the most up-to-date image that the + // refresher has completely written, and a consumer may acquire it. It may be + // == acquired if there has been no refresh since the last acquisition. + // These two images can be accessed by painting in parallel, in an unordered + // way, with guest output refreshing. + std::atomic guest_output_mailbox_acquired_and_ready_{0}; + // The "writable" image is different than both "acquired" and "ready" and is + // accessible only by the guest output refreshing - it's the image that the + // refresher may write to. + uint32_t guest_output_mailbox_writable_ = 1; + // The guest output images may be consumed by two operations - painting, and + // capturing to a CPU-side buffer. These two usually never happen in parallel + // in reality though, as they're usually not even needed both at once in the + // same app within Xenia, so there's no need to create any + // complex lock-free synchronization between the two, but still, the situation + // when multiple consumers want the guest output image at the same is + // perfectly valid (unlike for producers, because even with a producer lock + // that would still be a race condition since the two refreshes themselves + // will be done in an undefined order) - so, a sufficient synchronization + // mechanism is used to make sure multiple consumers can acquire images + // without interfering with each other. + // While this is held, paint_mode_mutex_ must not be locked (the lock order is + // the reverse when painting in the guest output thread - painting is done + // with paint_mode_mutex_ held in this case, and guest output consumption + // happens as part of painting. + std::mutex guest_output_mailbox_consumer_mutex_; + + std::array + guest_output_properties_; + // Accessible only by refreshing, whether the last refresh contained an image + // rather than being blank. + bool guest_output_active_last_refresh_ = false; + + // Ordered by the Z order, and then by the time of addition. + // Note: All the iteration logic involving this Z ordering must be the same as + // in input handling (in the input listeners in the Window), but in reverse. + std::multimap ui_drawers_; + + size_t ui_draw_current_ = 0; + size_t ui_draw_current_z_order_; + std::multimap::iterator ui_draw_next_iterator_; + bool is_executing_ui_drawers_ = false; + + // Whether currently running the logic of PaintFromUIThread, so certain + // actions (such as changing the paint mode, requesting a redraw) must be + // deferred and be handled by the tail of PaintFromUIThread for consistency + // with what PaintFromUIThread does internally. + bool is_in_ui_thread_paint_ = false; + bool request_guest_output_paint_after_current_ui_thread_paint_; + bool request_ui_paint_after_current_ui_thread_paint_; + + // Platform-specific, but implementation-agnostic parts, primarily for + // limiting of the frame rate of the UI to avoid drawing the UI at extreme + // frame rates wasting the CPU and the GPU resources and starving everything + // else. The waits performed here must be interruptible by guest output + // presentation requests to prevent adding arbitrary amounts of latency to it. + // On GTK, this is not needed, the frame rate of draw signals is limited to + // the display refresh rate internally. +#if XE_PLATFORM_WIN32 + static Microsoft::WRL::ComPtr GetDXGIOutputForMonitor( + IDXGIFactory1* factory, HMONITOR monitor); + bool AreDXGIUITicksWaitable( + [[maybe_unused]] const std::unique_lock& dxgi_ui_tick_lock) { + return dxgi_ui_ticks_needed_ && !dxgi_ui_tick_thread_shutdown_ && + dxgi_ui_tick_output_; + } + void DXGIUITickThread(); + + // Accessible only from the UI thread, to avoid updating monitor-dependent + // information such as the DXGI output if the monitor hasn't actually been + // changed in the current state change (such as window positioning changes). + HMONITOR surface_win32_monitor_ = nullptr; + + // Requiring the lowest version of DXGI for IDXGIOutput::WaitForVBlank, which + // is available even on Windows Vista, but for IDXGIFactory1::IsCurrent, + // DXGI 1.1 is needed (available starting from Windows 7; also mixing DXGI 1.0 + // and 1.1+ in the Direct3D 12 code is not supported, see CreateDXGIFactory on + // MSDN). The factory is created when it's needed, and may be released and + // recreated when it's not current anymore and that becomes relevant. + Microsoft::WRL::ComPtr dxgi_ui_tick_factory_; + + // Accessible only from the UI thread, though the value is taken from the + // tick-mutex-protected variable. + uint64_t dxgi_ui_tick_last_draw_ = 0; + + std::mutex dxgi_ui_tick_mutex_; + uint64_t dxgi_ui_tick_last_vblank_ = 1; + // If output is null or shutdown is true, the signal may not be sent, either + // don't limit the frame rate in this case (an exceptional situation, such as + // a failure to find the output in DXGI), or don't draw at all if the window + // was removed from a connected monitor. + Microsoft::WRL::ComPtr dxgi_ui_tick_output_; + // To avoid allocating processing resources to the thread when nothing needs + // the ticks (not drawing the UI), the thread waits for vertical blanking + // intervals only when the UI drawing ticks are needed, and sleeping waiting + // for the control condition variable signals otherwise. Modifiable only from + // the UI thread, so readable by it without locking the mutex. + bool dxgi_ui_ticks_needed_ = false; + // The shutdown flag is modifiable only from the UI thread. + bool dxgi_ui_tick_thread_shutdown_ = false; + bool dxgi_ui_tick_force_requested_ = false; + + std::condition_variable dxgi_ui_tick_control_condition_; + // May be signaled by guest output refreshing. + std::condition_variable dxgi_ui_tick_signal_condition_; + + std::thread dxgi_ui_tick_thread_; +#endif // XE_PLATFORM +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_PRESENTER_H_ diff --git a/src/xenia/ui/renderdoc_api.cc b/src/xenia/ui/renderdoc_api.cc new file mode 100644 index 000000000..7c5f7ef99 --- /dev/null +++ b/src/xenia/ui/renderdoc_api.cc @@ -0,0 +1,76 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/renderdoc_api.h" + +#include "xenia/base/assert.h" +#include "xenia/base/logging.h" +#include "xenia/base/platform.h" + +#if XE_PLATFORM_LINUX +#include +#elif XE_PLATFORM_WIN32 +#include "xenia/base/platform_win.h" +#endif + +namespace xe { +namespace ui { + +bool RenderdocApi::Initialize() { + Shutdown(); + pRENDERDOC_GetAPI get_api = nullptr; + // The RenderDoc library should be already loaded into the process if + // RenderDoc is attached - this is why RTLD_NOLOAD or GetModuleHandle instead + // of LoadLibrary. +#if XE_PLATFORM_LINUX +#if XE_PLATFORM_ANDROID + const char* librenderdoc_name = "libVkLayer_GLES_RenderDoc.so"; +#else + const char* librenderdoc_name = "librenderdoc.so"; +#endif + library_ = dlopen(librenderdoc_name, RTLD_NOW | RTLD_NOLOAD); + if (library_) { + get_api = pRENDERDOC_GetAPI(dlsym(library_, "RENDERDOC_GetAPI")); + } +#elif XE_PLATFORM_WIN32 + library_ = GetModuleHandleA("renderdoc.dll"); + if (library_) { + get_api = pRENDERDOC_GetAPI( + GetProcAddress(HMODULE(library_), "RENDERDOC_GetAPI")); + } +#endif + if (!get_api) { + Shutdown(); + return false; + } + // get_api will be null if RenderDoc is not attached, or the API isn't + // available on this platform, or there was an error. + if (!get_api || !get_api(eRENDERDOC_API_Version_1_0_0, (void**)&api_1_0_0_) || + !api_1_0_0_) { + Shutdown(); + return false; + } + XELOGI("RenderDoc API initialized"); + return true; +} + +void RenderdocApi::Shutdown() { + api_1_0_0_ = nullptr; + if (library_) { +#if XE_PLATFORM_LINUX + dlclose(library_); +#endif + // Not calling FreeLibrary on Windows as GetModuleHandle doesn't increment + // the reference count. + library_ = nullptr; + } +} + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/renderdoc_api.h b/src/xenia/ui/renderdoc_api.h new file mode 100644 index 000000000..1a07fe116 --- /dev/null +++ b/src/xenia/ui/renderdoc_api.h @@ -0,0 +1,39 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_RENDERDOC_API_H_ +#define XENIA_UI_RENDERDOC_API_H_ + +#include "third_party/renderdoc/renderdoc_app.h" + +namespace xe { +namespace ui { + +class RenderdocApi { + public: + RenderdocApi() = default; + RenderdocApi(const RenderdocApi& renderdoc_api) = delete; + RenderdocApi& operator=(const RenderdocApi& renderdoc_api) = delete; + ~RenderdocApi() { Shutdown(); } + + bool Initialize(); + void Shutdown(); + + // nullptr if not attached. + const RENDERDOC_API_1_0_0* api_1_0_0() const { return api_1_0_0_; } + + private: + void* library_ = nullptr; + const RENDERDOC_API_1_0_0* api_1_0_0_ = nullptr; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_RENDERDOC_API_H_ \ No newline at end of file diff --git a/src/xenia/ui/shaders/bytecode/.clang-format b/src/xenia/ui/shaders/bytecode/.clang-format index e3845288a..9d159247d 100644 --- a/src/xenia/ui/shaders/bytecode/.clang-format +++ b/src/xenia/ui/shaders/bytecode/.clang-format @@ -1 +1,2 @@ DisableFormat: true +SortIncludes: false diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_dither_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_dither_ps.h new file mode 100644 index 000000000..3c22503d1 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_dither_ps.h @@ -0,0 +1,1226 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeBilinearConstants +// { +// +// int2 xe_bilinear_output_offset; // Offset: 0 Size: 8 +// float2 xe_bilinear_output_size_inv;// Offset: 8 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_sampler_linear_clamp sampler NA NA S0 s0 1 +// xe_texture texture float3 2d T0 t0 1 +// XeBilinearConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_5_1 +dcl_globalFlags refactoringAllowed +dcl_immediateConstantBuffer { { -0.001003, 0, 0, 0}, + { 0.000881, 0, 0, 0}, + { 0.001693, 0, 0, 0}, + { -0.001555, 0, 0, 0}, + { 0.001279, 0, 0, 0}, + { -0.000605, 0, 0, 0}, + { 0.001065, 0, 0, 0}, + { -0.001386, 0, 0, 0}, + { 0.001356, 0, 0, 0}, + { 0.000513, 0, 0, 0}, + { 0.001218, 0, 0, 0}, + { -0.001601, 0, 0, 0}, + { 0.000590, 0, 0, 0}, + { -0.000283, 0, 0, 0}, + { 0.001111, 0, 0, 0}, + { -0.001417, 0, 0, 0}, + { 0.001448, 0, 0, 0}, + { -0.000544, 0, 0, 0}, + { 0.000130, 0, 0, 0}, + { -0.001203, 0, 0, 0}, + { 0.000437, 0, 0, 0}, + { -0.001049, 0, 0, 0}, + { 0.000620, 0, 0, 0}, + { -0.000483, 0, 0, 0}, + { 0.001877, 0, 0, 0}, + { -0.001095, 0, 0, 0}, + { -0.000100, 0, 0, 0}, + { -0.000528, 0, 0, 0}, + { 0.001432, 0, 0, 0}, + { -0.001938, 0, 0, 0}, + { -0.000697, 0, 0, 0}, + { 0.000038, 0, 0, 0}, + { 0.000712, 0, 0, 0}, + { -0.001310, 0, 0, 0}, + { 0.001095, 0, 0, 0}, + { -0.000299, 0, 0, 0}, + { 0.001754, 0, 0, 0}, + { -0.001677, 0, 0, 0}, + { 0.001478, 0, 0, 0}, + { -0.000038, 0, 0, 0}, + { 0.000789, 0, 0, 0}, + { -0.001831, 0, 0, 0}, + { 0.000299, 0, 0, 0}, + { 0.000988, 0, 0, 0}, + { -0.001172, 0, 0, 0}, + { 0.000176, 0, 0, 0}, + { 0.001647, 0, 0, 0}, + { -0.001585, 0, 0, 0}, + { 0.000345, 0, 0, 0}, + { 0.001861, 0, 0, 0}, + { -0.001769, 0, 0, 0}, + { -0.000866, 0, 0, 0}, + { 0.000896, 0, 0, 0}, + { 0.000161, 0, 0, 0}, + { -0.000927, 0, 0, 0}, + { -0.001524, 0, 0, 0}, + { -0.000651, 0, 0, 0}, + { 0.001294, 0, 0, 0}, + { -0.000804, 0, 0, 0}, + { -0.001463, 0, 0, 0}, + { 0.001800, 0, 0, 0}, + { -0.000850, 0, 0, 0}, + { 0.000850, 0, 0, 0}, + { -0.000452, 0, 0, 0}, + { -0.001065, 0, 0, 0}, + { -0.000146, 0, 0, 0}, + { 0.000237, 0, 0, 0}, + { 0.001417, 0, 0, 0}, + { -0.000590, 0, 0, 0}, + { -0.000191, 0, 0, 0}, + { 0.001601, 0, 0, 0}, + { 0.001019, 0, 0, 0}, + { 0.000406, 0, 0, 0}, + { -0.000207, 0, 0, 0}, + { 0.001585, 0, 0, 0}, + { 0.000651, 0, 0, 0}, + { -0.000069, 0, 0, 0}, + { 0.000421, 0, 0, 0}, + { -0.001647, 0, 0, 0}, + { 0.001371, 0, 0, 0}, + { 0.000927, 0, 0, 0}, + { -0.000666, 0, 0, 0}, + { 0.001187, 0, 0, 0}, + { -0.001448, 0, 0, 0}, + { 0.000574, 0, 0, 0}, + { -0.001892, 0, 0, 0}, + { 0.000758, 0, 0, 0}, + { -0.001294, 0, 0, 0}, + { 0.001922, 0, 0, 0}, + { -0.001662, 0, 0, 0}, + { -0.001034, 0, 0, 0}, + { -0.000498, 0, 0, 0}, + { -0.001861, 0, 0, 0}, + { 0.001203, 0, 0, 0}, + { -0.000329, 0, 0, 0}, + { -0.001371, 0, 0, 0}, + { 0.001631, 0, 0, 0}, + { -0.001846, 0, 0, 0}, + { 0.000728, 0, 0, 0}, + { -0.000911, 0, 0, 0}, + { 0.001815, 0, 0, 0}, + { -0.001141, 0, 0, 0}, + { -0.000375, 0, 0, 0}, + { 0.000100, 0, 0, 0}, + { -0.000743, 0, 0, 0}, + { 0.001172, 0, 0, 0}, + { 0.000069, 0, 0, 0}, + { 0.001494, 0, 0, 0}, + { 0.000973, 0, 0, 0}, + { -0.000957, 0, 0, 0}, + { 0.001938, 0, 0, 0}, + { 0.000528, 0, 0, 0}, + { 0.000054, 0, 0, 0}, + { -0.001248, 0, 0, 0}, + { -0.000268, 0, 0, 0}, + { 0.001540, 0, 0, 0}, + { -0.000008, 0, 0, 0}, + { 0.000314, 0, 0, 0}, + { 0.001340, 0, 0, 0}, + { -0.001754, 0, 0, 0}, + { 0.000498, 0, 0, 0}, + { -0.001187, 0, 0, 0}, + { 0.000774, 0, 0, 0}, + { -0.001340, 0, 0, 0}, + { 0.000268, 0, 0, 0}, + { -0.001478, 0, 0, 0}, + { -0.000130, 0, 0, 0}, + { -0.000774, 0, 0, 0}, + { 0.001310, 0, 0, 0}, + { 0.000391, 0, 0, 0}, + { 0.000957, 0, 0, 0}, + { -0.000467, 0, 0, 0}, + { -0.001540, 0, 0, 0}, + { 0.001034, 0, 0, 0}, + { -0.000682, 0, 0, 0}, + { 0.001677, 0, 0, 0}, + { 0.001003, 0, 0, 0}, + { -0.000421, 0, 0, 0}, + { 0.001785, 0, 0, 0}, + { -0.000237, 0, 0, 0}, + { -0.000620, 0, 0, 0}, + { 0.001662, 0, 0, 0}, + { 0.000835, 0, 0, 0}, + { -0.001723, 0, 0, 0}, + { -0.001080, 0, 0, 0}, + { 0.001769, 0, 0, 0}, + { -0.000789, 0, 0, 0}, + { -0.001785, 0, 0, 0}, + { 0.000682, 0, 0, 0}, + { -0.000988, 0, 0, 0}, + { -0.001325, 0, 0, 0}, + { -0.000176, 0, 0, 0}, + { -0.001509, 0, 0, 0}, + { 0.000329, 0, 0, 0}, + { -0.001953, 0, 0, 0}, + { 0.000666, 0, 0, 0}, + { -0.001616, 0, 0, 0}, + { 0.001157, 0, 0, 0}, + { 0.000452, 0, 0, 0}, + { -0.000437, 0, 0, 0}, + { 0.000191, 0, 0, 0}, + { -0.001494, 0, 0, 0}, + { 0.001141, 0, 0, 0}, + { 0.000084, 0, 0, 0}, + { 0.001892, 0, 0, 0}, + { 0.001402, 0, 0, 0}, + { 0.000559, 0, 0, 0}, + { 0.000115, 0, 0, 0}, + { 0.001264, 0, 0, 0}, + { -0.000574, 0, 0, 0}, + { -0.000973, 0, 0, 0}, + { 0.001325, 0, 0, 0}, + { 0.000222, 0, 0, 0}, + { -0.000758, 0, 0, 0}, + { -0.001356, 0, 0, 0}, + { 0.001463, 0, 0, 0}, + { 0.000866, 0, 0, 0}, + { -0.000360, 0, 0, 0}, + { 0.000544, 0, 0, 0}, + { -0.001126, 0, 0, 0}, + { -0.000253, 0, 0, 0}, + { -0.000559, 0, 0, 0}, + { -0.001815, 0, 0, 0}, + { 0.001723, 0, 0, 0}, + { -0.001157, 0, 0, 0}, + { 0.000743, 0, 0, 0}, + { 0.001570, 0, 0, 0}, + { -0.000115, 0, 0, 0}, + { -0.001218, 0, 0, 0}, + { 0.001831, 0, 0, 0}, + { 0.000023, 0, 0, 0}, + { -0.001922, 0, 0, 0}, + { 0.001739, 0, 0, 0}, + { -0.000712, 0, 0, 0}, + { 0.001555, 0, 0, 0}, + { -0.001708, 0, 0, 0}, + { 0.001233, 0, 0, 0}, + { 0.000207, 0, 0, 0}, + { 0.001049, 0, 0, 0}, + { -0.000728, 0, 0, 0}, + { -0.001631, 0, 0, 0}, + { -0.000314, 0, 0, 0}, + { 0.000483, 0, 0, 0}, + { -0.001800, 0, 0, 0}, + { 0.000942, 0, 0, 0}, + { -0.000345, 0, 0, 0}, + { 0.000697, 0, 0, 0}, + { -0.001019, 0, 0, 0}, + { -0.001570, 0, 0, 0}, + { -0.000023, 0, 0, 0}, + { -0.001279, 0, 0, 0}, + { 0.000804, 0, 0, 0}, + { -0.000896, 0, 0, 0}, + { -0.001432, 0, 0, 0}, + { 0.000605, 0, 0, 0}, + { -0.000084, 0, 0, 0}, + { 0.000911, 0, 0, 0}, + { 0.001953, 0, 0, 0}, + { -0.001402, 0, 0, 0}, + { -0.000636, 0, 0, 0}, + { 0.001509, 0, 0, 0}, + { -0.000820, 0, 0, 0}, + { 0.001248, 0, 0, 0}, + { 0.000253, 0, 0, 0}, + { 0.001524, 0, 0, 0}, + { 0.001126, 0, 0, 0}, + { 0.000360, 0, 0, 0}, + { -0.000391, 0, 0, 0}, + { 0.001907, 0, 0, 0}, + { 0.001386, 0, 0, 0}, + { -0.001111, 0, 0, 0}, + { 0.001616, 0, 0, 0}, + { -0.000881, 0, 0, 0}, + { 0.000146, 0, 0, 0}, + { 0.001080, 0, 0, 0}, + { -0.000054, 0, 0, 0}, + { 0.000283, 0, 0, 0}, + { -0.001693, 0, 0, 0}, + { -0.001264, 0, 0, 0}, + { -0.000513, 0, 0, 0}, + { -0.000161, 0, 0, 0}, + { -0.001877, 0, 0, 0}, + { -0.000835, 0, 0, 0}, + { 0.000636, 0, 0, 0}, + { 0.000008, 0, 0, 0}, + { -0.001907, 0, 0, 0}, + { -0.000222, 0, 0, 0}, + { 0.000375, 0, 0, 0}, + { -0.001739, 0, 0, 0}, + { -0.000406, 0, 0, 0}, + { -0.001233, 0, 0, 0}, + { 0.001708, 0, 0, 0}, + { -0.000942, 0, 0, 0}, + { 0.000820, 0, 0, 0}, + { 0.001846, 0, 0, 0}, + { 0.000467, 0, 0, 0} } +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_sampler S0[0:0], mode_default, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_input_ps_siv linear noperspective v0.xy, position +dcl_output o0.xyzw +dcl_temps 2 +ftoi r0.xy, v0.xyxx +iadd r0.xy, r0.xyxx, -CB0[0][0].xyxx +utof r0.zw, r0.xxxy +add r0.zw, r0.zzzw, l(0.000000, 0.000000, 0.500000, 0.500000) +mul r0.zw, r0.zzzw, CB0[0][0].zzzw +sample_l r1.xyz, r0.zwzz, T0[0].xyzw, S0[0], l(0.000000) +bfi r0.y, l(4), l(4), r0.y, l(0) +bfi r0.x, l(4), l(0), r0.x, r0.y +add o0.xyz, r1.xyzx, icb[r0.x + 0].xxxx +mov o0.w, l(1.000000) +ret +// Approximately 11 instruction slots used +#endif + +const BYTE guest_output_bilinear_dither_ps[] = +{ + 68, 88, 66, 67, 238, 251, + 7, 36, 12, 102, 119, 199, + 78, 59, 74, 185, 195, 243, + 55, 64, 1, 0, 0, 0, + 56, 21, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 68, 2, 0, 0, 120, 2, + 0, 0, 172, 2, 0, 0, + 156, 20, 0, 0, 82, 68, + 69, 70, 8, 2, 0, 0, + 1, 0, 0, 0, 236, 0, + 0, 0, 3, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 255, 255, 0, 5, 0, 0, + 224, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 180, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 204, 0, + 0, 0, 2, 0, 0, 0, + 5, 0, 0, 0, 4, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 215, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 120, 101, 95, 115, 97, 109, + 112, 108, 101, 114, 95, 108, + 105, 110, 101, 97, 114, 95, + 99, 108, 97, 109, 112, 0, + 120, 101, 95, 116, 101, 120, + 116, 117, 114, 101, 0, 88, + 101, 66, 105, 108, 105, 110, + 101, 97, 114, 67, 111, 110, + 115, 116, 97, 110, 116, 115, + 0, 171, 215, 0, 0, 0, + 2, 0, 0, 0, 4, 1, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 84, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 116, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 152, 1, 0, 0, 8, 0, + 0, 0, 8, 0, 0, 0, + 2, 0, 0, 0, 188, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 120, 101, + 95, 98, 105, 108, 105, 110, + 101, 97, 114, 95, 111, 117, + 116, 112, 117, 116, 95, 111, + 102, 102, 115, 101, 116, 0, + 105, 110, 116, 50, 0, 171, + 1, 0, 2, 0, 1, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 110, 1, 0, 0, + 120, 101, 95, 98, 105, 108, + 105, 110, 101, 97, 114, 95, + 111, 117, 116, 112, 117, 116, + 95, 115, 105, 122, 101, 95, + 105, 110, 118, 0, 102, 108, + 111, 97, 116, 50, 0, 171, + 1, 0, 3, 0, 1, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 180, 1, 0, 0, + 77, 105, 99, 114, 111, 115, + 111, 102, 116, 32, 40, 82, + 41, 32, 72, 76, 83, 76, + 32, 83, 104, 97, 100, 101, + 114, 32, 67, 111, 109, 112, + 105, 108, 101, 114, 32, 49, + 48, 46, 49, 0, 73, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 3, + 0, 0, 83, 86, 95, 80, + 111, 115, 105, 116, 105, 111, + 110, 0, 79, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 83, 86, 95, 84, 97, 114, + 103, 101, 116, 0, 171, 171, + 83, 72, 69, 88, 232, 17, + 0, 0, 81, 0, 0, 0, + 122, 4, 0, 0, 106, 8, + 0, 1, 53, 24, 0, 0, + 2, 4, 0, 0, 132, 131, + 131, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 231, 230, 102, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 222, 221, 221, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 204, 203, + 203, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 168, 167, 167, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 159, 158, 30, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 140, 139, + 139, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 182, 181, 181, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 178, 177, 177, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 135, 134, + 6, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 160, 159, 159, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 210, 209, 209, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 155, 154, + 26, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 149, 148, 148, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 146, 145, 145, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 186, 185, + 185, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 190, 189, 189, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 143, 142, 14, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 137, 136, + 8, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 158, 157, 157, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 229, 228, 228, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 138, 137, + 137, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 163, 162, 34, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 253, 252, 252, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 246, 245, + 245, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 144, 143, 143, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 209, 208, 208, 184, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 139, 138, + 10, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 188, 187, 187, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 254, 253, 253, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 183, 182, + 54, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 161, 160, 32, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 187, 186, 58, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 172, 171, + 171, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 144, 143, 143, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 157, 156, 156, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 230, 229, + 229, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 220, 219, 219, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 194, 193, 193, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 161, 160, + 32, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 207, 206, 78, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 240, 239, 239, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 157, 156, + 156, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 130, 129, 129, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 154, 153, 153, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 185, 184, + 56, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 216, 215, 215, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 208, 207, 207, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 181, 180, + 180, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 244, 243, 243, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 232, 231, 231, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 227, 226, + 98, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 235, 234, 106, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 169, 168, 40, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 243, 242, + 114, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 200, 199, 199, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 171, 170, 42, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 170, 169, + 169, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 211, 210, 82, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 192, 191, 191, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 236, 235, + 235, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 223, 222, 94, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 223, 222, 94, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 236, + 236, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 140, 139, 139, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 153, 152, 24, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 248, + 120, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 186, 185, 185, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 155, 154, 26, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 201, 200, + 72, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 210, 209, 209, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 134, 133, 133, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 213, 212, + 212, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 217, 216, 88, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 208, 207, 207, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 171, 170, + 42, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 145, 144, 144, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 221, 220, 220, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 215, + 215, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 180, 179, 179, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 243, 242, 114, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 175, 174, + 46, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 156, 155, 155, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 190, 189, 189, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 150, + 22, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 248, 247, 247, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 199, 198, 70, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 170, 169, + 169, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 252, 251, 251, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 218, 217, 217, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 136, 135, + 135, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 131, 130, 2, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 244, 243, 243, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 158, 157, + 157, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 173, 172, 172, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 180, 179, 179, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 214, 213, + 213, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 242, 241, 241, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 191, 190, 62, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 239, 238, + 110, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 238, 237, 237, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 150, 149, 149, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 197, 196, + 196, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 209, 208, 208, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 195, 194, 66, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 154, 153, + 153, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 145, 144, 144, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 196, 195, 195, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 254, + 126, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 251, 250, 122, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 254, 253, 253, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 139, 138, + 10, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 225, 224, 96, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 164, 163, 163, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 141, 140, + 140, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 202, 201, 201, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 129, 128, 0, 183, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 165, 164, + 164, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 176, 175, 175, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 230, 229, 229, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 131, 130, + 2, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 156, 155, 155, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 203, 202, 74, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 176, 175, + 175, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 141, 140, 140, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 194, 193, 193, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 137, 136, + 8, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 203, 202, 74, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 172, 171, 171, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 205, 204, + 204, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 251, 250, 122, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 245, 244, 244, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 202, 201, + 201, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 136, 135, 135, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 179, 178, 50, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 220, 219, + 219, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 132, 131, 131, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 221, 220, 220, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 234, 233, + 233, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 249, 248, 120, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 163, 162, 34, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 218, 217, + 217, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 219, 218, 90, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 226, 225, 225, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 142, 141, + 141, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 232, 231, 231, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 207, 206, 78, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 234, 233, + 233, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 179, 178, 50, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 130, 129, 129, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 174, 173, + 173, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 185, 184, 56, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 198, 197, 197, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 173, 172, + 172, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 187, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 175, 174, 46, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 212, 211, + 211, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 152, 151, 151, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 237, 236, 236, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 229, 228, + 228, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 201, 200, 72, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 196, 195, 195, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 150, 149, + 149, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 177, 176, 176, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 248, 247, 247, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 184, 183, + 183, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 147, 146, 18, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 241, 240, 240, 56, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 166, 165, + 165, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 151, 150, 22, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 255, 254, 126, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 174, 173, + 173, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 233, 232, 104, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 199, 198, 70, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 178, 177, + 177, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 192, 191, 191, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 227, 226, 98, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 189, 188, + 188, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 143, 142, 14, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 148, 147, 147, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 133, 132, + 132, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 147, 146, 18, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 238, 237, 237, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 226, 225, + 225, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 152, 151, 151, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 195, 194, 66, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 206, 205, + 205, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 241, 240, 240, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 160, 159, 159, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 240, 239, + 239, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 193, 192, 192, 55, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 252, 251, 251, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 227, + 227, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 187, 186, 58, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 204, 203, 203, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 224, 223, + 223, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 162, 161, 161, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 217, 216, 88, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 138, 137, + 137, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 191, 190, 62, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 214, 213, 213, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 165, 164, + 164, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 253, 252, 252, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 236, 235, 235, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 247, 246, + 118, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 181, 180, 180, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 183, 182, 54, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 134, 133, + 133, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 206, 205, 205, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 193, 192, 192, 183, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 168, 167, + 167, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 211, 210, 82, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 235, 234, 106, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 188, 187, + 187, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 159, 158, 30, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 177, 176, 176, 184, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 239, 238, + 110, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 59, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 184, 183, 183, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 167, 166, + 38, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 198, 197, 197, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 215, 214, 86, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 164, 163, + 163, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 133, 132, 132, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 200, 199, 199, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 148, 147, + 147, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 189, 188, 188, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 205, 204, 204, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 250, 249, + 249, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 182, 181, 181, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 146, 145, 145, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 212, 211, + 211, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 231, 230, 102, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 153, 152, 24, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 142, 141, + 141, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 225, 224, 96, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 149, 148, 148, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 222, 221, + 221, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 166, 165, 165, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 135, 134, 6, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 169, 168, + 40, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 246, 245, 245, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 219, 218, 90, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 167, 166, + 38, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 129, 128, 0, 55, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 250, 249, 249, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 233, 232, + 104, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 197, 196, 196, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 228, 227, 227, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 213, 212, + 212, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 162, 161, 161, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 224, 223, 223, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 247, 246, + 118, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 215, 214, 86, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 242, 241, 241, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 245, 244, + 244, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 89, 0, 0, 7, + 70, 142, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 90, 0, 0, 6, 70, 110, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 88, 24, 0, 7, 70, 126, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 85, 85, 0, 0, + 0, 0, 0, 0, 100, 32, + 0, 4, 50, 16, 16, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 0, 0, + 0, 0, 104, 0, 0, 2, + 2, 0, 0, 0, 27, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 16, + 16, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 70, 128, 48, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 86, 0, + 0, 5, 194, 0, 16, 0, + 0, 0, 0, 0, 6, 4, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 194, 0, + 16, 0, 0, 0, 0, 0, + 166, 14, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 63, + 0, 0, 0, 63, 56, 0, + 0, 9, 194, 0, 16, 0, + 0, 0, 0, 0, 166, 14, + 16, 0, 0, 0, 0, 0, + 166, 142, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 72, 0, + 0, 13, 114, 0, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 140, 0, 0, 11, + 34, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 140, 0, + 0, 11, 18, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 114, 32, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 6, 144, 144, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 130, 32, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 62, 0, + 0, 1, 83, 84, 65, 84, + 148, 0, 0, 0, 11, 0, + 0, 0, 2, 0, 0, 0, + 0, 1, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_gamma_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_ps.h similarity index 53% rename from src/xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_gamma_ps.h rename to src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_ps.h index b0ff6436a..c12024a7a 100644 --- a/src/xenia/gpu/shaders/bytecode/d3d12_5_1/stretch_gamma_ps.h +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_bilinear_ps.h @@ -5,10 +5,11 @@ // // Buffer Definitions: // -// cbuffer XeStretchGammaRootConstants +// cbuffer XeBilinearConstants // { // -// float xe_gamma_ramp_inv_size; // Offset: 0 Size: 4 +// int2 xe_bilinear_output_offset; // Offset: 0 Size: 8 +// float2 xe_bilinear_output_size_inv;// Offset: 8 Size: 8 // // } // @@ -19,8 +20,7 @@ // ------------------------------ ---------- ------- ----------- ------- -------------- ------ // xe_sampler_linear_clamp sampler NA NA S0 s0 1 // xe_texture texture float3 2d T0 t0 1 -// xe_gamma_ramp texture float3 1d T1 t1 1 -// XeStretchGammaRootConstants cbuffer NA NA CB0 cb0 1 +// XeBilinearConstants cbuffer NA NA CB0 cb0 1 // // // @@ -28,7 +28,7 @@ // // Name Index Mask Register SysValue Format Used // -------------------- ----- ------ -------- -------- ------- ------ -// TEXCOORD 0 xy 0 NONE float xy +// SV_Position 0 xyzw 0 POS float xy // // // Output signature: @@ -42,107 +42,113 @@ dcl_globalFlags refactoringAllowed dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 dcl_sampler S0[0:0], mode_default, space=0 dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 -dcl_resource_texture1d (float,float,float,float) T1[1:1], space=0 -dcl_input_ps linear v0.xy +dcl_input_ps_siv linear noperspective v0.xy, position dcl_output o0.xyzw -dcl_temps 2 -sample_l r0.xyz, v0.xyxx, T0[0].xyzw, S0[0], l(0.000000) -add r0.w, -CB0[0][0].x, l(1.000000) -mul r1.x, CB0[0][0].x, l(0.500000) -mad r0.xyz, r0.xyzx, r0.wwww, r1.xxxx -sample_l r0.x, r0.x, T1[1].xyzw, S0[0], l(0.000000) -sample_l r0.y, r0.y, T1[1].xyzw, S0[0], l(0.000000) -sample_l r0.z, r0.z, T1[1].xyzw, S0[0], l(0.000000) +dcl_temps 1 +ftoi r0.xy, v0.xyxx +iadd r0.xy, r0.xyxx, -CB0[0][0].xyxx +utof r0.xy, r0.xyxx +add r0.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000) +mul r0.xy, r0.xyxx, CB0[0][0].zwzz +sample_l r0.xyz, r0.xyxx, T0[0].xyzw, S0[0], l(0.000000) mov o0.xyz, r0.xyzx mov o0.w, l(1.000000) ret -// Approximately 10 instruction slots used +// Approximately 9 instruction slots used #endif -const BYTE stretch_gamma_ps[] = +const BYTE guest_output_bilinear_ps[] = { - 68, 88, 66, 67, 4, 222, - 150, 161, 149, 185, 43, 51, - 177, 110, 80, 244, 108, 113, - 108, 20, 1, 0, 0, 0, - 36, 5, 0, 0, 5, 0, + 68, 88, 66, 67, 195, 73, + 6, 106, 30, 227, 35, 134, + 146, 186, 4, 157, 98, 172, + 18, 157, 1, 0, 0, 0, + 204, 4, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, - 20, 2, 0, 0, 72, 2, - 0, 0, 124, 2, 0, 0, - 136, 4, 0, 0, 82, 68, - 69, 70, 216, 1, 0, 0, - 1, 0, 0, 0, 44, 1, - 0, 0, 4, 0, 0, 0, + 68, 2, 0, 0, 120, 2, + 0, 0, 172, 2, 0, 0, + 48, 4, 0, 0, 82, 68, + 69, 70, 8, 2, 0, 0, + 1, 0, 0, 0, 236, 0, + 0, 0, 3, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 255, 255, 0, 5, 4, 0, - 176, 1, 0, 0, 19, 19, + 255, 255, 0, 5, 0, 0, + 224, 1, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, 40, 0, 0, 0, 36, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, - 220, 0, 0, 0, 3, 0, + 180, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 244, 0, + 0, 0, 0, 0, 204, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 4, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, - 2, 0, 0, 0, 5, 0, + 0, 0, 215, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 120, 101, 95, 115, 97, 109, + 112, 108, 101, 114, 95, 108, + 105, 110, 101, 97, 114, 95, + 99, 108, 97, 109, 112, 0, + 120, 101, 95, 116, 101, 120, + 116, 117, 114, 101, 0, 88, + 101, 66, 105, 108, 105, 110, + 101, 97, 114, 67, 111, 110, + 115, 116, 97, 110, 116, 115, + 0, 171, 215, 0, 0, 0, + 2, 0, 0, 0, 4, 1, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 84, 1, 0, 0, + 0, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, - 255, 255, 255, 255, 1, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 1, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 120, 101, - 95, 115, 97, 109, 112, 108, - 101, 114, 95, 108, 105, 110, - 101, 97, 114, 95, 99, 108, - 97, 109, 112, 0, 120, 101, - 95, 116, 101, 120, 116, 117, - 114, 101, 0, 120, 101, 95, - 103, 97, 109, 109, 97, 95, - 114, 97, 109, 112, 0, 88, - 101, 83, 116, 114, 101, 116, - 99, 104, 71, 97, 109, 109, - 97, 82, 111, 111, 116, 67, - 111, 110, 115, 116, 97, 110, - 116, 115, 0, 171, 171, 171, - 13, 1, 0, 0, 1, 0, - 0, 0, 68, 1, 0, 0, - 16, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 108, 1, 0, 0, 0, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 140, 1, + 116, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 152, 1, 0, 0, 8, 0, + 0, 0, 8, 0, 0, 0, + 2, 0, 0, 0, 188, 1, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 120, 101, - 95, 103, 97, 109, 109, 97, - 95, 114, 97, 109, 112, 95, - 105, 110, 118, 95, 115, 105, - 122, 101, 0, 102, 108, 111, - 97, 116, 0, 171, 171, 171, - 0, 0, 3, 0, 1, 0, - 1, 0, 0, 0, 0, 0, + 95, 98, 105, 108, 105, 110, + 101, 97, 114, 95, 111, 117, + 116, 112, 117, 116, 95, 111, + 102, 102, 115, 101, 116, 0, + 105, 110, 116, 50, 0, 171, + 1, 0, 2, 0, 1, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 131, 1, 0, 0, + 0, 0, 110, 1, 0, 0, + 120, 101, 95, 98, 105, 108, + 105, 110, 101, 97, 114, 95, + 111, 117, 116, 112, 117, 116, + 95, 115, 105, 122, 101, 95, + 105, 110, 118, 0, 102, 108, + 111, 97, 116, 50, 0, 171, + 1, 0, 3, 0, 1, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 180, 1, 0, 0, 77, 105, 99, 114, 111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, @@ -153,12 +159,12 @@ const BYTE stretch_gamma_ps[] = 71, 78, 44, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 3, 3, - 0, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 171, - 171, 171, 79, 83, 71, 78, + 0, 0, 0, 0, 15, 3, + 0, 0, 83, 86, 95, 80, + 111, 115, 105, 116, 105, 111, + 110, 0, 79, 83, 71, 78, 44, 0, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 32, 0, 0, 0, 0, 0, @@ -167,9 +173,9 @@ const BYTE stretch_gamma_ps[] = 0, 0, 15, 0, 0, 0, 83, 86, 95, 84, 97, 114, 103, 101, 116, 0, 171, 171, - 83, 72, 69, 88, 4, 2, + 83, 72, 69, 88, 124, 1, 0, 0, 81, 0, 0, 0, - 129, 0, 0, 0, 106, 8, + 95, 0, 0, 0, 106, 8, 0, 1, 89, 0, 0, 7, 70, 142, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -183,92 +189,71 @@ const BYTE stretch_gamma_ps[] = 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 85, 0, 0, - 0, 0, 0, 0, 88, 16, - 0, 7, 70, 126, 48, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 85, 85, 0, 0, 0, 0, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 0, 0, + 0, 0, 0, 0, 100, 32, + 0, 4, 50, 16, 16, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 101, 0, 0, 3, 242, 32, 16, 0, 0, 0, 0, 0, 104, 0, 0, 2, - 2, 0, 0, 0, 72, 0, - 0, 13, 114, 0, 16, 0, + 1, 0, 0, 0, 27, 0, + 0, 5, 50, 0, 16, 0, 0, 0, 0, 0, 70, 16, 16, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 70, 128, 48, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 86, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 9, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 230, 138, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 72, 0, + 0, 13, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, 70, 126, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10, - 130, 0, 16, 0, 0, 0, - 0, 0, 10, 128, 48, 128, - 65, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 114, 32, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 130, 32, 16, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 128, 63, - 56, 0, 0, 9, 18, 0, - 16, 0, 1, 0, 0, 0, - 10, 128, 48, 0, 0, 0, + 62, 0, 0, 1, 83, 84, + 65, 84, 148, 0, 0, 0, + 9, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 0, 63, - 50, 0, 0, 9, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 246, 15, 16, 0, - 0, 0, 0, 0, 6, 0, - 16, 0, 1, 0, 0, 0, - 72, 0, 0, 13, 18, 0, - 16, 0, 0, 0, 0, 0, - 10, 0, 16, 0, 0, 0, - 0, 0, 70, 126, 32, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 0, 96, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 64, 0, 0, - 0, 0, 0, 0, 72, 0, - 0, 13, 34, 0, 16, 0, - 0, 0, 0, 0, 26, 0, - 16, 0, 0, 0, 0, 0, - 70, 126, 32, 0, 1, 0, + 2, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, - 0, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 0, 0, 72, 0, 0, 13, - 66, 0, 16, 0, 0, 0, - 0, 0, 42, 0, 16, 0, - 0, 0, 0, 0, 70, 126, - 32, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 0, 96, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 5, 114, 32, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 130, 32, 16, 0, 0, 0, - 0, 0, 1, 64, 0, 0, - 0, 0, 128, 63, 62, 0, - 0, 1, 83, 84, 65, 84, - 148, 0, 0, 0, 10, 0, - 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, - 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -278,7 +263,5 @@ const BYTE stretch_gamma_ps[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0 + 0, 0, 0, 0 }; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_dither_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_dither_ps.h new file mode 100644 index 000000000..924aed761 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_dither_ps.h @@ -0,0 +1,2227 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeCasResampleConstants +// { +// +// int2 xe_cas_output_offset; // Offset: 0 Size: 8 +// float2 xe_cas_input_output_size_ratio;// Offset: 8 Size: 8 +// float xe_cas_sharpness_post_setup; // Offset: 16 Size: 4 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_texture texture float3 2d T0 t0 1 +// XeCasResampleConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_5_1 +dcl_globalFlags refactoringAllowed +dcl_immediateConstantBuffer { { -0.001003, 0, 0, 0}, + { 0.000881, 0, 0, 0}, + { 0.001693, 0, 0, 0}, + { -0.001555, 0, 0, 0}, + { 0.001279, 0, 0, 0}, + { -0.000605, 0, 0, 0}, + { 0.001065, 0, 0, 0}, + { -0.001386, 0, 0, 0}, + { 0.001356, 0, 0, 0}, + { 0.000513, 0, 0, 0}, + { 0.001218, 0, 0, 0}, + { -0.001601, 0, 0, 0}, + { 0.000590, 0, 0, 0}, + { -0.000283, 0, 0, 0}, + { 0.001111, 0, 0, 0}, + { -0.001417, 0, 0, 0}, + { 0.001448, 0, 0, 0}, + { -0.000544, 0, 0, 0}, + { 0.000130, 0, 0, 0}, + { -0.001203, 0, 0, 0}, + { 0.000437, 0, 0, 0}, + { -0.001049, 0, 0, 0}, + { 0.000620, 0, 0, 0}, + { -0.000483, 0, 0, 0}, + { 0.001877, 0, 0, 0}, + { -0.001095, 0, 0, 0}, + { -0.000100, 0, 0, 0}, + { -0.000528, 0, 0, 0}, + { 0.001432, 0, 0, 0}, + { -0.001938, 0, 0, 0}, + { -0.000697, 0, 0, 0}, + { 0.000038, 0, 0, 0}, + { 0.000712, 0, 0, 0}, + { -0.001310, 0, 0, 0}, + { 0.001095, 0, 0, 0}, + { -0.000299, 0, 0, 0}, + { 0.001754, 0, 0, 0}, + { -0.001677, 0, 0, 0}, + { 0.001478, 0, 0, 0}, + { -0.000038, 0, 0, 0}, + { 0.000789, 0, 0, 0}, + { -0.001831, 0, 0, 0}, + { 0.000299, 0, 0, 0}, + { 0.000988, 0, 0, 0}, + { -0.001172, 0, 0, 0}, + { 0.000176, 0, 0, 0}, + { 0.001647, 0, 0, 0}, + { -0.001585, 0, 0, 0}, + { 0.000345, 0, 0, 0}, + { 0.001861, 0, 0, 0}, + { -0.001769, 0, 0, 0}, + { -0.000866, 0, 0, 0}, + { 0.000896, 0, 0, 0}, + { 0.000161, 0, 0, 0}, + { -0.000927, 0, 0, 0}, + { -0.001524, 0, 0, 0}, + { -0.000651, 0, 0, 0}, + { 0.001294, 0, 0, 0}, + { -0.000804, 0, 0, 0}, + { -0.001463, 0, 0, 0}, + { 0.001800, 0, 0, 0}, + { -0.000850, 0, 0, 0}, + { 0.000850, 0, 0, 0}, + { -0.000452, 0, 0, 0}, + { -0.001065, 0, 0, 0}, + { -0.000146, 0, 0, 0}, + { 0.000237, 0, 0, 0}, + { 0.001417, 0, 0, 0}, + { -0.000590, 0, 0, 0}, + { -0.000191, 0, 0, 0}, + { 0.001601, 0, 0, 0}, + { 0.001019, 0, 0, 0}, + { 0.000406, 0, 0, 0}, + { -0.000207, 0, 0, 0}, + { 0.001585, 0, 0, 0}, + { 0.000651, 0, 0, 0}, + { -0.000069, 0, 0, 0}, + { 0.000421, 0, 0, 0}, + { -0.001647, 0, 0, 0}, + { 0.001371, 0, 0, 0}, + { 0.000927, 0, 0, 0}, + { -0.000666, 0, 0, 0}, + { 0.001187, 0, 0, 0}, + { -0.001448, 0, 0, 0}, + { 0.000574, 0, 0, 0}, + { -0.001892, 0, 0, 0}, + { 0.000758, 0, 0, 0}, + { -0.001294, 0, 0, 0}, + { 0.001922, 0, 0, 0}, + { -0.001662, 0, 0, 0}, + { -0.001034, 0, 0, 0}, + { -0.000498, 0, 0, 0}, + { -0.001861, 0, 0, 0}, + { 0.001203, 0, 0, 0}, + { -0.000329, 0, 0, 0}, + { -0.001371, 0, 0, 0}, + { 0.001631, 0, 0, 0}, + { -0.001846, 0, 0, 0}, + { 0.000728, 0, 0, 0}, + { -0.000911, 0, 0, 0}, + { 0.001815, 0, 0, 0}, + { -0.001141, 0, 0, 0}, + { -0.000375, 0, 0, 0}, + { 0.000100, 0, 0, 0}, + { -0.000743, 0, 0, 0}, + { 0.001172, 0, 0, 0}, + { 0.000069, 0, 0, 0}, + { 0.001494, 0, 0, 0}, + { 0.000973, 0, 0, 0}, + { -0.000957, 0, 0, 0}, + { 0.001938, 0, 0, 0}, + { 0.000528, 0, 0, 0}, + { 0.000054, 0, 0, 0}, + { -0.001248, 0, 0, 0}, + { -0.000268, 0, 0, 0}, + { 0.001540, 0, 0, 0}, + { -0.000008, 0, 0, 0}, + { 0.000314, 0, 0, 0}, + { 0.001340, 0, 0, 0}, + { -0.001754, 0, 0, 0}, + { 0.000498, 0, 0, 0}, + { -0.001187, 0, 0, 0}, + { 0.000774, 0, 0, 0}, + { -0.001340, 0, 0, 0}, + { 0.000268, 0, 0, 0}, + { -0.001478, 0, 0, 0}, + { -0.000130, 0, 0, 0}, + { -0.000774, 0, 0, 0}, + { 0.001310, 0, 0, 0}, + { 0.000391, 0, 0, 0}, + { 0.000957, 0, 0, 0}, + { -0.000467, 0, 0, 0}, + { -0.001540, 0, 0, 0}, + { 0.001034, 0, 0, 0}, + { -0.000682, 0, 0, 0}, + { 0.001677, 0, 0, 0}, + { 0.001003, 0, 0, 0}, + { -0.000421, 0, 0, 0}, + { 0.001785, 0, 0, 0}, + { -0.000237, 0, 0, 0}, + { -0.000620, 0, 0, 0}, + { 0.001662, 0, 0, 0}, + { 0.000835, 0, 0, 0}, + { -0.001723, 0, 0, 0}, + { -0.001080, 0, 0, 0}, + { 0.001769, 0, 0, 0}, + { -0.000789, 0, 0, 0}, + { -0.001785, 0, 0, 0}, + { 0.000682, 0, 0, 0}, + { -0.000988, 0, 0, 0}, + { -0.001325, 0, 0, 0}, + { -0.000176, 0, 0, 0}, + { -0.001509, 0, 0, 0}, + { 0.000329, 0, 0, 0}, + { -0.001953, 0, 0, 0}, + { 0.000666, 0, 0, 0}, + { -0.001616, 0, 0, 0}, + { 0.001157, 0, 0, 0}, + { 0.000452, 0, 0, 0}, + { -0.000437, 0, 0, 0}, + { 0.000191, 0, 0, 0}, + { -0.001494, 0, 0, 0}, + { 0.001141, 0, 0, 0}, + { 0.000084, 0, 0, 0}, + { 0.001892, 0, 0, 0}, + { 0.001402, 0, 0, 0}, + { 0.000559, 0, 0, 0}, + { 0.000115, 0, 0, 0}, + { 0.001264, 0, 0, 0}, + { -0.000574, 0, 0, 0}, + { -0.000973, 0, 0, 0}, + { 0.001325, 0, 0, 0}, + { 0.000222, 0, 0, 0}, + { -0.000758, 0, 0, 0}, + { -0.001356, 0, 0, 0}, + { 0.001463, 0, 0, 0}, + { 0.000866, 0, 0, 0}, + { -0.000360, 0, 0, 0}, + { 0.000544, 0, 0, 0}, + { -0.001126, 0, 0, 0}, + { -0.000253, 0, 0, 0}, + { -0.000559, 0, 0, 0}, + { -0.001815, 0, 0, 0}, + { 0.001723, 0, 0, 0}, + { -0.001157, 0, 0, 0}, + { 0.000743, 0, 0, 0}, + { 0.001570, 0, 0, 0}, + { -0.000115, 0, 0, 0}, + { -0.001218, 0, 0, 0}, + { 0.001831, 0, 0, 0}, + { 0.000023, 0, 0, 0}, + { -0.001922, 0, 0, 0}, + { 0.001739, 0, 0, 0}, + { -0.000712, 0, 0, 0}, + { 0.001555, 0, 0, 0}, + { -0.001708, 0, 0, 0}, + { 0.001233, 0, 0, 0}, + { 0.000207, 0, 0, 0}, + { 0.001049, 0, 0, 0}, + { -0.000728, 0, 0, 0}, + { -0.001631, 0, 0, 0}, + { -0.000314, 0, 0, 0}, + { 0.000483, 0, 0, 0}, + { -0.001800, 0, 0, 0}, + { 0.000942, 0, 0, 0}, + { -0.000345, 0, 0, 0}, + { 0.000697, 0, 0, 0}, + { -0.001019, 0, 0, 0}, + { -0.001570, 0, 0, 0}, + { -0.000023, 0, 0, 0}, + { -0.001279, 0, 0, 0}, + { 0.000804, 0, 0, 0}, + { -0.000896, 0, 0, 0}, + { -0.001432, 0, 0, 0}, + { 0.000605, 0, 0, 0}, + { -0.000084, 0, 0, 0}, + { 0.000911, 0, 0, 0}, + { 0.001953, 0, 0, 0}, + { -0.001402, 0, 0, 0}, + { -0.000636, 0, 0, 0}, + { 0.001509, 0, 0, 0}, + { -0.000820, 0, 0, 0}, + { 0.001248, 0, 0, 0}, + { 0.000253, 0, 0, 0}, + { 0.001524, 0, 0, 0}, + { 0.001126, 0, 0, 0}, + { 0.000360, 0, 0, 0}, + { -0.000391, 0, 0, 0}, + { 0.001907, 0, 0, 0}, + { 0.001386, 0, 0, 0}, + { -0.001111, 0, 0, 0}, + { 0.001616, 0, 0, 0}, + { -0.000881, 0, 0, 0}, + { 0.000146, 0, 0, 0}, + { 0.001080, 0, 0, 0}, + { -0.000054, 0, 0, 0}, + { 0.000283, 0, 0, 0}, + { -0.001693, 0, 0, 0}, + { -0.001264, 0, 0, 0}, + { -0.000513, 0, 0, 0}, + { -0.000161, 0, 0, 0}, + { -0.001877, 0, 0, 0}, + { -0.000835, 0, 0, 0}, + { 0.000636, 0, 0, 0}, + { 0.000008, 0, 0, 0}, + { -0.001907, 0, 0, 0}, + { -0.000222, 0, 0, 0}, + { 0.000375, 0, 0, 0}, + { -0.001739, 0, 0, 0}, + { -0.000406, 0, 0, 0}, + { -0.001233, 0, 0, 0}, + { 0.001708, 0, 0, 0}, + { -0.000942, 0, 0, 0}, + { 0.000820, 0, 0, 0}, + { 0.001846, 0, 0, 0}, + { 0.000467, 0, 0, 0} } +dcl_constantbuffer CB0[0:0][2], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_input_ps_siv linear noperspective v0.xy, position +dcl_output o0.xyzw +dcl_temps 14 +ftoi r0.xy, v0.xyxx +iadd r0.xy, r0.xyxx, -CB0[0][0].xyxx +mad r0.zw, CB0[0][0].zzzw, l(0.000000, 0.000000, 0.500000, 0.500000), l(0.000000, 0.000000, -0.500000, -0.500000) +utof r1.xy, r0.xyxx +mad r0.zw, r1.xxxy, CB0[0][0].zzzw, r0.zzzw +round_ni r1.xy, r0.zwzz +add r0.zw, r0.zzzw, -r1.xxxy +ftoi r1.xy, r1.xyxx +iadd r2.xyzw, r1.xyxy, l(-1, 0, 0, -1) +mov r3.xy, r2.zwzz +mov r3.zw, l(0,0,0,0) +ld r3.xyz, r3.xyzw, T0[0].xyzw +mov r2.zw, l(0,0,0,0) +ld r2.xyz, r2.xyzw, T0[0].xyzw +mov r1.zw, l(0,0,0,0) +ld r4.xyz, r1.xyzw, T0[0].xyzw +iadd r5.xyzw, r1.xyxy, l(1, 0, 1, -1) +mov r6.xy, r5.zwzz +mov r6.zw, l(0,0,0,0) +ld r6.xyz, r6.xyzw, T0[0].xyzw +mov r5.zw, l(0,0,0,0) +ld r5.xyz, r5.xyzw, T0[0].xyzw +iadd r7.xyzw, r1.xyxy, l(-1, 1, 2, 0) +mov r8.xy, r7.zwzz +mov r8.zw, l(0,0,0,0) +ld r8.xyz, r8.xyzw, T0[0].xyzw +mov r7.zw, l(0,0,0,0) +ld r7.xyz, r7.xyzw, T0[0].xyzw +iadd r9.xyzw, r1.xyxy, l(0, 2, 0, 1) +mov r10.xy, r9.zwzz +mov r10.zw, l(0,0,0,0) +ld r10.xyz, r10.xyzw, T0[0].xyzw +mov r9.zw, l(0,0,0,0) +ld r9.xyz, r9.xyzw, T0[0].xyzw +iadd r11.xyzw, r1.xyxy, l(2, 1, 1, 1) +mov r12.xy, r11.zwzz +mov r12.zw, l(0,0,0,0) +ld r12.xyz, r12.xyzw, T0[0].xyzw +mov r11.zw, l(0,0,0,0) +ld r11.xyz, r11.xyzw, T0[0].xyzw +iadd r1.xy, r1.xyxx, l(1, 2, 0, 0) +mov r1.zw, l(0,0,0,0) +ld r1.xyz, r1.xyzw, T0[0].xyzw +mul r3.xyz, r3.xyzx, r3.xyzx +mul r6.xyz, r6.xyzx, r6.xyzx +mul r2.xyz, r2.xyzx, r2.xyzx +mul r4.xyz, r4.xyzx, r4.xyzx +mul r5.xyz, r5.xyzx, r5.xyzx +mul r8.xyz, r8.xyzx, r8.xyzx +mul r7.xyz, r7.xyzx, r7.xyzx +mul r10.xyz, r10.xyzx, r10.xyzx +mul r12.xyz, r12.xyzx, r12.xyzx +mul r11.xyz, r11.xyzx, r11.xyzx +mul r9.xyz, r9.xyzx, r9.xyzx +mul r1.xyz, r1.xyzx, r1.xyzx +min r1.w, r2.y, r4.y +min r1.w, r1.w, r3.y +min r2.w, r5.y, r10.y +min r1.w, r1.w, r2.w +max r2.w, r2.y, r4.y +max r2.w, r2.w, r3.y +max r3.w, r5.y, r10.y +max r2.w, r2.w, r3.w +min r3.w, r4.y, r5.y +min r3.w, r3.w, r6.y +min r4.w, r8.y, r12.y +min r3.w, r3.w, r4.w +max r4.w, r4.y, r5.y +max r4.w, r4.w, r6.y +max r5.w, r8.y, r12.y +max r4.w, r4.w, r5.w +min r5.w, r7.y, r10.y +min r5.w, r4.y, r5.w +min r6.w, r9.y, r12.y +min r5.w, r5.w, r6.w +max r6.w, r7.y, r10.y +max r6.w, r4.y, r6.w +max r7.w, r9.y, r12.y +max r6.w, r6.w, r7.w +min r7.w, r10.y, r12.y +min r7.w, r5.y, r7.w +min r8.w, r1.y, r11.y +min r7.w, r7.w, r8.w +max r8.w, r10.y, r12.y +max r8.w, r5.y, r8.w +max r9.w, r1.y, r11.y +max r8.w, r8.w, r9.w +iadd r9.w, -r2.w, l(0x7ef07ebb) +iadd r10.w, -r4.w, l(0x7ef07ebb) +iadd r11.w, -r6.w, l(0x7ef07ebb) +iadd r12.w, -r8.w, l(0x7ef07ebb) +add r13.x, -r2.w, l(1.000000) +min r13.x, r1.w, r13.x +mul_sat r9.w, r9.w, r13.x +add r13.x, -r4.w, l(1.000000) +min r13.x, r3.w, r13.x +mul_sat r10.w, r10.w, r13.x +add r13.x, -r6.w, l(1.000000) +min r13.x, r5.w, r13.x +mul_sat r11.w, r11.w, r13.x +add r13.x, -r8.w, l(1.000000) +min r13.x, r7.w, r13.x +mul_sat r12.w, r12.w, r13.x +ushr r9.w, r9.w, l(1) +iadd r9.w, r9.w, l(0x1fbc4639) +ushr r10.w, r10.w, l(1) +iadd r10.w, r10.w, l(0x1fbc4639) +ushr r11.w, r11.w, l(1) +iadd r11.w, r11.w, l(0x1fbc4639) +ushr r12.w, r12.w, l(1) +iadd r12.w, r12.w, l(0x1fbc4639) +mul r9.w, r9.w, CB0[0][1].x +mul r10.w, r10.w, CB0[0][1].x +mul r11.w, r11.w, CB0[0][1].x +mul r12.w, r12.w, CB0[0][1].x +add r13.xy, -r0.wzww, l(1.000000, 1.000000, 0.000000, 0.000000) +mul r13.z, r13.x, r13.y +mul r13.xy, r0.zwzz, r13.xyxx +mul r0.z, r0.w, r0.z +add r0.w, -r1.w, r2.w +add r0.w, r0.w, l(0.031250) +iadd r0.w, -r0.w, l(0x7ef07ebb) +mul r1.w, r0.w, r13.z +add r2.w, -r3.w, r4.w +add r2.w, r2.w, l(0.031250) +iadd r2.w, -r2.w, l(0x7ef07ebb) +mul r3.w, r2.w, r13.x +add r4.w, -r5.w, r6.w +add r4.w, r4.w, l(0.031250) +iadd r4.w, -r4.w, l(0x7ef07ebb) +mul r5.w, r4.w, r13.y +add r6.w, -r7.w, r8.w +add r6.w, r6.w, l(0.031250) +iadd r6.w, -r6.w, l(0x7ef07ebb) +mul r7.w, r0.z, r6.w +mul r8.w, r1.w, r9.w +mul r13.w, r3.w, r10.w +mul r5.w, r5.w, r11.w +mad r3.w, r10.w, r3.w, r5.w +mad r0.w, r13.z, r0.w, r3.w +mul r7.w, r7.w, r12.w +mad r1.w, r9.w, r1.w, r7.w +mad r2.w, r13.x, r2.w, r1.w +mad r1.w, r13.y, r4.w, r1.w +mad r0.z, r0.z, r6.w, r3.w +add r3.w, r13.w, r13.w +mad r3.w, r8.w, l(2.000000), r3.w +mad r3.w, r5.w, l(2.000000), r3.w +mad r3.w, r7.w, l(2.000000), r3.w +add r3.w, r0.w, r3.w +add r3.w, r2.w, r3.w +add r3.w, r1.w, r3.w +add r3.w, r0.z, r3.w +iadd r4.w, -r3.w, l(0x7ef19fff) +mad r3.w, -r4.w, r3.w, l(2.000000) +mul r3.w, r3.w, r4.w +mul r2.xyz, r2.xyzx, r8.wwww +mad r2.xyz, r3.xyzx, r8.wwww, r2.xyzx +mad r2.xyz, r6.xyzx, r13.wwww, r2.xyzx +mad r2.xyz, r8.xyzx, r13.wwww, r2.xyzx +mad r2.xyz, r7.xyzx, r5.wwww, r2.xyzx +mad r2.xyz, r9.xyzx, r5.wwww, r2.xyzx +mad r2.xyz, r11.xyzx, r7.wwww, r2.xyzx +mad r1.xyz, r1.xyzx, r7.wwww, r2.xyzx +mad r1.xyz, r4.xyzx, r0.wwww, r1.xyzx +mad r1.xyz, r5.xyzx, r2.wwww, r1.xyzx +mad r1.xyz, r10.xyzx, r1.wwww, r1.xyzx +mad r1.xyz, r12.xyzx, r0.zzzz, r1.xyzx +mul_sat r1.xyz, r3.wwww, r1.xyzx +sqrt r1.xyz, r1.xyzx +bfi r0.y, l(4), l(4), r0.y, l(0) +bfi r0.x, l(4), l(0), r0.x, r0.y +add o0.xyz, r1.xyzx, icb[r0.x + 0].xxxx +mov o0.w, l(1.000000) +ret +// Approximately 175 instruction slots used +#endif + +const BYTE guest_output_ffx_cas_resample_dither_ps[] = +{ + 68, 88, 66, 67, 162, 124, + 243, 114, 220, 254, 28, 54, + 81, 63, 203, 49, 205, 242, + 53, 186, 1, 0, 0, 0, + 220, 40, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 120, 2, 0, 0, 172, 2, + 0, 0, 224, 2, 0, 0, + 64, 40, 0, 0, 82, 68, + 69, 70, 60, 2, 0, 0, + 1, 0, 0, 0, 176, 0, + 0, 0, 2, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 255, 255, 0, 5, 0, 0, + 20, 2, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 140, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 120, 101, 95, 116, + 101, 120, 116, 117, 114, 101, + 0, 88, 101, 67, 97, 115, + 82, 101, 115, 97, 109, 112, + 108, 101, 67, 111, 110, 115, + 116, 97, 110, 116, 115, 0, + 171, 171, 151, 0, 0, 0, + 3, 0, 0, 0, 200, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 64, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 92, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 128, 1, 0, 0, 8, 0, + 0, 0, 8, 0, 0, 0, + 2, 0, 0, 0, 168, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 204, 1, + 0, 0, 16, 0, 0, 0, + 4, 0, 0, 0, 2, 0, + 0, 0, 240, 1, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 120, 101, 95, 99, + 97, 115, 95, 111, 117, 116, + 112, 117, 116, 95, 111, 102, + 102, 115, 101, 116, 0, 105, + 110, 116, 50, 0, 171, 171, + 1, 0, 2, 0, 1, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 85, 1, 0, 0, + 120, 101, 95, 99, 97, 115, + 95, 105, 110, 112, 117, 116, + 95, 111, 117, 116, 112, 117, + 116, 95, 115, 105, 122, 101, + 95, 114, 97, 116, 105, 111, + 0, 102, 108, 111, 97, 116, + 50, 0, 171, 171, 1, 0, + 3, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 159, 1, 0, 0, 120, 101, + 95, 99, 97, 115, 95, 115, + 104, 97, 114, 112, 110, 101, + 115, 115, 95, 112, 111, 115, + 116, 95, 115, 101, 116, 117, + 112, 0, 102, 108, 111, 97, + 116, 0, 171, 171, 0, 0, + 3, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 232, 1, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 3, 0, 0, + 83, 86, 95, 80, 111, 115, + 105, 116, 105, 111, 110, 0, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 84, 97, 114, 103, 101, + 116, 0, 171, 171, 83, 72, + 69, 88, 88, 37, 0, 0, + 81, 0, 0, 0, 86, 9, + 0, 0, 106, 8, 0, 1, + 53, 24, 0, 0, 2, 4, + 0, 0, 132, 131, 131, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 231, 230, 102, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 222, 221, + 221, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 204, 203, 203, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 168, 167, 167, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 159, 158, + 30, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 140, 139, 139, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 182, 181, 181, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 178, 177, + 177, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 135, 134, 6, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 160, 159, 159, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 210, 209, + 209, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 155, 154, 26, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 149, 148, 148, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 146, 145, + 145, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 186, 185, 185, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 190, 189, 189, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 143, 142, + 14, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 137, 136, 8, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 158, 157, 157, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 229, 228, + 228, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 138, 137, 137, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 163, 162, 34, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 253, 252, + 252, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 246, 245, 245, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 144, 143, 143, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 209, 208, + 208, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 139, 138, 10, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 188, 187, 187, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 254, 253, + 253, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 183, 182, 54, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 161, 160, 32, 56, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 187, 186, + 58, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 172, 171, 171, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 144, 143, 143, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 157, 156, + 156, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 230, 229, 229, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 220, 219, 219, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 194, 193, + 193, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 161, 160, 32, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 207, 206, 78, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 240, 239, + 239, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 157, 156, 156, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 130, 129, 129, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 154, 153, + 153, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 185, 184, 56, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 216, 215, 215, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 207, + 207, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 181, 180, 180, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 244, 243, 243, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 232, 231, + 231, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 227, 226, 98, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 235, 234, 106, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 169, 168, + 40, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 243, 242, 114, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 200, 199, 199, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 171, 170, + 42, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 170, 169, 169, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 211, 210, 82, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 192, 191, + 191, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 236, 235, 235, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 223, 222, 94, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 223, 222, + 94, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 237, 236, 236, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 140, 139, 139, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 153, 152, + 24, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 249, 248, 120, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 186, 185, 185, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 155, 154, + 26, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 201, 200, 72, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 210, 209, 209, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 134, 133, + 133, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 213, 212, 212, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 217, 216, 88, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 207, + 207, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 171, 170, 42, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 145, 144, 144, 184, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 221, 220, + 220, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 216, 215, 215, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 180, 179, 179, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 243, 242, + 114, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 175, 174, 46, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 156, 155, 155, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 190, 189, + 189, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 151, 150, 22, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 248, 247, 247, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 199, 198, + 70, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 170, 169, 169, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 252, 251, 251, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 218, 217, + 217, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 136, 135, 135, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 131, 130, 2, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 244, 243, + 243, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 158, 157, 157, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 173, 172, 172, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 180, 179, + 179, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 214, 213, 213, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 242, 241, 241, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 191, 190, + 62, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 239, 238, 110, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 238, 237, 237, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 150, 149, + 149, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 197, 196, 196, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 209, 208, 208, 56, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 195, 194, + 66, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 154, 153, 153, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 145, 144, 144, 56, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 195, + 195, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 254, 126, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 251, 250, 122, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 254, 253, + 253, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 139, 138, 10, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 225, 224, 96, 56, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 164, 163, + 163, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 141, 140, 140, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 202, 201, 201, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 129, 128, + 0, 183, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 165, 164, 164, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 176, 175, 175, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 230, 229, + 229, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 131, 130, 2, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 156, 155, 155, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 203, 202, + 74, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 176, 175, 175, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 141, 140, 140, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 194, 193, + 193, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 137, 136, 8, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 203, 202, 74, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 172, 171, + 171, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 205, 204, 204, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 251, 250, 122, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 245, 244, + 244, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 202, 201, 201, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 136, 135, 135, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 179, 178, + 50, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 220, 219, 219, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 132, 131, 131, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 221, 220, + 220, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 234, 233, 233, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 249, 248, 120, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 163, 162, + 34, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 218, 217, 217, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 219, 218, 90, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 226, 225, + 225, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 142, 141, 141, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 232, 231, 231, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 207, 206, + 78, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 234, 233, 233, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 179, 178, 50, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 130, 129, + 129, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 174, 173, 173, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 185, 184, 56, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 197, + 197, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 173, 172, 172, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 187, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 175, 174, + 46, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 212, 211, 211, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 152, 151, 151, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 236, + 236, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 229, 228, 228, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 201, 200, 72, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 195, + 195, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 150, 149, 149, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 177, 176, 176, 56, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 248, 247, + 247, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 184, 183, 183, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 147, 146, 18, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 241, 240, + 240, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 166, 165, 165, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 151, 150, 22, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 254, + 126, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 174, 173, 173, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 233, 232, 104, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 199, 198, + 70, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 178, 177, 177, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 192, 191, 191, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 227, 226, + 98, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 189, 188, 188, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 143, 142, 14, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 148, 147, + 147, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 133, 132, 132, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 147, 146, 18, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 238, 237, + 237, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 226, 225, 225, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 152, 151, 151, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 195, 194, + 66, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 206, 205, 205, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 241, 240, 240, 184, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 160, 159, + 159, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 240, 239, 239, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 193, 192, 192, 55, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 252, 251, + 251, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 228, 227, 227, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 187, 186, 58, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 204, 203, + 203, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 224, 223, 223, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 162, 161, 161, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 217, 216, + 88, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 138, 137, 137, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 191, 190, 62, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 214, 213, + 213, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 165, 164, 164, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 253, 252, 252, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 236, 235, + 235, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 247, 246, 118, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 181, 180, 180, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 183, 182, + 54, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 134, 133, 133, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 206, 205, 205, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 193, 192, + 192, 183, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 168, 167, 167, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 211, 210, 82, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 235, 234, + 106, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 188, 187, 187, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 159, 158, 30, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 177, 176, + 176, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 239, 238, 110, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 59, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 184, 183, + 183, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 167, 166, 38, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 198, 197, 197, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 215, 214, + 86, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 164, 163, 163, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 133, 132, 132, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 200, 199, + 199, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 148, 147, 147, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 189, 188, 188, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 205, 204, + 204, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 250, 249, 249, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 182, 181, 181, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 146, 145, + 145, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 212, 211, 211, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 231, 230, 102, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 153, 152, + 24, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 142, 141, 141, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 225, 224, 96, 184, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 149, 148, + 148, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 222, 221, 221, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 166, 165, 165, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 135, 134, + 6, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 169, 168, 40, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 246, 245, 245, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 219, 218, + 90, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 167, 166, 38, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 129, 128, 0, 55, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 250, 249, + 249, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 233, 232, 104, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 197, 196, 196, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 227, + 227, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 213, 212, 212, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 162, 161, 161, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 224, 223, + 223, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 247, 246, 118, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 215, 214, 86, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 242, 241, + 241, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 245, 244, 244, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 100, 32, 0, 4, + 50, 16, 16, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 101, 0, 0, 3, 242, 32, + 16, 0, 0, 0, 0, 0, + 104, 0, 0, 2, 14, 0, + 0, 0, 27, 0, 0, 5, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 16, 16, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 70, 128, 48, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 50, 0, 0, 17, + 194, 0, 16, 0, 0, 0, + 0, 0, 166, 142, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 191, + 0, 0, 0, 191, 86, 0, + 0, 5, 50, 0, 16, 0, + 1, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 11, 194, 0, + 16, 0, 0, 0, 0, 0, + 6, 4, 16, 0, 1, 0, + 0, 0, 166, 142, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 166, 14, 16, 0, 0, 0, + 0, 0, 65, 0, 0, 5, + 50, 0, 16, 0, 1, 0, + 0, 0, 230, 10, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 8, 194, 0, 16, 0, + 0, 0, 0, 0, 166, 14, + 16, 0, 0, 0, 0, 0, + 6, 4, 16, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 27, 0, 0, 5, 50, 0, + 16, 0, 1, 0, 0, 0, + 70, 0, 16, 0, 1, 0, + 0, 0, 30, 0, 0, 10, + 242, 0, 16, 0, 2, 0, + 0, 0, 70, 4, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 54, 0, 0, 5, 50, 0, + 16, 0, 3, 0, 0, 0, + 230, 10, 16, 0, 2, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 3, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 3, 0, 0, 0, 70, 14, + 16, 0, 3, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 2, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 14, 16, 0, + 2, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 30, 0, 0, 10, + 242, 0, 16, 0, 5, 0, + 0, 0, 70, 4, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 255, 255, 255, 255, + 54, 0, 0, 5, 50, 0, + 16, 0, 6, 0, 0, 0, + 230, 10, 16, 0, 5, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 6, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 6, 0, 0, 0, 70, 14, + 16, 0, 6, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 5, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 14, 16, 0, + 5, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 242, 0, 16, 0, + 7, 0, 0, 0, 70, 4, + 16, 0, 1, 0, 0, 0, + 2, 64, 0, 0, 255, 255, + 255, 255, 1, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 50, 0, 16, 0, 8, 0, + 0, 0, 230, 10, 16, 0, + 7, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 8, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 8, 0, 0, 0, + 70, 14, 16, 0, 8, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 7, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 7, 0, 0, 0, 70, 14, + 16, 0, 7, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 242, 0, + 16, 0, 9, 0, 0, 0, + 70, 4, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 54, 0, + 0, 5, 50, 0, 16, 0, + 10, 0, 0, 0, 230, 10, + 16, 0, 9, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 10, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 10, 0, + 0, 0, 70, 14, 16, 0, + 10, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 9, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 9, 0, 0, 0, + 70, 14, 16, 0, 9, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 30, 0, 0, 10, + 242, 0, 16, 0, 11, 0, + 0, 0, 70, 4, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 54, 0, 0, 5, 50, 0, + 16, 0, 12, 0, 0, 0, + 230, 10, 16, 0, 11, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 12, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 12, 0, 0, 0, 70, 14, + 16, 0, 12, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 11, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 11, 0, + 0, 0, 70, 14, 16, 0, + 11, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 50, 0, 16, 0, + 1, 0, 0, 0, 70, 0, + 16, 0, 1, 0, 0, 0, + 2, 64, 0, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 6, 0, 0, 0, 70, 2, + 16, 0, 6, 0, 0, 0, + 70, 2, 16, 0, 6, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 5, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 8, 0, + 0, 0, 70, 2, 16, 0, + 8, 0, 0, 0, 70, 2, + 16, 0, 8, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 7, 0, 0, 0, + 70, 2, 16, 0, 7, 0, + 0, 0, 70, 2, 16, 0, + 7, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 10, 0, 0, 0, 70, 2, + 16, 0, 10, 0, 0, 0, + 70, 2, 16, 0, 10, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 12, 0, + 0, 0, 70, 2, 16, 0, + 12, 0, 0, 0, 70, 2, + 16, 0, 12, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 11, 0, 0, 0, + 70, 2, 16, 0, 11, 0, + 0, 0, 70, 2, 16, 0, + 11, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 9, 0, 0, 0, 70, 2, + 16, 0, 9, 0, 0, 0, + 70, 2, 16, 0, 9, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 1, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 1, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 26, 0, 16, 0, + 4, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 26, 0, 16, 0, + 5, 0, 0, 0, 26, 0, + 16, 0, 10, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 26, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 3, 0, 0, 0, + 26, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 0, + 10, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 3, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 26, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 5, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 3, 0, + 0, 0, 26, 0, 16, 0, + 6, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 8, 0, 0, 0, + 26, 0, 16, 0, 12, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 4, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 5, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 6, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 0, + 8, 0, 0, 0, 26, 0, + 16, 0, 12, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 5, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 5, 0, 0, 0, 26, 0, + 16, 0, 7, 0, 0, 0, + 26, 0, 16, 0, 10, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 5, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 6, 0, 0, 0, + 26, 0, 16, 0, 9, 0, + 0, 0, 26, 0, 16, 0, + 12, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 0, 5, 0, 0, 0, + 58, 0, 16, 0, 6, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 6, 0, + 0, 0, 26, 0, 16, 0, + 7, 0, 0, 0, 26, 0, + 16, 0, 10, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 6, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 6, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 7, 0, 0, 0, 26, 0, + 16, 0, 9, 0, 0, 0, + 26, 0, 16, 0, 12, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 6, 0, + 0, 0, 58, 0, 16, 0, + 6, 0, 0, 0, 58, 0, + 16, 0, 7, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 7, 0, 0, 0, + 26, 0, 16, 0, 10, 0, + 0, 0, 26, 0, 16, 0, + 12, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 7, 0, 0, 0, 26, 0, + 16, 0, 5, 0, 0, 0, + 58, 0, 16, 0, 7, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 8, 0, + 0, 0, 26, 0, 16, 0, + 1, 0, 0, 0, 26, 0, + 16, 0, 11, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 7, 0, 0, 0, + 58, 0, 16, 0, 7, 0, + 0, 0, 58, 0, 16, 0, + 8, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 8, 0, 0, 0, 26, 0, + 16, 0, 10, 0, 0, 0, + 26, 0, 16, 0, 12, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 8, 0, + 0, 0, 26, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 0, 8, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 9, 0, 0, 0, + 26, 0, 16, 0, 1, 0, + 0, 0, 26, 0, 16, 0, + 11, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 8, 0, 0, 0, 58, 0, + 16, 0, 8, 0, 0, 0, + 58, 0, 16, 0, 9, 0, + 0, 0, 30, 0, 0, 8, + 130, 0, 16, 0, 9, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 2, 0, + 0, 0, 1, 64, 0, 0, + 187, 126, 240, 126, 30, 0, + 0, 8, 130, 0, 16, 0, + 10, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 187, 126, 240, 126, + 30, 0, 0, 8, 130, 0, + 16, 0, 11, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 6, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 30, 0, 0, 8, + 130, 0, 16, 0, 12, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 8, 0, + 0, 0, 1, 64, 0, 0, + 187, 126, 240, 126, 0, 0, + 0, 8, 18, 0, 16, 0, + 13, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 51, 0, 0, 7, 18, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 13, 0, 0, 0, 56, 32, + 0, 7, 130, 0, 16, 0, + 9, 0, 0, 0, 58, 0, + 16, 0, 9, 0, 0, 0, + 10, 0, 16, 0, 13, 0, + 0, 0, 0, 0, 0, 8, + 18, 0, 16, 0, 13, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 4, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 51, 0, + 0, 7, 18, 0, 16, 0, + 13, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 10, 0, 16, 0, 13, 0, + 0, 0, 56, 32, 0, 7, + 130, 0, 16, 0, 10, 0, + 0, 0, 58, 0, 16, 0, + 10, 0, 0, 0, 10, 0, + 16, 0, 13, 0, 0, 0, + 0, 0, 0, 8, 18, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 6, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 51, 0, 0, 7, + 18, 0, 16, 0, 13, 0, + 0, 0, 58, 0, 16, 0, + 5, 0, 0, 0, 10, 0, + 16, 0, 13, 0, 0, 0, + 56, 32, 0, 7, 130, 0, + 16, 0, 11, 0, 0, 0, + 58, 0, 16, 0, 11, 0, + 0, 0, 10, 0, 16, 0, + 13, 0, 0, 0, 0, 0, + 0, 8, 18, 0, 16, 0, + 13, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 8, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 51, 0, 0, 7, 18, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 0, 7, 0, + 0, 0, 10, 0, 16, 0, + 13, 0, 0, 0, 56, 32, + 0, 7, 130, 0, 16, 0, + 12, 0, 0, 0, 58, 0, + 16, 0, 12, 0, 0, 0, + 10, 0, 16, 0, 13, 0, + 0, 0, 85, 0, 0, 7, + 130, 0, 16, 0, 9, 0, + 0, 0, 58, 0, 16, 0, + 9, 0, 0, 0, 1, 64, + 0, 0, 1, 0, 0, 0, + 30, 0, 0, 7, 130, 0, + 16, 0, 9, 0, 0, 0, + 58, 0, 16, 0, 9, 0, + 0, 0, 1, 64, 0, 0, + 57, 70, 188, 31, 85, 0, + 0, 7, 130, 0, 16, 0, + 10, 0, 0, 0, 58, 0, + 16, 0, 10, 0, 0, 0, + 1, 64, 0, 0, 1, 0, + 0, 0, 30, 0, 0, 7, + 130, 0, 16, 0, 10, 0, + 0, 0, 58, 0, 16, 0, + 10, 0, 0, 0, 1, 64, + 0, 0, 57, 70, 188, 31, + 85, 0, 0, 7, 130, 0, + 16, 0, 11, 0, 0, 0, + 58, 0, 16, 0, 11, 0, + 0, 0, 1, 64, 0, 0, + 1, 0, 0, 0, 30, 0, + 0, 7, 130, 0, 16, 0, + 11, 0, 0, 0, 58, 0, + 16, 0, 11, 0, 0, 0, + 1, 64, 0, 0, 57, 70, + 188, 31, 85, 0, 0, 7, + 130, 0, 16, 0, 12, 0, + 0, 0, 58, 0, 16, 0, + 12, 0, 0, 0, 1, 64, + 0, 0, 1, 0, 0, 0, + 30, 0, 0, 7, 130, 0, + 16, 0, 12, 0, 0, 0, + 58, 0, 16, 0, 12, 0, + 0, 0, 1, 64, 0, 0, + 57, 70, 188, 31, 56, 0, + 0, 9, 130, 0, 16, 0, + 9, 0, 0, 0, 58, 0, + 16, 0, 9, 0, 0, 0, + 10, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 56, 0, + 0, 9, 130, 0, 16, 0, + 10, 0, 0, 0, 58, 0, + 16, 0, 10, 0, 0, 0, + 10, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 56, 0, + 0, 9, 130, 0, 16, 0, + 11, 0, 0, 0, 58, 0, + 16, 0, 11, 0, 0, 0, + 10, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 56, 0, + 0, 9, 130, 0, 16, 0, + 12, 0, 0, 0, 58, 0, + 16, 0, 12, 0, 0, 0, + 10, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 11, 50, 0, 16, 0, + 13, 0, 0, 0, 182, 15, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 66, 0, + 16, 0, 13, 0, 0, 0, + 10, 0, 16, 0, 13, 0, + 0, 0, 26, 0, 16, 0, + 13, 0, 0, 0, 56, 0, + 0, 7, 50, 0, 16, 0, + 13, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 13, 0, + 0, 0, 56, 0, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 61, + 30, 0, 0, 8, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 13, 0, 0, 0, + 0, 0, 0, 8, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 61, + 30, 0, 0, 8, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 2, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 13, 0, 0, 0, + 0, 0, 0, 8, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 5, 0, 0, 0, + 58, 0, 16, 0, 6, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 61, + 30, 0, 0, 8, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 130, 0, 16, 0, 5, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 13, 0, 0, 0, + 0, 0, 0, 8, 130, 0, + 16, 0, 6, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 7, 0, 0, 0, + 58, 0, 16, 0, 8, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 6, 0, + 0, 0, 58, 0, 16, 0, + 6, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 61, + 30, 0, 0, 8, 130, 0, + 16, 0, 6, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 6, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 130, 0, 16, 0, 7, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 6, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 8, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 9, 0, 0, 0, 56, 0, + 0, 7, 130, 0, 16, 0, + 13, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 10, 0, + 0, 0, 56, 0, 0, 7, + 130, 0, 16, 0, 5, 0, + 0, 0, 58, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 0, 11, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 10, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 5, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 13, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 7, 0, 0, 0, + 58, 0, 16, 0, 7, 0, + 0, 0, 58, 0, 16, 0, + 12, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 9, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 7, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 2, 0, 0, 0, 10, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 1, 0, 0, 0, 26, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 6, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 0, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 0, 13, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 8, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 58, 0, 16, 0, 3, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 5, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 58, 0, 16, 0, 3, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 7, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 58, 0, 16, 0, 3, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 0, 0, 0, 7, 130, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 0, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 3, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 30, 0, 0, 8, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 255, 159, + 241, 126, 50, 0, 0, 10, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 56, 0, 0, 7, 130, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 246, 15, 16, 0, 8, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 246, 15, + 16, 0, 8, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 6, 0, 0, 0, 246, 15, + 16, 0, 13, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 8, 0, 0, 0, 246, 15, + 16, 0, 13, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 7, 0, 0, 0, 246, 15, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 9, 0, 0, 0, 246, 15, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 11, 0, 0, 0, 246, 15, + 16, 0, 7, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 246, 15, + 16, 0, 7, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 246, 15, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 10, 0, 0, 0, 246, 15, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 12, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 56, 32, 0, 7, + 114, 0, 16, 0, 1, 0, + 0, 0, 246, 15, 16, 0, + 3, 0, 0, 0, 70, 2, + 16, 0, 1, 0, 0, 0, + 75, 0, 0, 5, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 140, 0, 0, 11, + 34, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 140, 0, + 0, 11, 18, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 114, 32, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 6, 144, 144, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 130, 32, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 62, 0, + 0, 1, 83, 84, 65, 84, + 148, 0, 0, 0, 175, 0, + 0, 0, 14, 0, 0, 0, + 0, 1, 0, 0, 2, 0, + 0, 0, 115, 0, 0, 0, + 20, 0, 0, 0, 4, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 18, 0, 0, 0, + 0, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_ps.h new file mode 100644 index 000000000..1a7634fd6 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_resample_ps.h @@ -0,0 +1,1264 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeCasResampleConstants +// { +// +// int2 xe_cas_output_offset; // Offset: 0 Size: 8 +// float2 xe_cas_input_output_size_ratio;// Offset: 8 Size: 8 +// float xe_cas_sharpness_post_setup; // Offset: 16 Size: 4 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_texture texture float3 2d T0 t0 1 +// XeCasResampleConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][2], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_input_ps_siv linear noperspective v0.xy, position +dcl_output o0.xyzw +dcl_temps 14 +ftoi r0.xy, v0.xyxx +iadd r0.xy, r0.xyxx, -CB0[0][0].xyxx +mad r0.zw, CB0[0][0].zzzw, l(0.000000, 0.000000, 0.500000, 0.500000), l(0.000000, 0.000000, -0.500000, -0.500000) +utof r0.xy, r0.xyxx +mad r0.xy, r0.xyxx, CB0[0][0].zwzz, r0.zwzz +round_ni r0.zw, r0.xxxy +add r0.xy, -r0.zwzz, r0.xyxx +ftoi r1.xy, r0.zwzz +iadd r2.xyzw, r1.xyxy, l(-1, 0, 0, -1) +mov r3.xy, r2.zwzz +mov r3.zw, l(0,0,0,0) +ld r3.xyz, r3.xyzw, T0[0].xyzw +mov r2.zw, l(0,0,0,0) +ld r2.xyz, r2.xyzw, T0[0].xyzw +mov r1.zw, l(0,0,0,0) +ld r4.xyz, r1.xyzw, T0[0].xyzw +iadd r5.xyzw, r1.xyxy, l(1, 0, 1, -1) +mov r6.xy, r5.zwzz +mov r6.zw, l(0,0,0,0) +ld r6.xyz, r6.xyzw, T0[0].xyzw +mov r5.zw, l(0,0,0,0) +ld r5.xyz, r5.xyzw, T0[0].xyzw +iadd r7.xyzw, r1.xyxy, l(-1, 1, 2, 0) +mov r8.xy, r7.zwzz +mov r8.zw, l(0,0,0,0) +ld r8.xyz, r8.xyzw, T0[0].xyzw +mov r7.zw, l(0,0,0,0) +ld r7.xyz, r7.xyzw, T0[0].xyzw +iadd r9.xyzw, r1.xyxy, l(0, 2, 0, 1) +mov r10.xy, r9.zwzz +mov r10.zw, l(0,0,0,0) +ld r10.xyz, r10.xyzw, T0[0].xyzw +mov r9.zw, l(0,0,0,0) +ld r9.xyz, r9.xyzw, T0[0].xyzw +iadd r11.xyzw, r1.xyxy, l(2, 1, 1, 1) +mov r12.xy, r11.zwzz +mov r12.zw, l(0,0,0,0) +ld r12.xyz, r12.xyzw, T0[0].xyzw +mov r11.zw, l(0,0,0,0) +ld r11.xyz, r11.xyzw, T0[0].xyzw +iadd r1.xy, r1.xyxx, l(1, 2, 0, 0) +mov r1.zw, l(0,0,0,0) +ld r1.xyz, r1.xyzw, T0[0].xyzw +mul r3.xyz, r3.xyzx, r3.xyzx +mul r6.xyz, r6.xyzx, r6.xyzx +mul r2.xyz, r2.xyzx, r2.xyzx +mul r4.xyz, r4.xyzx, r4.xyzx +mul r5.xyz, r5.xyzx, r5.xyzx +mul r8.xyz, r8.xyzx, r8.xyzx +mul r7.xyz, r7.xyzx, r7.xyzx +mul r10.xyz, r10.xyzx, r10.xyzx +mul r12.xyz, r12.xyzx, r12.xyzx +mul r11.xyz, r11.xyzx, r11.xyzx +mul r9.xyz, r9.xyzx, r9.xyzx +mul r1.xyz, r1.xyzx, r1.xyzx +min r0.z, r2.y, r4.y +min r0.z, r0.z, r3.y +min r0.w, r5.y, r10.y +min r0.z, r0.w, r0.z +max r0.w, r2.y, r4.y +max r0.w, r0.w, r3.y +max r1.w, r5.y, r10.y +max r0.w, r0.w, r1.w +min r1.w, r4.y, r5.y +min r1.w, r1.w, r6.y +min r2.w, r8.y, r12.y +min r1.w, r1.w, r2.w +max r2.w, r4.y, r5.y +max r2.w, r2.w, r6.y +max r3.w, r8.y, r12.y +max r2.w, r2.w, r3.w +min r3.w, r7.y, r10.y +min r3.w, r3.w, r4.y +min r4.w, r9.y, r12.y +min r3.w, r3.w, r4.w +max r4.w, r7.y, r10.y +max r4.w, r4.w, r4.y +max r5.w, r9.y, r12.y +max r4.w, r4.w, r5.w +min r5.w, r10.y, r12.y +min r5.w, r5.w, r5.y +min r6.w, r1.y, r11.y +min r5.w, r5.w, r6.w +max r6.w, r10.y, r12.y +max r6.w, r5.y, r6.w +max r7.w, r1.y, r11.y +max r6.w, r6.w, r7.w +iadd r7.w, -r0.w, l(0x7ef07ebb) +iadd r8.w, -r2.w, l(0x7ef07ebb) +iadd r9.w, -r4.w, l(0x7ef07ebb) +iadd r10.w, -r6.w, l(0x7ef07ebb) +add r11.w, -r0.w, l(1.000000) +min r11.w, r0.z, r11.w +mul_sat r7.w, r7.w, r11.w +add r11.w, -r2.w, l(1.000000) +min r11.w, r1.w, r11.w +mul_sat r8.w, r8.w, r11.w +add r11.w, -r4.w, l(1.000000) +min r11.w, r3.w, r11.w +mul_sat r9.w, r9.w, r11.w +add r11.w, -r6.w, l(1.000000) +min r11.w, r5.w, r11.w +mul_sat r10.w, r10.w, r11.w +ushr r7.w, r7.w, l(1) +iadd r7.w, r7.w, l(0x1fbc4639) +ushr r8.w, r8.w, l(1) +iadd r8.w, r8.w, l(0x1fbc4639) +ushr r9.w, r9.w, l(1) +iadd r9.w, r9.w, l(0x1fbc4639) +ushr r10.w, r10.w, l(1) +iadd r10.w, r10.w, l(0x1fbc4639) +mul r7.w, r7.w, CB0[0][1].x +mul r8.w, r8.w, CB0[0][1].x +mul r9.w, r9.w, CB0[0][1].x +mul r10.w, r10.w, CB0[0][1].x +add r13.xy, -r0.yxyy, l(1.000000, 1.000000, 0.000000, 0.000000) +mul r11.w, r13.x, r13.y +mul r13.xy, r0.xyxx, r13.xyxx +mul r0.x, r0.y, r0.x +add r0.y, -r0.z, r0.w +add r0.y, r0.y, l(0.031250) +iadd r0.y, -r0.y, l(0x7ef07ebb) +mul r0.z, r0.y, r11.w +add r0.w, -r1.w, r2.w +add r0.w, r0.w, l(0.031250) +iadd r0.w, -r0.w, l(0x7ef07ebb) +mul r1.w, r0.w, r13.x +add r2.w, -r3.w, r4.w +add r2.w, r2.w, l(0.031250) +iadd r2.w, -r2.w, l(0x7ef07ebb) +mul r3.w, r2.w, r13.y +add r4.w, -r5.w, r6.w +add r4.w, r4.w, l(0.031250) +iadd r4.w, -r4.w, l(0x7ef07ebb) +mul r5.w, r0.x, r4.w +mul r6.w, r0.z, r7.w +mul r12.w, r1.w, r8.w +mul r3.w, r3.w, r9.w +mad r1.w, r8.w, r1.w, r3.w +mad r0.y, r11.w, r0.y, r1.w +mul r5.w, r5.w, r10.w +mad r0.z, r7.w, r0.z, r5.w +mad r0.w, r13.x, r0.w, r0.z +mad r0.z, r13.y, r2.w, r0.z +mad r0.x, r0.x, r4.w, r1.w +add r1.w, r12.w, r12.w +mad r1.w, r6.w, l(2.000000), r1.w +mad r1.w, r3.w, l(2.000000), r1.w +mad r1.w, r5.w, l(2.000000), r1.w +add r1.w, r0.y, r1.w +add r1.w, r0.w, r1.w +add r1.w, r0.z, r1.w +add r1.w, r0.x, r1.w +iadd r2.w, -r1.w, l(0x7ef19fff) +mad r1.w, -r2.w, r1.w, l(2.000000) +mul r1.w, r1.w, r2.w +mul r2.xyz, r2.xyzx, r6.wwww +mad r2.xyz, r3.xyzx, r6.wwww, r2.xyzx +mad r2.xyz, r6.xyzx, r12.wwww, r2.xyzx +mad r2.xyz, r8.xyzx, r12.wwww, r2.xyzx +mad r2.xyz, r7.xyzx, r3.wwww, r2.xyzx +mad r2.xyz, r9.xyzx, r3.wwww, r2.xyzx +mad r2.xyz, r11.xyzx, r5.wwww, r2.xyzx +mad r1.xyz, r1.xyzx, r5.wwww, r2.xyzx +mad r1.xyz, r4.xyzx, r0.yyyy, r1.xyzx +mad r1.xyz, r5.xyzx, r0.wwww, r1.xyzx +mad r0.yzw, r10.xxyz, r0.zzzz, r1.xxyz +mad r0.xyz, r12.xyzx, r0.xxxx, r0.yzwy +mul_sat r0.xyz, r1.wwww, r0.xyzx +sqrt o0.xyz, r0.xyzx +mov o0.w, l(1.000000) +ret +// Approximately 172 instruction slots used +#endif + +const BYTE guest_output_ffx_cas_resample_ps[] = +{ + 68, 88, 66, 67, 153, 223, + 100, 209, 57, 228, 221, 164, + 114, 23, 52, 94, 29, 110, + 188, 4, 1, 0, 0, 0, + 92, 24, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 120, 2, 0, 0, 172, 2, + 0, 0, 224, 2, 0, 0, + 192, 23, 0, 0, 82, 68, + 69, 70, 60, 2, 0, 0, + 1, 0, 0, 0, 176, 0, + 0, 0, 2, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 255, 255, 0, 5, 0, 0, + 20, 2, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 140, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 120, 101, 95, 116, + 101, 120, 116, 117, 114, 101, + 0, 88, 101, 67, 97, 115, + 82, 101, 115, 97, 109, 112, + 108, 101, 67, 111, 110, 115, + 116, 97, 110, 116, 115, 0, + 171, 171, 151, 0, 0, 0, + 3, 0, 0, 0, 200, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 64, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 92, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 128, 1, 0, 0, 8, 0, + 0, 0, 8, 0, 0, 0, + 2, 0, 0, 0, 168, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 204, 1, + 0, 0, 16, 0, 0, 0, + 4, 0, 0, 0, 2, 0, + 0, 0, 240, 1, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 120, 101, 95, 99, + 97, 115, 95, 111, 117, 116, + 112, 117, 116, 95, 111, 102, + 102, 115, 101, 116, 0, 105, + 110, 116, 50, 0, 171, 171, + 1, 0, 2, 0, 1, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 85, 1, 0, 0, + 120, 101, 95, 99, 97, 115, + 95, 105, 110, 112, 117, 116, + 95, 111, 117, 116, 112, 117, + 116, 95, 115, 105, 122, 101, + 95, 114, 97, 116, 105, 111, + 0, 102, 108, 111, 97, 116, + 50, 0, 171, 171, 1, 0, + 3, 0, 1, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 159, 1, 0, 0, 120, 101, + 95, 99, 97, 115, 95, 115, + 104, 97, 114, 112, 110, 101, + 115, 115, 95, 112, 111, 115, + 116, 95, 115, 101, 116, 117, + 112, 0, 102, 108, 111, 97, + 116, 0, 171, 171, 0, 0, + 3, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 232, 1, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 3, 0, 0, + 83, 86, 95, 80, 111, 115, + 105, 116, 105, 111, 110, 0, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 84, 97, 114, 103, 101, + 116, 0, 171, 171, 83, 72, + 69, 88, 216, 20, 0, 0, + 81, 0, 0, 0, 54, 5, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 100, 32, 0, 4, + 50, 16, 16, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 101, 0, 0, 3, 242, 32, + 16, 0, 0, 0, 0, 0, + 104, 0, 0, 2, 14, 0, + 0, 0, 27, 0, 0, 5, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 16, 16, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 70, 128, 48, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 50, 0, 0, 17, + 194, 0, 16, 0, 0, 0, + 0, 0, 166, 142, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 191, + 0, 0, 0, 191, 86, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 11, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 230, 138, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 230, 10, 16, 0, 0, 0, + 0, 0, 65, 0, 0, 5, + 194, 0, 16, 0, 0, 0, + 0, 0, 6, 4, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 8, 50, 0, 16, 0, + 0, 0, 0, 0, 230, 10, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 27, 0, 0, 5, 50, 0, + 16, 0, 1, 0, 0, 0, + 230, 10, 16, 0, 0, 0, + 0, 0, 30, 0, 0, 10, + 242, 0, 16, 0, 2, 0, + 0, 0, 70, 4, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 54, 0, 0, 5, 50, 0, + 16, 0, 3, 0, 0, 0, + 230, 10, 16, 0, 2, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 3, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 3, 0, 0, 0, 70, 14, + 16, 0, 3, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 2, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 14, 16, 0, + 2, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 30, 0, 0, 10, + 242, 0, 16, 0, 5, 0, + 0, 0, 70, 4, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 255, 255, 255, 255, + 54, 0, 0, 5, 50, 0, + 16, 0, 6, 0, 0, 0, + 230, 10, 16, 0, 5, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 6, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 6, 0, 0, 0, 70, 14, + 16, 0, 6, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 5, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 14, 16, 0, + 5, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 242, 0, 16, 0, + 7, 0, 0, 0, 70, 4, + 16, 0, 1, 0, 0, 0, + 2, 64, 0, 0, 255, 255, + 255, 255, 1, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 50, 0, 16, 0, 8, 0, + 0, 0, 230, 10, 16, 0, + 7, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 8, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 8, 0, 0, 0, + 70, 14, 16, 0, 8, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 7, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 7, 0, 0, 0, 70, 14, + 16, 0, 7, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 242, 0, + 16, 0, 9, 0, 0, 0, + 70, 4, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 54, 0, + 0, 5, 50, 0, 16, 0, + 10, 0, 0, 0, 230, 10, + 16, 0, 9, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 10, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 10, 0, + 0, 0, 70, 14, 16, 0, + 10, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 9, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 9, 0, 0, 0, + 70, 14, 16, 0, 9, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 30, 0, 0, 10, + 242, 0, 16, 0, 11, 0, + 0, 0, 70, 4, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 54, 0, 0, 5, 50, 0, + 16, 0, 12, 0, 0, 0, + 230, 10, 16, 0, 11, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 12, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 12, 0, 0, 0, 70, 14, + 16, 0, 12, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 11, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 11, 0, + 0, 0, 70, 14, 16, 0, + 11, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 50, 0, 16, 0, + 1, 0, 0, 0, 70, 0, + 16, 0, 1, 0, 0, 0, + 2, 64, 0, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 6, 0, 0, 0, 70, 2, + 16, 0, 6, 0, 0, 0, + 70, 2, 16, 0, 6, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 5, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 8, 0, + 0, 0, 70, 2, 16, 0, + 8, 0, 0, 0, 70, 2, + 16, 0, 8, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 7, 0, 0, 0, + 70, 2, 16, 0, 7, 0, + 0, 0, 70, 2, 16, 0, + 7, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 10, 0, 0, 0, 70, 2, + 16, 0, 10, 0, 0, 0, + 70, 2, 16, 0, 10, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 12, 0, + 0, 0, 70, 2, 16, 0, + 12, 0, 0, 0, 70, 2, + 16, 0, 12, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 11, 0, 0, 0, + 70, 2, 16, 0, 11, 0, + 0, 0, 70, 2, 16, 0, + 11, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 9, 0, 0, 0, 70, 2, + 16, 0, 9, 0, 0, 0, + 70, 2, 16, 0, 9, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 1, 0, 0, 0, + 51, 0, 0, 7, 66, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 26, 0, 16, 0, + 4, 0, 0, 0, 51, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 5, 0, 0, 0, 26, 0, + 16, 0, 10, 0, 0, 0, + 51, 0, 0, 7, 66, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 1, 0, 0, 0, + 26, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 0, + 10, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 26, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 5, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 26, 0, 16, 0, + 6, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 26, 0, + 16, 0, 8, 0, 0, 0, + 26, 0, 16, 0, 12, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 5, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 6, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 26, 0, 16, 0, + 8, 0, 0, 0, 26, 0, + 16, 0, 12, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 26, 0, + 16, 0, 7, 0, 0, 0, + 26, 0, 16, 0, 10, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 26, 0, + 16, 0, 4, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 9, 0, + 0, 0, 26, 0, 16, 0, + 12, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 7, 0, 0, 0, 26, 0, + 16, 0, 10, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 4, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 5, 0, 0, 0, 26, 0, + 16, 0, 9, 0, 0, 0, + 26, 0, 16, 0, 12, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 58, 0, + 16, 0, 5, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 0, 10, 0, + 0, 0, 26, 0, 16, 0, + 12, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 0, 5, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 6, 0, + 0, 0, 26, 0, 16, 0, + 1, 0, 0, 0, 26, 0, + 16, 0, 11, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 5, 0, 0, 0, + 58, 0, 16, 0, 5, 0, + 0, 0, 58, 0, 16, 0, + 6, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 6, 0, 0, 0, 26, 0, + 16, 0, 10, 0, 0, 0, + 26, 0, 16, 0, 12, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 6, 0, + 0, 0, 26, 0, 16, 0, + 5, 0, 0, 0, 58, 0, + 16, 0, 6, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 7, 0, 0, 0, + 26, 0, 16, 0, 1, 0, + 0, 0, 26, 0, 16, 0, + 11, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 6, 0, 0, 0, 58, 0, + 16, 0, 6, 0, 0, 0, + 58, 0, 16, 0, 7, 0, + 0, 0, 30, 0, 0, 8, + 130, 0, 16, 0, 7, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 187, 126, 240, 126, 30, 0, + 0, 8, 130, 0, 16, 0, + 8, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 187, 126, 240, 126, + 30, 0, 0, 8, 130, 0, + 16, 0, 9, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 30, 0, 0, 8, + 130, 0, 16, 0, 10, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 6, 0, + 0, 0, 1, 64, 0, 0, + 187, 126, 240, 126, 0, 0, + 0, 8, 130, 0, 16, 0, + 11, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 51, 0, 0, 7, 130, 0, + 16, 0, 11, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 11, 0, 0, 0, 56, 32, + 0, 7, 130, 0, 16, 0, + 7, 0, 0, 0, 58, 0, + 16, 0, 7, 0, 0, 0, + 58, 0, 16, 0, 11, 0, + 0, 0, 0, 0, 0, 8, + 130, 0, 16, 0, 11, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 2, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 51, 0, + 0, 7, 130, 0, 16, 0, + 11, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 11, 0, + 0, 0, 56, 32, 0, 7, + 130, 0, 16, 0, 8, 0, + 0, 0, 58, 0, 16, 0, + 8, 0, 0, 0, 58, 0, + 16, 0, 11, 0, 0, 0, + 0, 0, 0, 8, 130, 0, + 16, 0, 11, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 51, 0, 0, 7, + 130, 0, 16, 0, 11, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 11, 0, 0, 0, + 56, 32, 0, 7, 130, 0, + 16, 0, 9, 0, 0, 0, + 58, 0, 16, 0, 9, 0, + 0, 0, 58, 0, 16, 0, + 11, 0, 0, 0, 0, 0, + 0, 8, 130, 0, 16, 0, + 11, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 6, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 51, 0, 0, 7, 130, 0, + 16, 0, 11, 0, 0, 0, + 58, 0, 16, 0, 5, 0, + 0, 0, 58, 0, 16, 0, + 11, 0, 0, 0, 56, 32, + 0, 7, 130, 0, 16, 0, + 10, 0, 0, 0, 58, 0, + 16, 0, 10, 0, 0, 0, + 58, 0, 16, 0, 11, 0, + 0, 0, 85, 0, 0, 7, + 130, 0, 16, 0, 7, 0, + 0, 0, 58, 0, 16, 0, + 7, 0, 0, 0, 1, 64, + 0, 0, 1, 0, 0, 0, + 30, 0, 0, 7, 130, 0, + 16, 0, 7, 0, 0, 0, + 58, 0, 16, 0, 7, 0, + 0, 0, 1, 64, 0, 0, + 57, 70, 188, 31, 85, 0, + 0, 7, 130, 0, 16, 0, + 8, 0, 0, 0, 58, 0, + 16, 0, 8, 0, 0, 0, + 1, 64, 0, 0, 1, 0, + 0, 0, 30, 0, 0, 7, + 130, 0, 16, 0, 8, 0, + 0, 0, 58, 0, 16, 0, + 8, 0, 0, 0, 1, 64, + 0, 0, 57, 70, 188, 31, + 85, 0, 0, 7, 130, 0, + 16, 0, 9, 0, 0, 0, + 58, 0, 16, 0, 9, 0, + 0, 0, 1, 64, 0, 0, + 1, 0, 0, 0, 30, 0, + 0, 7, 130, 0, 16, 0, + 9, 0, 0, 0, 58, 0, + 16, 0, 9, 0, 0, 0, + 1, 64, 0, 0, 57, 70, + 188, 31, 85, 0, 0, 7, + 130, 0, 16, 0, 10, 0, + 0, 0, 58, 0, 16, 0, + 10, 0, 0, 0, 1, 64, + 0, 0, 1, 0, 0, 0, + 30, 0, 0, 7, 130, 0, + 16, 0, 10, 0, 0, 0, + 58, 0, 16, 0, 10, 0, + 0, 0, 1, 64, 0, 0, + 57, 70, 188, 31, 56, 0, + 0, 9, 130, 0, 16, 0, + 7, 0, 0, 0, 58, 0, + 16, 0, 7, 0, 0, 0, + 10, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 56, 0, + 0, 9, 130, 0, 16, 0, + 8, 0, 0, 0, 58, 0, + 16, 0, 8, 0, 0, 0, + 10, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 56, 0, + 0, 9, 130, 0, 16, 0, + 9, 0, 0, 0, 58, 0, + 16, 0, 9, 0, 0, 0, + 10, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 56, 0, + 0, 9, 130, 0, 16, 0, + 10, 0, 0, 0, 58, 0, + 16, 0, 10, 0, 0, 0, + 10, 128, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 11, 50, 0, 16, 0, + 13, 0, 0, 0, 22, 5, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 11, 0, 0, 0, + 10, 0, 16, 0, 13, 0, + 0, 0, 26, 0, 16, 0, + 13, 0, 0, 0, 56, 0, + 0, 7, 50, 0, 16, 0, + 13, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 13, 0, + 0, 0, 56, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 34, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 7, + 34, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 61, + 30, 0, 0, 8, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 11, 0, 0, 0, + 0, 0, 0, 8, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 61, + 30, 0, 0, 8, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 13, 0, 0, 0, + 0, 0, 0, 8, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 3, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 61, + 30, 0, 0, 8, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 2, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 26, 0, + 16, 0, 13, 0, 0, 0, + 0, 0, 0, 8, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 5, 0, 0, 0, + 58, 0, 16, 0, 6, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 61, + 30, 0, 0, 8, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 130, 0, 16, 0, 5, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 4, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 6, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 7, 0, 0, 0, 56, 0, + 0, 7, 130, 0, 16, 0, + 12, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 8, 0, + 0, 0, 56, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 58, 0, + 16, 0, 9, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 8, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 3, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 11, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 5, 0, 0, 0, + 58, 0, 16, 0, 5, 0, + 0, 0, 58, 0, 16, 0, + 10, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 7, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 5, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 0, 0, + 0, 7, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 12, 0, 0, 0, + 58, 0, 16, 0, 12, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 6, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 58, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 58, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 5, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 58, 0, 16, 0, 1, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 0, 0, 0, 7, 130, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 0, 0, + 0, 7, 130, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 30, 0, 0, 8, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 255, 159, + 241, 126, 50, 0, 0, 10, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 64, + 56, 0, 0, 7, 130, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 246, 15, 16, 0, 6, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 246, 15, + 16, 0, 6, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 6, 0, 0, 0, 246, 15, + 16, 0, 12, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 8, 0, 0, 0, 246, 15, + 16, 0, 12, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 7, 0, 0, 0, 246, 15, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 9, 0, 0, 0, 246, 15, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 11, 0, 0, 0, 246, 15, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 246, 15, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 86, 5, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 226, 0, 16, 0, 0, 0, + 0, 0, 6, 9, 16, 0, + 10, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 6, 9, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 12, 0, 0, 0, 6, 0, + 16, 0, 0, 0, 0, 0, + 150, 7, 16, 0, 0, 0, + 0, 0, 56, 32, 0, 7, + 114, 0, 16, 0, 0, 0, + 0, 0, 246, 15, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 75, 0, 0, 5, 114, 32, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 130, 32, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 62, 0, + 0, 1, 83, 84, 65, 84, + 148, 0, 0, 0, 172, 0, + 0, 0, 14, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 114, 0, 0, 0, + 20, 0, 0, 0, 4, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 18, 0, 0, 0, + 0, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_dither_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_dither_ps.h new file mode 100644 index 000000000..14e7dd87b --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_dither_ps.h @@ -0,0 +1,1454 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeCasSharpenConstants +// { +// +// int2 xe_cas_output_offset; // Offset: 0 Size: 8 +// float xe_cas_sharpness_post_setup; // Offset: 8 Size: 4 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_texture texture float3 2d T0 t0 1 +// XeCasSharpenConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_5_1 +dcl_globalFlags refactoringAllowed +dcl_immediateConstantBuffer { { -0.001003, 0, 0, 0}, + { 0.000881, 0, 0, 0}, + { 0.001693, 0, 0, 0}, + { -0.001555, 0, 0, 0}, + { 0.001279, 0, 0, 0}, + { -0.000605, 0, 0, 0}, + { 0.001065, 0, 0, 0}, + { -0.001386, 0, 0, 0}, + { 0.001356, 0, 0, 0}, + { 0.000513, 0, 0, 0}, + { 0.001218, 0, 0, 0}, + { -0.001601, 0, 0, 0}, + { 0.000590, 0, 0, 0}, + { -0.000283, 0, 0, 0}, + { 0.001111, 0, 0, 0}, + { -0.001417, 0, 0, 0}, + { 0.001448, 0, 0, 0}, + { -0.000544, 0, 0, 0}, + { 0.000130, 0, 0, 0}, + { -0.001203, 0, 0, 0}, + { 0.000437, 0, 0, 0}, + { -0.001049, 0, 0, 0}, + { 0.000620, 0, 0, 0}, + { -0.000483, 0, 0, 0}, + { 0.001877, 0, 0, 0}, + { -0.001095, 0, 0, 0}, + { -0.000100, 0, 0, 0}, + { -0.000528, 0, 0, 0}, + { 0.001432, 0, 0, 0}, + { -0.001938, 0, 0, 0}, + { -0.000697, 0, 0, 0}, + { 0.000038, 0, 0, 0}, + { 0.000712, 0, 0, 0}, + { -0.001310, 0, 0, 0}, + { 0.001095, 0, 0, 0}, + { -0.000299, 0, 0, 0}, + { 0.001754, 0, 0, 0}, + { -0.001677, 0, 0, 0}, + { 0.001478, 0, 0, 0}, + { -0.000038, 0, 0, 0}, + { 0.000789, 0, 0, 0}, + { -0.001831, 0, 0, 0}, + { 0.000299, 0, 0, 0}, + { 0.000988, 0, 0, 0}, + { -0.001172, 0, 0, 0}, + { 0.000176, 0, 0, 0}, + { 0.001647, 0, 0, 0}, + { -0.001585, 0, 0, 0}, + { 0.000345, 0, 0, 0}, + { 0.001861, 0, 0, 0}, + { -0.001769, 0, 0, 0}, + { -0.000866, 0, 0, 0}, + { 0.000896, 0, 0, 0}, + { 0.000161, 0, 0, 0}, + { -0.000927, 0, 0, 0}, + { -0.001524, 0, 0, 0}, + { -0.000651, 0, 0, 0}, + { 0.001294, 0, 0, 0}, + { -0.000804, 0, 0, 0}, + { -0.001463, 0, 0, 0}, + { 0.001800, 0, 0, 0}, + { -0.000850, 0, 0, 0}, + { 0.000850, 0, 0, 0}, + { -0.000452, 0, 0, 0}, + { -0.001065, 0, 0, 0}, + { -0.000146, 0, 0, 0}, + { 0.000237, 0, 0, 0}, + { 0.001417, 0, 0, 0}, + { -0.000590, 0, 0, 0}, + { -0.000191, 0, 0, 0}, + { 0.001601, 0, 0, 0}, + { 0.001019, 0, 0, 0}, + { 0.000406, 0, 0, 0}, + { -0.000207, 0, 0, 0}, + { 0.001585, 0, 0, 0}, + { 0.000651, 0, 0, 0}, + { -0.000069, 0, 0, 0}, + { 0.000421, 0, 0, 0}, + { -0.001647, 0, 0, 0}, + { 0.001371, 0, 0, 0}, + { 0.000927, 0, 0, 0}, + { -0.000666, 0, 0, 0}, + { 0.001187, 0, 0, 0}, + { -0.001448, 0, 0, 0}, + { 0.000574, 0, 0, 0}, + { -0.001892, 0, 0, 0}, + { 0.000758, 0, 0, 0}, + { -0.001294, 0, 0, 0}, + { 0.001922, 0, 0, 0}, + { -0.001662, 0, 0, 0}, + { -0.001034, 0, 0, 0}, + { -0.000498, 0, 0, 0}, + { -0.001861, 0, 0, 0}, + { 0.001203, 0, 0, 0}, + { -0.000329, 0, 0, 0}, + { -0.001371, 0, 0, 0}, + { 0.001631, 0, 0, 0}, + { -0.001846, 0, 0, 0}, + { 0.000728, 0, 0, 0}, + { -0.000911, 0, 0, 0}, + { 0.001815, 0, 0, 0}, + { -0.001141, 0, 0, 0}, + { -0.000375, 0, 0, 0}, + { 0.000100, 0, 0, 0}, + { -0.000743, 0, 0, 0}, + { 0.001172, 0, 0, 0}, + { 0.000069, 0, 0, 0}, + { 0.001494, 0, 0, 0}, + { 0.000973, 0, 0, 0}, + { -0.000957, 0, 0, 0}, + { 0.001938, 0, 0, 0}, + { 0.000528, 0, 0, 0}, + { 0.000054, 0, 0, 0}, + { -0.001248, 0, 0, 0}, + { -0.000268, 0, 0, 0}, + { 0.001540, 0, 0, 0}, + { -0.000008, 0, 0, 0}, + { 0.000314, 0, 0, 0}, + { 0.001340, 0, 0, 0}, + { -0.001754, 0, 0, 0}, + { 0.000498, 0, 0, 0}, + { -0.001187, 0, 0, 0}, + { 0.000774, 0, 0, 0}, + { -0.001340, 0, 0, 0}, + { 0.000268, 0, 0, 0}, + { -0.001478, 0, 0, 0}, + { -0.000130, 0, 0, 0}, + { -0.000774, 0, 0, 0}, + { 0.001310, 0, 0, 0}, + { 0.000391, 0, 0, 0}, + { 0.000957, 0, 0, 0}, + { -0.000467, 0, 0, 0}, + { -0.001540, 0, 0, 0}, + { 0.001034, 0, 0, 0}, + { -0.000682, 0, 0, 0}, + { 0.001677, 0, 0, 0}, + { 0.001003, 0, 0, 0}, + { -0.000421, 0, 0, 0}, + { 0.001785, 0, 0, 0}, + { -0.000237, 0, 0, 0}, + { -0.000620, 0, 0, 0}, + { 0.001662, 0, 0, 0}, + { 0.000835, 0, 0, 0}, + { -0.001723, 0, 0, 0}, + { -0.001080, 0, 0, 0}, + { 0.001769, 0, 0, 0}, + { -0.000789, 0, 0, 0}, + { -0.001785, 0, 0, 0}, + { 0.000682, 0, 0, 0}, + { -0.000988, 0, 0, 0}, + { -0.001325, 0, 0, 0}, + { -0.000176, 0, 0, 0}, + { -0.001509, 0, 0, 0}, + { 0.000329, 0, 0, 0}, + { -0.001953, 0, 0, 0}, + { 0.000666, 0, 0, 0}, + { -0.001616, 0, 0, 0}, + { 0.001157, 0, 0, 0}, + { 0.000452, 0, 0, 0}, + { -0.000437, 0, 0, 0}, + { 0.000191, 0, 0, 0}, + { -0.001494, 0, 0, 0}, + { 0.001141, 0, 0, 0}, + { 0.000084, 0, 0, 0}, + { 0.001892, 0, 0, 0}, + { 0.001402, 0, 0, 0}, + { 0.000559, 0, 0, 0}, + { 0.000115, 0, 0, 0}, + { 0.001264, 0, 0, 0}, + { -0.000574, 0, 0, 0}, + { -0.000973, 0, 0, 0}, + { 0.001325, 0, 0, 0}, + { 0.000222, 0, 0, 0}, + { -0.000758, 0, 0, 0}, + { -0.001356, 0, 0, 0}, + { 0.001463, 0, 0, 0}, + { 0.000866, 0, 0, 0}, + { -0.000360, 0, 0, 0}, + { 0.000544, 0, 0, 0}, + { -0.001126, 0, 0, 0}, + { -0.000253, 0, 0, 0}, + { -0.000559, 0, 0, 0}, + { -0.001815, 0, 0, 0}, + { 0.001723, 0, 0, 0}, + { -0.001157, 0, 0, 0}, + { 0.000743, 0, 0, 0}, + { 0.001570, 0, 0, 0}, + { -0.000115, 0, 0, 0}, + { -0.001218, 0, 0, 0}, + { 0.001831, 0, 0, 0}, + { 0.000023, 0, 0, 0}, + { -0.001922, 0, 0, 0}, + { 0.001739, 0, 0, 0}, + { -0.000712, 0, 0, 0}, + { 0.001555, 0, 0, 0}, + { -0.001708, 0, 0, 0}, + { 0.001233, 0, 0, 0}, + { 0.000207, 0, 0, 0}, + { 0.001049, 0, 0, 0}, + { -0.000728, 0, 0, 0}, + { -0.001631, 0, 0, 0}, + { -0.000314, 0, 0, 0}, + { 0.000483, 0, 0, 0}, + { -0.001800, 0, 0, 0}, + { 0.000942, 0, 0, 0}, + { -0.000345, 0, 0, 0}, + { 0.000697, 0, 0, 0}, + { -0.001019, 0, 0, 0}, + { -0.001570, 0, 0, 0}, + { -0.000023, 0, 0, 0}, + { -0.001279, 0, 0, 0}, + { 0.000804, 0, 0, 0}, + { -0.000896, 0, 0, 0}, + { -0.001432, 0, 0, 0}, + { 0.000605, 0, 0, 0}, + { -0.000084, 0, 0, 0}, + { 0.000911, 0, 0, 0}, + { 0.001953, 0, 0, 0}, + { -0.001402, 0, 0, 0}, + { -0.000636, 0, 0, 0}, + { 0.001509, 0, 0, 0}, + { -0.000820, 0, 0, 0}, + { 0.001248, 0, 0, 0}, + { 0.000253, 0, 0, 0}, + { 0.001524, 0, 0, 0}, + { 0.001126, 0, 0, 0}, + { 0.000360, 0, 0, 0}, + { -0.000391, 0, 0, 0}, + { 0.001907, 0, 0, 0}, + { 0.001386, 0, 0, 0}, + { -0.001111, 0, 0, 0}, + { 0.001616, 0, 0, 0}, + { -0.000881, 0, 0, 0}, + { 0.000146, 0, 0, 0}, + { 0.001080, 0, 0, 0}, + { -0.000054, 0, 0, 0}, + { 0.000283, 0, 0, 0}, + { -0.001693, 0, 0, 0}, + { -0.001264, 0, 0, 0}, + { -0.000513, 0, 0, 0}, + { -0.000161, 0, 0, 0}, + { -0.001877, 0, 0, 0}, + { -0.000835, 0, 0, 0}, + { 0.000636, 0, 0, 0}, + { 0.000008, 0, 0, 0}, + { -0.001907, 0, 0, 0}, + { -0.000222, 0, 0, 0}, + { 0.000375, 0, 0, 0}, + { -0.001739, 0, 0, 0}, + { -0.000406, 0, 0, 0}, + { -0.001233, 0, 0, 0}, + { 0.001708, 0, 0, 0}, + { -0.000942, 0, 0, 0}, + { 0.000820, 0, 0, 0}, + { 0.001846, 0, 0, 0}, + { 0.000467, 0, 0, 0} } +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_input_ps_siv linear noperspective v0.xy, position +dcl_output o0.xyzw +dcl_temps 6 +ftoi r0.xy, v0.xyxx +iadd r0.xy, r0.xyxx, -CB0[0][0].xyxx +iadd r1.xyzw, r0.xyxy, l(-1, 0, 0, -1) +mov r2.xy, r1.zwzz +mov r2.zw, l(0,0,0,0) +ld r2.xyz, r2.xyzw, T0[0].xyzw +mov r1.zw, l(0,0,0,0) +ld r1.xyz, r1.xyzw, T0[0].xyzw +mov r0.zw, l(0,0,0,0) +ld r3.xyz, r0.xyzw, T0[0].xyzw +iadd r4.xyzw, r0.xyxy, l(0, 1, 1, 0) +mov r5.xy, r4.zwzz +mov r5.zw, l(0,0,0,0) +ld r5.xyz, r5.xyzw, T0[0].xyzw +mov r4.zw, l(0,0,0,0) +ld r4.xyz, r4.xyzw, T0[0].xyzw +mul r2.xyz, r2.xyzx, r2.xyzx +mul r1.xyz, r1.xyzx, r1.xyzx +mul r0.z, r3.y, r3.y +mul r5.xyz, r5.xyzx, r5.xyzx +mul r4.xyz, r4.xyzx, r4.xyzx +min r0.w, r0.z, r5.y +min r0.w, r0.w, r1.y +min r1.w, r2.y, r4.y +min r0.w, r0.w, r1.w +max r0.z, r0.z, r5.y +max r0.z, r0.z, r1.y +max r1.w, r2.y, r4.y +max r0.z, r0.z, r1.w +iadd r1.w, -r0.z, l(0x7ef07ebb) +add r0.z, -r0.z, l(1.000000) +min r0.z, r0.z, r0.w +mul_sat r0.z, r1.w, r0.z +ushr r0.z, r0.z, l(1) +iadd r0.z, r0.z, l(0x1fbc4639) +mul r0.z, r0.z, CB0[0][0].z +mad r0.w, r0.z, l(4.000000), l(1.000000) +iadd r1.w, -r0.w, l(0x7ef19fff) +mad r0.w, -r1.w, r0.w, l(2.000000) +mul r0.w, r0.w, r1.w +mul r1.xyz, r0.zzzz, r1.xyzx +mad r1.xyz, r2.xyzx, r0.zzzz, r1.xyzx +mad r1.xyz, r5.xyzx, r0.zzzz, r1.xyzx +mad r1.xyz, r4.xyzx, r0.zzzz, r1.xyzx +mad r1.xyz, r3.xyzx, r3.xyzx, r1.xyzx +mul_sat r1.xyz, r0.wwww, r1.xyzx +sqrt r1.xyz, r1.xyzx +bfi r0.y, l(4), l(4), r0.y, l(0) +bfi r0.x, l(4), l(0), r0.x, r0.y +add o0.xyz, r1.xyzx, icb[r0.x + 0].xxxx +mov o0.w, l(1.000000) +ret +// Approximately 52 instruction slots used +#endif + +const BYTE guest_output_ffx_cas_sharpen_dither_ps[] = +{ + 68, 88, 66, 67, 200, 82, + 48, 235, 252, 11, 120, 26, + 79, 183, 112, 246, 236, 81, + 181, 118, 1, 0, 0, 0, + 168, 25, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 4, 2, 0, 0, 56, 2, + 0, 0, 108, 2, 0, 0, + 12, 25, 0, 0, 82, 68, + 69, 70, 200, 1, 0, 0, + 1, 0, 0, 0, 176, 0, + 0, 0, 2, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 255, 255, 0, 5, 0, 0, + 160, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 140, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 120, 101, 95, 116, + 101, 120, 116, 117, 114, 101, + 0, 88, 101, 67, 97, 115, + 83, 104, 97, 114, 112, 101, + 110, 67, 111, 110, 115, 116, + 97, 110, 116, 115, 0, 171, + 171, 171, 151, 0, 0, 0, + 2, 0, 0, 0, 200, 0, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 24, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 52, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 88, 1, 0, 0, 8, 0, + 0, 0, 4, 0, 0, 0, + 2, 0, 0, 0, 124, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 120, 101, + 95, 99, 97, 115, 95, 111, + 117, 116, 112, 117, 116, 95, + 111, 102, 102, 115, 101, 116, + 0, 105, 110, 116, 50, 0, + 171, 171, 1, 0, 2, 0, + 1, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 1, + 0, 0, 120, 101, 95, 99, + 97, 115, 95, 115, 104, 97, + 114, 112, 110, 101, 115, 115, + 95, 112, 111, 115, 116, 95, + 115, 101, 116, 117, 112, 0, + 102, 108, 111, 97, 116, 0, + 171, 171, 0, 0, 3, 0, + 1, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 116, 1, + 0, 0, 77, 105, 99, 114, + 111, 115, 111, 102, 116, 32, + 40, 82, 41, 32, 72, 76, + 83, 76, 32, 83, 104, 97, + 100, 101, 114, 32, 67, 111, + 109, 112, 105, 108, 101, 114, + 32, 49, 48, 46, 49, 0, + 73, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 3, 0, 0, 83, 86, + 95, 80, 111, 115, 105, 116, + 105, 111, 110, 0, 79, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 0, + 0, 0, 83, 86, 95, 84, + 97, 114, 103, 101, 116, 0, + 171, 171, 83, 72, 69, 88, + 152, 22, 0, 0, 81, 0, + 0, 0, 166, 5, 0, 0, + 106, 8, 0, 1, 53, 24, + 0, 0, 2, 4, 0, 0, + 132, 131, 131, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 231, 230, + 102, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 222, 221, 221, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 204, 203, 203, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 168, 167, + 167, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 159, 158, 30, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 140, 139, 139, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 182, 181, + 181, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 178, 177, 177, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 135, 134, 6, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 160, 159, + 159, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 210, 209, 209, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 155, 154, 26, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 149, 148, + 148, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 146, 145, 145, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 186, 185, 185, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 190, 189, + 189, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 143, 142, 14, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 137, 136, 8, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 158, 157, + 157, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 229, 228, 228, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 138, 137, 137, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 163, 162, + 34, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 253, 252, 252, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 246, 245, 245, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 144, 143, + 143, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 209, 208, 208, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 139, 138, 10, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 188, 187, + 187, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 254, 253, 253, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 183, 182, 54, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 161, 160, + 32, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 187, 186, 58, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 172, 171, 171, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 144, 143, + 143, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 157, 156, 156, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 230, 229, 229, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 220, 219, + 219, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 194, 193, 193, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 161, 160, 32, 184, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 207, 206, + 78, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 240, 239, 239, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 157, 156, 156, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 130, 129, + 129, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 154, 153, 153, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 185, 184, 56, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 215, + 215, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 208, 207, 207, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 181, 180, 180, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 244, 243, + 243, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 232, 231, 231, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 227, 226, 98, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 235, 234, + 106, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 169, 168, 40, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 243, 242, 114, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 200, 199, + 199, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 171, 170, 42, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 170, 169, 169, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 211, 210, + 82, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 192, 191, 191, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 236, 235, 235, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 223, 222, + 94, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 223, 222, 94, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 237, 236, 236, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 140, 139, + 139, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 153, 152, 24, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 249, 248, 120, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 186, 185, + 185, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 155, 154, 26, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 201, 200, 72, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 210, 209, + 209, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 134, 133, 133, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 213, 212, 212, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 217, 216, + 88, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 208, 207, 207, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 171, 170, 42, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 145, 144, + 144, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 221, 220, 220, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 216, 215, 215, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 180, 179, + 179, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 243, 242, 114, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 175, 174, 46, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 156, 155, + 155, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 190, 189, 189, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 151, 150, 22, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 248, 247, + 247, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 199, 198, 70, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 170, 169, 169, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 252, 251, + 251, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 218, 217, 217, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 136, 135, 135, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 131, 130, + 2, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 244, 243, 243, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 158, 157, 157, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 173, 172, + 172, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 180, 179, 179, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 214, 213, 213, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 242, 241, + 241, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 191, 190, 62, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 239, 238, 110, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 238, 237, + 237, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 150, 149, 149, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 197, 196, 196, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 209, 208, + 208, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 195, 194, 66, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 154, 153, 153, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 145, 144, + 144, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 196, 195, 195, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 255, 254, 126, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 251, 250, + 122, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 254, 253, 253, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 139, 138, 10, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 225, 224, + 96, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 164, 163, 163, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 141, 140, 140, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 202, 201, + 201, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 129, 128, 0, 183, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 165, 164, 164, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 176, 175, + 175, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 230, 229, 229, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 131, 130, 2, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 156, 155, + 155, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 203, 202, 74, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 176, 175, 175, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 141, 140, + 140, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 194, 193, 193, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 137, 136, 8, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 203, 202, + 74, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 172, 171, 171, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 205, 204, 204, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 251, 250, + 122, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 245, 244, 244, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 202, 201, 201, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 136, 135, + 135, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 179, 178, 50, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 220, 219, 219, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 132, 131, + 131, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 221, 220, 220, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 234, 233, 233, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 248, + 120, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 163, 162, 34, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 218, 217, 217, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 219, 218, + 90, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 226, 225, 225, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 142, 141, 141, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 232, 231, + 231, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 207, 206, 78, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 234, 233, 233, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 179, 178, + 50, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 130, 129, 129, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 174, 173, 173, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 185, 184, + 56, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 198, 197, 197, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 173, 172, 172, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 187, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 175, 174, 46, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 212, 211, 211, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 151, + 151, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 237, 236, 236, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 229, 228, 228, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 201, 200, + 72, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 196, 195, 195, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 150, 149, 149, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 177, 176, + 176, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 248, 247, 247, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 184, 183, 183, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 147, 146, + 18, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 241, 240, 240, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 166, 165, 165, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 150, + 22, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 254, 126, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 174, 173, 173, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 233, 232, + 104, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 199, 198, 70, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 178, 177, 177, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 192, 191, + 191, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 227, 226, 98, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 189, 188, 188, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 143, 142, + 14, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 148, 147, 147, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 133, 132, 132, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 147, 146, + 18, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 238, 237, 237, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 226, 225, 225, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 151, + 151, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 195, 194, 66, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 206, 205, 205, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 241, 240, + 240, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 160, 159, 159, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 240, 239, 239, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 193, 192, + 192, 55, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 252, 251, 251, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 228, 227, 227, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 187, 186, + 58, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 204, 203, 203, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 224, 223, 223, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 162, 161, + 161, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 217, 216, 88, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 138, 137, 137, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 191, 190, + 62, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 214, 213, 213, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 165, 164, 164, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 253, 252, + 252, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 236, 235, 235, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 247, 246, 118, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 181, 180, + 180, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 183, 182, 54, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 134, 133, 133, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 206, 205, + 205, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 193, 192, 192, 183, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 168, 167, 167, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 211, 210, + 82, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 235, 234, 106, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 188, 187, 187, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 159, 158, + 30, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 177, 176, 176, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 239, 238, 110, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 59, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 184, 183, 183, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 167, 166, 38, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 197, + 197, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 215, 214, 86, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 164, 163, 163, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 133, 132, + 132, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 200, 199, 199, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 148, 147, 147, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 189, 188, + 188, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 205, 204, 204, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 250, 249, 249, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 182, 181, + 181, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 146, 145, 145, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 212, 211, 211, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 231, 230, + 102, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 153, 152, 24, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 142, 141, 141, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 225, 224, + 96, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 149, 148, 148, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 222, 221, 221, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 166, 165, + 165, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 135, 134, 6, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 169, 168, 40, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 246, 245, + 245, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 219, 218, 90, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 167, 166, 38, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 129, 128, + 0, 55, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 250, 249, 249, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 233, 232, 104, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 197, 196, + 196, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 228, 227, 227, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 213, 212, 212, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 162, 161, + 161, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 224, 223, 223, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 247, 246, 118, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 215, 214, + 86, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 242, 241, 241, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 245, 244, 244, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 89, 0, + 0, 7, 70, 142, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 88, 24, 0, 7, + 70, 126, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 85, 85, + 0, 0, 0, 0, 0, 0, + 100, 32, 0, 4, 50, 16, + 16, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 0, 0, 0, 0, 104, 0, + 0, 2, 6, 0, 0, 0, + 27, 0, 0, 5, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 16, 16, 0, 0, 0, + 0, 0, 30, 0, 0, 10, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 70, 128, + 48, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 4, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 54, 0, + 0, 5, 50, 0, 16, 0, + 2, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 2, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 14, 16, 0, + 2, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 3, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 242, 0, + 16, 0, 4, 0, 0, 0, + 70, 4, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 50, 0, 16, 0, + 5, 0, 0, 0, 230, 10, + 16, 0, 4, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 5, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 14, 16, 0, + 5, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 4, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 14, 16, 0, 4, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 56, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 5, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 1, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 1, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 26, 0, 16, 0, + 4, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 52, 0, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 5, 0, 0, 0, + 52, 0, 0, 7, 66, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 1, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 1, 0, 0, 0, 26, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 52, 0, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 30, 0, 0, 8, 130, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 0, 0, 0, 8, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 51, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 56, 32, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 85, 0, 0, 7, 66, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 1, 0, 0, 0, 30, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 57, 70, + 188, 31, 56, 0, 0, 9, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 42, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 64, + 1, 64, 0, 0, 0, 0, + 128, 63, 30, 0, 0, 8, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 255, 159, 241, 126, 50, 0, + 0, 10, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 56, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 1, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 56, 32, + 0, 7, 114, 0, 16, 0, + 1, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 75, 0, 0, 5, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 140, 0, + 0, 11, 34, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 140, 0, 0, 11, 18, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 4, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 8, + 114, 32, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 6, 144, + 144, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 130, 32, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 62, 0, 0, 1, 83, 84, + 65, 84, 148, 0, 0, 0, + 52, 0, 0, 0, 6, 0, + 0, 0, 0, 1, 0, 0, + 2, 0, 0, 0, 28, 0, + 0, 0, 6, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_ps.h new file mode 100644 index 000000000..5cb5f7499 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_cas_sharpen_ps.h @@ -0,0 +1,491 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeCasSharpenConstants +// { +// +// int2 xe_cas_output_offset; // Offset: 0 Size: 8 +// float xe_cas_sharpness_post_setup; // Offset: 8 Size: 4 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_texture texture float3 2d T0 t0 1 +// XeCasSharpenConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_input_ps_siv linear noperspective v0.xy, position +dcl_output o0.xyzw +dcl_temps 5 +ftoi r0.xy, v0.xyxx +iadd r0.xy, r0.xyxx, -CB0[0][0].xyxx +iadd r1.xyzw, r0.xyxy, l(-1, 0, 0, -1) +mov r2.xy, r1.zwzz +mov r2.zw, l(0,0,0,0) +ld r2.xyz, r2.xyzw, T0[0].xyzw +mov r1.zw, l(0,0,0,0) +ld r1.xyz, r1.xyzw, T0[0].xyzw +mov r0.zw, l(0,0,0,0) +ld r3.xyz, r0.xyzw, T0[0].xyzw +iadd r0.xyzw, r0.xyxy, l(0, 1, 1, 0) +mov r4.xy, r0.zwzz +mov r4.zw, l(0,0,0,0) +ld r4.xyz, r4.xyzw, T0[0].xyzw +mov r0.zw, l(0,0,0,0) +ld r0.xyz, r0.xyzw, T0[0].xyzw +mul r2.xyz, r2.xyzx, r2.xyzx +mul r1.xyz, r1.xyzx, r1.xyzx +mul r0.w, r3.y, r3.y +mul r4.xyz, r4.xyzx, r4.xyzx +mul r0.xyz, r0.xyzx, r0.xyzx +min r1.w, r0.w, r4.y +min r1.w, r1.w, r1.y +min r2.w, r0.y, r2.y +min r1.w, r1.w, r2.w +max r0.w, r0.w, r4.y +max r0.w, r0.w, r1.y +max r2.w, r0.y, r2.y +max r0.w, r0.w, r2.w +iadd r2.w, -r0.w, l(0x7ef07ebb) +add r0.w, -r0.w, l(1.000000) +min r0.w, r0.w, r1.w +mul_sat r0.w, r2.w, r0.w +ushr r0.w, r0.w, l(1) +iadd r0.w, r0.w, l(0x1fbc4639) +mul r0.w, r0.w, CB0[0][0].z +mad r1.w, r0.w, l(4.000000), l(1.000000) +iadd r2.w, -r1.w, l(0x7ef19fff) +mad r1.w, -r2.w, r1.w, l(2.000000) +mul r1.w, r1.w, r2.w +mul r1.xyz, r0.wwww, r1.xyzx +mad r1.xyz, r2.xyzx, r0.wwww, r1.xyzx +mad r1.xyz, r4.xyzx, r0.wwww, r1.xyzx +mad r0.xyz, r0.xyzx, r0.wwww, r1.xyzx +mad r0.xyz, r3.xyzx, r3.xyzx, r0.xyzx +mul_sat r0.xyz, r1.wwww, r0.xyzx +sqrt o0.xyz, r0.xyzx +mov o0.w, l(1.000000) +ret +// Approximately 49 instruction slots used +#endif + +const BYTE guest_output_ffx_cas_sharpen_ps[] = +{ + 68, 88, 66, 67, 2, 226, + 230, 16, 201, 205, 207, 54, + 189, 193, 184, 163, 140, 156, + 247, 96, 1, 0, 0, 0, + 40, 9, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 4, 2, 0, 0, 56, 2, + 0, 0, 108, 2, 0, 0, + 140, 8, 0, 0, 82, 68, + 69, 70, 200, 1, 0, 0, + 1, 0, 0, 0, 176, 0, + 0, 0, 2, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 255, 255, 0, 5, 0, 0, + 160, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 140, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 120, 101, 95, 116, + 101, 120, 116, 117, 114, 101, + 0, 88, 101, 67, 97, 115, + 83, 104, 97, 114, 112, 101, + 110, 67, 111, 110, 115, 116, + 97, 110, 116, 115, 0, 171, + 171, 171, 151, 0, 0, 0, + 2, 0, 0, 0, 200, 0, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 24, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 52, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 88, 1, 0, 0, 8, 0, + 0, 0, 4, 0, 0, 0, + 2, 0, 0, 0, 124, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 120, 101, + 95, 99, 97, 115, 95, 111, + 117, 116, 112, 117, 116, 95, + 111, 102, 102, 115, 101, 116, + 0, 105, 110, 116, 50, 0, + 171, 171, 1, 0, 2, 0, + 1, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 1, + 0, 0, 120, 101, 95, 99, + 97, 115, 95, 115, 104, 97, + 114, 112, 110, 101, 115, 115, + 95, 112, 111, 115, 116, 95, + 115, 101, 116, 117, 112, 0, + 102, 108, 111, 97, 116, 0, + 171, 171, 0, 0, 3, 0, + 1, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 116, 1, + 0, 0, 77, 105, 99, 114, + 111, 115, 111, 102, 116, 32, + 40, 82, 41, 32, 72, 76, + 83, 76, 32, 83, 104, 97, + 100, 101, 114, 32, 67, 111, + 109, 112, 105, 108, 101, 114, + 32, 49, 48, 46, 49, 0, + 73, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 3, 0, 0, 83, 86, + 95, 80, 111, 115, 105, 116, + 105, 111, 110, 0, 79, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 0, + 0, 0, 83, 86, 95, 84, + 97, 114, 103, 101, 116, 0, + 171, 171, 83, 72, 69, 88, + 24, 6, 0, 0, 81, 0, + 0, 0, 134, 1, 0, 0, + 106, 8, 0, 1, 89, 0, + 0, 7, 70, 142, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 88, 24, 0, 7, + 70, 126, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 85, 85, + 0, 0, 0, 0, 0, 0, + 100, 32, 0, 4, 50, 16, + 16, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 0, 0, 0, 0, 104, 0, + 0, 2, 5, 0, 0, 0, + 27, 0, 0, 5, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 16, 16, 0, 0, 0, + 0, 0, 30, 0, 0, 10, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 70, 128, + 48, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 4, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 54, 0, + 0, 5, 50, 0, 16, 0, + 2, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 2, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 14, 16, 0, + 2, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 3, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 4, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 50, 0, 16, 0, + 4, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 4, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 4, 0, + 0, 0, 70, 14, 16, 0, + 4, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 56, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 26, 0, 16, 0, 3, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 4, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 26, 0, + 16, 0, 1, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 2, 0, 0, 0, 51, 0, + 0, 7, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 4, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 1, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 30, 0, 0, 8, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 0, 0, 0, 8, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 51, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 56, 32, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 85, 0, 0, 7, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 1, 0, 0, 0, 30, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 57, 70, + 188, 31, 56, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 42, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 64, + 1, 64, 0, 0, 0, 0, + 128, 63, 30, 0, 0, 8, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 255, 159, 241, 126, 50, 0, + 0, 10, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 56, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 1, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 56, 32, + 0, 7, 114, 0, 16, 0, + 0, 0, 0, 0, 246, 15, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 75, 0, 0, 5, + 114, 32, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 130, 32, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 62, 0, 0, 1, 83, 84, + 65, 84, 148, 0, 0, 0, + 49, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 27, 0, + 0, 0, 6, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_easu_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_easu_ps.h new file mode 100644 index 000000000..3c9a5a926 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_easu_ps.h @@ -0,0 +1,2098 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeFsrEasuConstants +// { +// +// float2 xe_fsr_easu_input_output_size_ratio;// Offset: 0 Size: 8 +// float2 xe_fsr_easu_input_size_inv; // Offset: 8 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_sampler_linear_clamp sampler NA NA S0 s0 1 +// xe_texture texture float3 2d T0 t0 1 +// XeFsrEasuConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_sampler S0[0:0], mode_default, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_input_ps_siv linear noperspective v0.xy, position +dcl_output o0.xyzw +dcl_temps 22 +mul r0.w, CB0[0][0].w, l(4.000000) +mad r0.xy, CB0[0][0].xyxx, l(0.500000, 0.500000, 0.000000, 0.000000), l(-0.500000, -0.500000, 0.000000, 0.000000) +mul r1.xyz, CB0[0][0].zwwz, l(1.000000, 1.000000, -1.000000, 0.000000) +ftou r2.xy, v0.xyxx +utof r2.xy, r2.xyxx +mad r0.xy, r2.xyxx, CB0[0][0].xyxx, r0.xyxx +round_ni r2.xy, r0.xyxx +add r0.xy, r0.xyxx, -r2.xyxx +mad r1.xy, r2.xyxx, r1.xyxx, r1.xzxx +mad r2.xyzw, CB0[0][0].zwzw, l(-1.000000, 2.000000, 1.000000, 2.000000), r1.xyxy +mov r0.z, l(0) +add r0.zw, r0.zzzw, r1.xxxy +gather4 r1.zw, r1.xyxx, T0[0].zwxy, S0[0].x +gather4 r3.xy, r1.xyxx, T0[0].xyzw, S0[0].y +gather4 r4.xz, r1.xyxx, T0[0].xzyw, S0[0].z +gather4 r5.xyzw, r2.xyxx, T0[0].xyzw, S0[0].x +gather4 r6.xyzw, r2.xyxx, T0[0].xyzw, S0[0].y +gather4 r7.xyzw, r2.xyxx, T0[0].yzxw, S0[0].z +gather4 r8.xyzw, r2.zwzz, T0[0].xyzw, S0[0].x +gather4 r9.xyzw, r2.zwzz, T0[0].xyzw, S0[0].y +gather4 r2.xyzw, r2.zwzz, T0[0].xyzw, S0[0].z +gather4 r1.xy, r0.zwzz, T0[0].zwxy, S0[0].x +gather4 r3.zw, r0.zwzz, T0[0].xyzw, S0[0].y +gather4 r10.zw, r0.zwzz, T0[0].xyzw, S0[0].z +mad r0.zw, r1.zzzw, l(0.000000, 0.000000, 0.500000, 0.500000), r3.xxxy +mad r0.zw, r4.xxxz, l(0.000000, 0.000000, 0.500000, 0.500000), r0.zzzw +mad r11.xyzw, r5.zwyx, l(0.500000, 0.500000, 0.500000, 0.500000), r6.zwyx +mad r11.xyzw, r7.ywxz, l(0.500000, 0.500000, 0.500000, 0.500000), r11.xyzw +mad r12.xyzw, r8.xyzw, l(0.500000, 0.500000, 0.500000, 0.500000), r9.xyzw +mad r12.xyzw, r2.xyzw, l(0.500000, 0.500000, 0.500000, 0.500000), r12.xyzw +mad r13.xy, r1.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000), r3.zwzz +mad r13.xy, r10.zwzz, l(0.500000, 0.500000, 0.000000, 0.000000), r13.xyxx +add r14.xyzw, -r0.xyxy, l(1.000000, 1.000000, 0.000000, -1.000000) +mul r4.w, r14.y, r14.x +add r15.xyzw, -r11.xyxz, r12.wwzx +add r16.xyz, -r11.yxwy, r11.xzzx +max r13.zw, |r15.xxxw|, |r16.xxxz| +iadd r13.zw, -r13.zzzw, l(0, 0, 0x7ef07ebb, 0x7ef07ebb) +mul_sat r11.y, r13.z, |r15.y| +mul r11.y, r11.y, r11.y +add r16.xz, -r0.zzzz, r11.xxzx +max r0.z, |r16.x|, |r16.y| +iadd r0.z, -r0.z, l(0x7ef07ebb) +mul_sat r0.z, r0.z, |r16.z| +mul r0.z, r0.z, r0.z +mul r0.z, r4.w, r0.z +mad r0.z, r11.y, r4.w, r0.z +mul r14.xy, r0.xyxx, r14.yxyy +add r17.xyz, -r12.wwxw, r12.zxyz +max r15.xw, |r15.xxxw|, |r17.xxxz| +iadd r15.xw, -r15.xxxw, l(0x7ef07ebb, 0, 0, 0x7ef07ebb) +mul r11.y, r14.x, r15.z +mad r11.y, r15.y, r4.w, r11.y +mul_sat r12.z, r15.x, |r15.z| +mul r12.z, r12.z, r12.z +mad r0.z, r12.z, r14.x, r0.z +add r15.xy, -r0.wwww, r12.wxww +max r0.w, |r15.x|, |r17.y| +iadd r0.w, -r0.w, l(0x7ef07ebb) +mul r12.z, r14.x, r15.y +mad r4.w, r16.z, r4.w, r12.z +mul_sat r0.w, r0.w, |r15.y| +mul r0.w, r0.w, r0.w +mad r0.z, r0.w, r14.x, r0.z +add r12.yz, -r11.wwzw, r12.xxyx +mad r0.w, r12.y, r14.y, r11.y +mul_sat r11.y, r13.w, |r12.y| +mul r11.y, r11.y, r11.y +mad r0.z, r11.y, r14.y, r0.z +add r11.xy, -r11.zxzz, r13.yyyy +max r11.x, |r16.y|, |r11.x| +iadd r11.x, -r11.x, l(0x7ef07ebb) +mad r4.w, r11.y, r14.y, r4.w +mul_sat r11.x, r11.x, |r11.y| +mul r11.x, r11.x, r11.x +mad r0.z, r11.x, r14.y, r0.z +mul r11.x, r0.y, r0.x +mad r15.y, r12.z, r11.x, r0.w +mul_sat r0.w, r15.w, |r12.z| +mul r0.w, r0.w, r0.w +mad r0.z, r0.w, r11.x, r0.z +add r11.yz, -r12.xxwx, r13.xxxx +max r0.w, |r17.y|, |r11.y| +iadd r0.w, -r0.w, l(0x7ef07ebb) +mad r15.z, r11.z, r11.x, r4.w +mul_sat r0.w, r0.w, |r11.z| +mul r0.w, r0.w, r0.w +mad r0.z, r0.w, r11.x, r0.z +mul r11.xy, r15.yzyy, r15.yzyy +add r0.w, r11.y, r11.x +lt r4.w, r0.w, l(0.000031) +ushr r0.w, r0.w, l(1) +iadd r0.w, -r0.w, l(0x5f347d74) +movc r0.w, r4.w, l(1.000000), r0.w +movc r15.x, r4.w, l(1.000000), r15.y +mul r11.xy, r0.wwww, r15.xzxx +mul r0.z, r0.z, l(0.500000) +mul r0.z, r0.z, r0.z +dp2 r0.w, r11.xyxx, r11.xyxx +max r4.w, |r11.y|, |r11.x| +iadd r4.w, -r4.w, l(0x7ef07ebb) +mad r0.w, r0.w, r4.w, l(-1.000000) +mad r12.x, r0.w, r0.z, l(1.000000) +mad r12.yz, r0.zzzz, l(0.000000, -0.500000, -0.290000, 0.000000), l(0.000000, 1.000000, 0.500000, 0.000000) +iadd r0.z, -r12.z, l(0x7ef07ebb) +mov r13.x, r8.w +mov r13.y, r9.w +mov r13.z, r2.w +mov r15.x, r5.y +mov r15.y, r6.y +mov r15.z, r7.x +min r16.xyz, r13.xyzx, r15.xyzx +mov r17.x, r5.z +mov r17.y, r6.z +mov r17.z, r7.y +min r16.xyz, r16.xyzx, r17.xyzx +mov r18.x, r8.x +mov r18.y, r9.x +mov r18.z, r2.x +min r16.xyz, r16.xyzx, r18.xyzx +max r19.xyz, r13.xyzx, r15.xyzx +max r19.xyz, r17.xyzx, r19.xyzx +max r19.xyz, r18.xyzx, r19.xyzx +dp2 r14.x, r14.zwzz, r11.xyxx +mul r0.w, r11.x, r14.w +mad r14.y, r14.z, -r11.y, r0.w +mul r5.yz, r12.xxyx, r14.xxyx +dp2 r0.w, r5.yzyy, r5.yzyy +min r0.w, r0.z, r0.w +mad r2.w, r0.w, l(0.400000), l(-1.000000) +mad r0.w, r12.z, r0.w, l(-1.000000) +mul r2.w, r2.w, r2.w +mul r0.w, r0.w, r0.w +mad r2.w, r2.w, l(1.562500), l(-0.562500) +mul r4.w, r0.w, r2.w +mov r14.x, r1.z +mov r14.y, r3.x +mov r14.z, r4.x +add r20.xyzw, -r0.xyxy, l(1.000000, -1.000000, -1.000000, 1.000000) +dp2 r21.y, r20.xyxx, r11.xyxx +mul r5.yz, r11.xxxx, r20.yywy +mad r21.zw, r20.xxxz, -r11.yyyy, r5.yyyz +mul r5.yz, r12.xxyx, r21.yyzy +dp2 r1.z, r5.yzyy, r5.yzyy +min r1.z, r0.z, r1.z +mad r3.x, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r3.x, r3.x, r3.x +mul r1.z, r1.z, r1.z +mad r3.x, r3.x, l(1.562500), l(-0.562500) +mul r1.z, r1.z, r3.x +mov r4.x, r1.w +mov r4.y, r3.y +mul r4.xyz, r1.zzzz, r4.xyzx +mad r4.xyz, r14.xyzx, r4.wwww, r4.xyzx +mad r0.w, r2.w, r0.w, r1.z +dp2 r21.x, r20.zwzz, r11.xyxx +mul r1.zw, r12.xxxy, r21.xxxw +dp2 r1.z, r1.zwzz, r1.zwzz +min r1.z, r0.z, r1.z +mad r1.w, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r1.zw, r1.zzzw, r1.zzzw +mad r1.w, r1.w, l(1.562500), l(-0.562500) +mul r2.w, r1.z, r1.w +mov r7.x, r5.x +mov r7.y, r6.x +mad r4.xyz, r7.xyzx, r2.wwww, r4.xyzx +mad r0.w, r1.w, r1.z, r0.w +add r14.xyzw, -r0.xyxy, l(0.000000, 1.000000, -1.000000, 0.000000) +dp2 r20.y, r14.xyxx, r11.xyxx +mul r1.zw, r11.xxxx, r14.yyyw +mad r20.zw, r14.xxxz, -r11.yyyy, r1.zzzw +mul r1.zw, r12.xxxy, r20.yyyz +dp2 r1.z, r1.zwzz, r1.zwzz +min r1.z, r0.z, r1.z +mad r1.w, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r1.zw, r1.zzzw, r1.zzzw +mad r1.w, r1.w, l(1.562500), l(-0.562500) +mul r2.w, r1.z, r1.w +mad r4.xyz, r15.xyzx, r2.wwww, r4.xyzx +mad r0.w, r1.w, r1.z, r0.w +dp2 r3.x, -r0.xyxx, r11.xyxx +mul r1.z, -r0.y, r11.x +mad r3.y, r0.x, r11.y, r1.z +mul r1.zw, r12.xxxy, r3.xxxy +dp2 r1.z, r1.zwzz, r1.zwzz +min r1.z, r0.z, r1.z +mad r1.w, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r1.zw, r1.zzzw, r1.zzzw +mad r1.w, r1.w, l(1.562500), l(-0.562500) +mul r2.w, r1.z, r1.w +mad r4.xyz, r17.xyzx, r2.wwww, r4.xyzx +mad r0.w, r1.w, r1.z, r0.w +dp2 r20.x, r14.zwzz, r11.xyxx +mul r1.zw, r12.xxxy, r20.xxxw +dp2 r1.z, r1.zwzz, r1.zwzz +min r1.z, r0.z, r1.z +mad r1.w, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r1.zw, r1.zzzw, r1.zzzw +mad r1.w, r1.w, l(1.562500), l(-0.562500) +mul r2.w, r1.z, r1.w +mov r7.z, r5.w +mov r7.x, r6.w +mad r4.xyz, r7.zxwz, r2.wwww, r4.xyzx +mad r0.w, r1.w, r1.z, r0.w +add r5.xyzw, -r0.xyxy, l(1.000000, 1.000000, 2.000000, 1.000000) +dp2 r6.y, r5.xyxx, r11.xyxx +mul r1.zw, r11.xxxx, r5.yyyw +mad r6.zw, r5.xxxz, -r11.yyyy, r1.zzzw +mul r1.zw, r12.xxxy, r6.yyyz +dp2 r1.z, r1.zwzz, r1.zwzz +min r1.z, r0.z, r1.z +mad r1.w, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r1.zw, r1.zzzw, r1.zzzw +mad r1.w, r1.w, l(1.562500), l(-0.562500) +mul r2.w, r1.z, r1.w +mad r4.xyz, r18.xyzx, r2.wwww, r4.xyzx +mad r0.w, r1.w, r1.z, r0.w +dp2 r6.x, r5.zwzz, r11.xyxx +mul r1.zw, r12.xxxy, r6.xxxw +dp2 r1.z, r1.zwzz, r1.zwzz +min r1.z, r0.z, r1.z +mad r1.w, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r1.zw, r1.zzzw, r1.zzzw +mad r1.w, r1.w, l(1.562500), l(-0.562500) +mul r2.w, r1.z, r1.w +mov r5.x, r8.y +mov r5.y, r9.y +mov r5.z, r2.y +mad r4.xyz, r5.xyzx, r2.wwww, r4.xyzx +mad r0.w, r1.w, r1.z, r0.w +add r5.xyzw, -r0.xyxy, l(2.000000, 0.000000, 1.000000, 0.000000) +dp2 r6.y, r5.xyxx, r11.xyxx +mul r1.zw, r11.xxxx, r5.yyyw +mad r6.zw, r5.xxxz, -r11.yyyy, r1.zzzw +mul r1.zw, r12.xxxy, r6.yyyz +dp2 r1.z, r1.zwzz, r1.zwzz +min r1.z, r0.z, r1.z +mad r1.w, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r1.zw, r1.zzzw, r1.zzzw +mad r1.w, r1.w, l(1.562500), l(-0.562500) +mul r2.w, r1.z, r1.w +mov r2.x, r8.z +mov r2.y, r9.z +mad r2.xyz, r2.xyzx, r2.wwww, r4.xyzx +mad r0.w, r1.w, r1.z, r0.w +dp2 r6.x, r5.zwzz, r11.xyxx +mul r1.zw, r12.xxxy, r6.xxxw +dp2 r1.z, r1.zwzz, r1.zwzz +min r1.z, r0.z, r1.z +mad r1.w, r1.z, l(0.400000), l(-1.000000) +mad r1.z, r12.z, r1.z, l(-1.000000) +mul r1.zw, r1.zzzw, r1.zzzw +mad r1.w, r1.w, l(1.562500), l(-0.562500) +mul r2.w, r1.z, r1.w +mad r2.xyz, r13.xyzx, r2.wwww, r2.xyzx +mad r0.w, r1.w, r1.z, r0.w +add r4.xyzw, -r0.xyxy, l(1.000000, 2.000000, 0.000000, 2.000000) +dp2 r5.y, r4.xyxx, r11.xyxx +mul r0.xy, r11.xxxx, r4.ywyy +mad r5.zw, r4.xxxz, -r11.yyyy, r0.xxxy +mul r0.xy, r12.xyxx, r5.yzyy +dp2 r0.x, r0.xyxx, r0.xyxx +min r0.x, r0.z, r0.x +mad r0.y, r0.x, l(0.400000), l(-1.000000) +mad r0.x, r12.z, r0.x, l(-1.000000) +mul r0.xy, r0.xyxx, r0.xyxx +mad r0.y, r0.y, l(1.562500), l(-0.562500) +mul r1.z, r0.x, r0.y +mov r10.x, r1.x +mov r10.y, r3.z +mad r1.xzw, r10.xxyz, r1.zzzz, r2.xxyz +mad r0.x, r0.y, r0.x, r0.w +dp2 r5.x, r4.zwzz, r11.xyxx +mul r0.yw, r12.xxxy, r5.xxxw +dp2 r0.y, r0.ywyy, r0.ywyy +min r0.y, r0.z, r0.y +mad r0.z, r0.y, l(0.400000), l(-1.000000) +mad r0.y, r12.z, r0.y, l(-1.000000) +mul r0.yz, r0.yyzy, r0.yyzy +mad r0.z, r0.z, l(1.562500), l(-0.562500) +mul r0.w, r0.y, r0.z +mov r10.x, r1.y +mov r10.y, r3.w +mad r1.xyz, r10.xywx, r0.wwww, r1.xzwx +mad r0.x, r0.z, r0.y, r0.x +rcp r0.x, r0.x +mul r0.xyz, r0.xxxx, r1.xyzx +max r0.xyz, r0.xyzx, r16.xyzx +min o0.xyz, r0.xyzx, r19.xyzx +mov o0.w, l(1.000000) +ret +// Approximately 299 instruction slots used +#endif + +const BYTE guest_output_ffx_fsr_easu_ps[] = +{ + 68, 88, 66, 67, 240, 45, + 22, 153, 167, 152, 10, 67, + 48, 29, 75, 57, 104, 138, + 197, 43, 1, 0, 0, 0, + 236, 40, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 36, 2, 0, 0, 88, 2, + 0, 0, 140, 2, 0, 0, + 80, 40, 0, 0, 82, 68, + 69, 70, 232, 1, 0, 0, + 1, 0, 0, 0, 236, 0, + 0, 0, 3, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 255, 255, 0, 5, 0, 0, + 191, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 180, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 204, 0, + 0, 0, 2, 0, 0, 0, + 5, 0, 0, 0, 4, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 215, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 120, 101, 95, 115, 97, 109, + 112, 108, 101, 114, 95, 108, + 105, 110, 101, 97, 114, 95, + 99, 108, 97, 109, 112, 0, + 120, 101, 95, 116, 101, 120, + 116, 117, 114, 101, 0, 88, + 101, 70, 115, 114, 69, 97, + 115, 117, 67, 111, 110, 115, + 116, 97, 110, 116, 115, 0, + 171, 171, 215, 0, 0, 0, + 2, 0, 0, 0, 4, 1, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 84, 1, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 2, 0, 0, 0, + 128, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 164, 1, 0, 0, 8, 0, + 0, 0, 8, 0, 0, 0, + 2, 0, 0, 0, 128, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 120, 101, + 95, 102, 115, 114, 95, 101, + 97, 115, 117, 95, 105, 110, + 112, 117, 116, 95, 111, 117, + 116, 112, 117, 116, 95, 115, + 105, 122, 101, 95, 114, 97, + 116, 105, 111, 0, 102, 108, + 111, 97, 116, 50, 0, 171, + 1, 0, 3, 0, 1, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 120, 1, 0, 0, + 120, 101, 95, 102, 115, 114, + 95, 101, 97, 115, 117, 95, + 105, 110, 112, 117, 116, 95, + 115, 105, 122, 101, 95, 105, + 110, 118, 0, 77, 105, 99, + 114, 111, 115, 111, 102, 116, + 32, 40, 82, 41, 32, 72, + 76, 83, 76, 32, 83, 104, + 97, 100, 101, 114, 32, 67, + 111, 109, 112, 105, 108, 101, + 114, 32, 49, 48, 46, 49, + 0, 171, 73, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 3, 0, 0, + 83, 86, 95, 80, 111, 115, + 105, 116, 105, 111, 110, 0, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 84, 97, 114, 103, 101, + 116, 0, 171, 171, 83, 72, + 69, 88, 188, 37, 0, 0, + 81, 0, 0, 0, 111, 9, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 90, 0, + 0, 6, 70, 110, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 88, 24, + 0, 7, 70, 126, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 0, 0, + 0, 0, 100, 32, 0, 4, + 50, 16, 16, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 101, 0, 0, 3, 242, 32, + 16, 0, 0, 0, 0, 0, + 104, 0, 0, 2, 22, 0, + 0, 0, 56, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 128, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 64, 50, 0, 0, 17, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 128, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 191, 0, 0, + 0, 191, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 12, 114, 0, 16, 0, + 1, 0, 0, 0, 230, 139, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 128, 63, 0, 0, + 128, 63, 0, 0, 128, 191, + 0, 0, 0, 0, 28, 0, + 0, 5, 50, 0, 16, 0, + 2, 0, 0, 0, 70, 16, + 16, 0, 0, 0, 0, 0, + 86, 0, 0, 5, 50, 0, + 16, 0, 2, 0, 0, 0, + 70, 0, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 11, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 2, 0, 0, 0, 70, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 65, 0, + 0, 5, 50, 0, 16, 0, + 2, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 128, + 65, 0, 0, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 50, 0, 16, 0, 1, 0, + 0, 0, 70, 0, 16, 0, + 2, 0, 0, 0, 70, 0, + 16, 0, 1, 0, 0, 0, + 134, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 14, + 242, 0, 16, 0, 2, 0, + 0, 0, 230, 142, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 191, 0, 0, 0, 64, + 0, 0, 128, 63, 0, 0, + 0, 64, 70, 4, 16, 0, + 1, 0, 0, 0, 54, 0, + 0, 5, 66, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 194, 0, + 16, 0, 0, 0, 0, 0, + 166, 14, 16, 0, 0, 0, + 0, 0, 6, 4, 16, 0, + 1, 0, 0, 0, 109, 0, + 0, 11, 194, 0, 16, 0, + 1, 0, 0, 0, 70, 0, + 16, 0, 1, 0, 0, 0, + 230, 116, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 10, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 109, 0, 0, 11, 50, 0, + 16, 0, 3, 0, 0, 0, + 70, 0, 16, 0, 1, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 26, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 109, 0, 0, 11, + 82, 0, 16, 0, 4, 0, + 0, 0, 70, 0, 16, 0, + 1, 0, 0, 0, 134, 125, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 109, 0, + 0, 11, 242, 0, 16, 0, + 5, 0, 0, 0, 70, 0, + 16, 0, 2, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 10, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 109, 0, 0, 11, 242, 0, + 16, 0, 6, 0, 0, 0, + 70, 0, 16, 0, 2, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 26, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 109, 0, 0, 11, + 242, 0, 16, 0, 7, 0, + 0, 0, 70, 0, 16, 0, + 2, 0, 0, 0, 150, 124, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 109, 0, + 0, 11, 242, 0, 16, 0, + 8, 0, 0, 0, 230, 10, + 16, 0, 2, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 10, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 109, 0, 0, 11, 242, 0, + 16, 0, 9, 0, 0, 0, + 230, 10, 16, 0, 2, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 26, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 109, 0, 0, 11, + 242, 0, 16, 0, 2, 0, + 0, 0, 230, 10, 16, 0, + 2, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 109, 0, + 0, 11, 50, 0, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 0, 0, 0, 0, + 230, 116, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 10, 96, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 109, 0, 0, 11, 194, 0, + 16, 0, 3, 0, 0, 0, + 230, 10, 16, 0, 0, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 26, 96, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 109, 0, 0, 11, + 194, 0, 16, 0, 10, 0, + 0, 0, 230, 10, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 96, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 12, 194, 0, 16, 0, + 0, 0, 0, 0, 166, 14, + 16, 0, 1, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 6, 4, 16, 0, + 3, 0, 0, 0, 50, 0, + 0, 12, 194, 0, 16, 0, + 0, 0, 0, 0, 6, 8, + 16, 0, 4, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 166, 14, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 12, 242, 0, 16, 0, + 11, 0, 0, 0, 230, 1, + 16, 0, 5, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 63, 0, 0, + 0, 63, 230, 1, 16, 0, + 6, 0, 0, 0, 50, 0, + 0, 12, 242, 0, 16, 0, + 11, 0, 0, 0, 214, 8, + 16, 0, 7, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 63, 0, 0, + 0, 63, 70, 14, 16, 0, + 11, 0, 0, 0, 50, 0, + 0, 12, 242, 0, 16, 0, + 12, 0, 0, 0, 70, 14, + 16, 0, 8, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 63, 0, 0, + 0, 63, 70, 14, 16, 0, + 9, 0, 0, 0, 50, 0, + 0, 12, 242, 0, 16, 0, + 12, 0, 0, 0, 70, 14, + 16, 0, 2, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 63, 0, 0, + 0, 63, 70, 14, 16, 0, + 12, 0, 0, 0, 50, 0, + 0, 12, 50, 0, 16, 0, + 13, 0, 0, 0, 70, 0, + 16, 0, 1, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 0, 0, 0, + 0, 0, 230, 10, 16, 0, + 3, 0, 0, 0, 50, 0, + 0, 12, 50, 0, 16, 0, + 13, 0, 0, 0, 230, 10, + 16, 0, 10, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 63, + 0, 0, 0, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 13, 0, 0, 0, 0, 0, + 0, 11, 242, 0, 16, 0, + 14, 0, 0, 0, 70, 4, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 0, 0, 0, 0, 128, 191, + 56, 0, 0, 7, 130, 0, + 16, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 14, 0, + 0, 0, 10, 0, 16, 0, + 14, 0, 0, 0, 0, 0, + 0, 8, 242, 0, 16, 0, + 15, 0, 0, 0, 70, 8, + 16, 128, 65, 0, 0, 0, + 11, 0, 0, 0, 246, 2, + 16, 0, 12, 0, 0, 0, + 0, 0, 0, 8, 114, 0, + 16, 0, 16, 0, 0, 0, + 22, 7, 16, 128, 65, 0, + 0, 0, 11, 0, 0, 0, + 134, 2, 16, 0, 11, 0, + 0, 0, 52, 0, 0, 9, + 194, 0, 16, 0, 13, 0, + 0, 0, 6, 12, 16, 128, + 129, 0, 0, 0, 15, 0, + 0, 0, 6, 8, 16, 128, + 129, 0, 0, 0, 16, 0, + 0, 0, 30, 0, 0, 11, + 194, 0, 16, 0, 13, 0, + 0, 0, 166, 14, 16, 128, + 65, 0, 0, 0, 13, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 187, 126, 240, 126, + 187, 126, 240, 126, 56, 32, + 0, 8, 34, 0, 16, 0, + 11, 0, 0, 0, 42, 0, + 16, 0, 13, 0, 0, 0, + 26, 0, 16, 128, 129, 0, + 0, 0, 15, 0, 0, 0, + 56, 0, 0, 7, 34, 0, + 16, 0, 11, 0, 0, 0, + 26, 0, 16, 0, 11, 0, + 0, 0, 26, 0, 16, 0, + 11, 0, 0, 0, 0, 0, + 0, 8, 82, 0, 16, 0, + 16, 0, 0, 0, 166, 10, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 6, 2, + 16, 0, 11, 0, 0, 0, + 52, 0, 0, 9, 66, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 128, 129, 0, + 0, 0, 16, 0, 0, 0, + 26, 0, 16, 128, 129, 0, + 0, 0, 16, 0, 0, 0, + 30, 0, 0, 8, 66, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 56, 32, 0, 8, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 128, 129, 0, 0, 0, + 16, 0, 0, 0, 56, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 66, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 11, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 50, 0, + 16, 0, 14, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 22, 5, 16, 0, + 14, 0, 0, 0, 0, 0, + 0, 8, 114, 0, 16, 0, + 17, 0, 0, 0, 246, 12, + 16, 128, 65, 0, 0, 0, + 12, 0, 0, 0, 38, 9, + 16, 0, 12, 0, 0, 0, + 52, 0, 0, 9, 146, 0, + 16, 0, 15, 0, 0, 0, + 6, 12, 16, 128, 129, 0, + 0, 0, 15, 0, 0, 0, + 6, 8, 16, 128, 129, 0, + 0, 0, 17, 0, 0, 0, + 30, 0, 0, 11, 146, 0, + 16, 0, 15, 0, 0, 0, + 6, 12, 16, 128, 65, 0, + 0, 0, 15, 0, 0, 0, + 2, 64, 0, 0, 187, 126, + 240, 126, 0, 0, 0, 0, + 0, 0, 0, 0, 187, 126, + 240, 126, 56, 0, 0, 7, + 34, 0, 16, 0, 11, 0, + 0, 0, 10, 0, 16, 0, + 14, 0, 0, 0, 42, 0, + 16, 0, 15, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 11, 0, 0, 0, + 26, 0, 16, 0, 15, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 11, 0, 0, 0, + 56, 32, 0, 8, 66, 0, + 16, 0, 12, 0, 0, 0, + 10, 0, 16, 0, 15, 0, + 0, 0, 42, 0, 16, 128, + 129, 0, 0, 0, 15, 0, + 0, 0, 56, 0, 0, 7, + 66, 0, 16, 0, 12, 0, + 0, 0, 42, 0, 16, 0, + 12, 0, 0, 0, 42, 0, + 16, 0, 12, 0, 0, 0, + 50, 0, 0, 9, 66, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 12, 0, + 0, 0, 10, 0, 16, 0, + 14, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 50, 0, + 16, 0, 15, 0, 0, 0, + 246, 15, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 54, 15, 16, 0, 12, 0, + 0, 0, 52, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 128, + 129, 0, 0, 0, 15, 0, + 0, 0, 26, 0, 16, 128, + 129, 0, 0, 0, 17, 0, + 0, 0, 30, 0, 0, 8, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 187, 126, 240, 126, 56, 0, + 0, 7, 66, 0, 16, 0, + 12, 0, 0, 0, 10, 0, + 16, 0, 14, 0, 0, 0, + 26, 0, 16, 0, 15, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 4, 0, + 0, 0, 42, 0, 16, 0, + 16, 0, 0, 0, 58, 0, + 16, 0, 4, 0, 0, 0, + 42, 0, 16, 0, 12, 0, + 0, 0, 56, 32, 0, 8, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 128, 129, 0, 0, 0, + 15, 0, 0, 0, 56, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 9, + 66, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 14, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 8, + 98, 0, 16, 0, 12, 0, + 0, 0, 246, 14, 16, 128, + 65, 0, 0, 0, 11, 0, + 0, 0, 6, 1, 16, 0, + 12, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 12, 0, 0, 0, + 26, 0, 16, 0, 14, 0, + 0, 0, 26, 0, 16, 0, + 11, 0, 0, 0, 56, 32, + 0, 8, 34, 0, 16, 0, + 11, 0, 0, 0, 58, 0, + 16, 0, 13, 0, 0, 0, + 26, 0, 16, 128, 129, 0, + 0, 0, 12, 0, 0, 0, + 56, 0, 0, 7, 34, 0, + 16, 0, 11, 0, 0, 0, + 26, 0, 16, 0, 11, 0, + 0, 0, 26, 0, 16, 0, + 11, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 11, 0, 0, 0, + 26, 0, 16, 0, 14, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 8, 50, 0, 16, 0, + 11, 0, 0, 0, 38, 10, + 16, 128, 65, 0, 0, 0, + 11, 0, 0, 0, 86, 5, + 16, 0, 13, 0, 0, 0, + 52, 0, 0, 9, 18, 0, + 16, 0, 11, 0, 0, 0, + 26, 0, 16, 128, 129, 0, + 0, 0, 16, 0, 0, 0, + 10, 0, 16, 128, 129, 0, + 0, 0, 11, 0, 0, 0, + 30, 0, 0, 8, 18, 0, + 16, 0, 11, 0, 0, 0, + 10, 0, 16, 128, 65, 0, + 0, 0, 11, 0, 0, 0, + 1, 64, 0, 0, 187, 126, + 240, 126, 50, 0, 0, 9, + 130, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 0, + 11, 0, 0, 0, 26, 0, + 16, 0, 14, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 56, 32, 0, 8, + 18, 0, 16, 0, 11, 0, + 0, 0, 10, 0, 16, 0, + 11, 0, 0, 0, 26, 0, + 16, 128, 129, 0, 0, 0, + 11, 0, 0, 0, 56, 0, + 0, 7, 18, 0, 16, 0, + 11, 0, 0, 0, 10, 0, + 16, 0, 11, 0, 0, 0, + 10, 0, 16, 0, 11, 0, + 0, 0, 50, 0, 0, 9, + 66, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 11, 0, 0, 0, 26, 0, + 16, 0, 14, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 18, 0, 16, 0, 11, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 15, 0, 0, 0, + 42, 0, 16, 0, 12, 0, + 0, 0, 10, 0, 16, 0, + 11, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 56, 32, 0, 8, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 15, 0, + 0, 0, 42, 0, 16, 128, + 129, 0, 0, 0, 12, 0, + 0, 0, 56, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 66, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 11, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 98, 0, + 16, 0, 11, 0, 0, 0, + 6, 3, 16, 128, 65, 0, + 0, 0, 12, 0, 0, 0, + 6, 0, 16, 0, 13, 0, + 0, 0, 52, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 128, + 129, 0, 0, 0, 17, 0, + 0, 0, 26, 0, 16, 128, + 129, 0, 0, 0, 11, 0, + 0, 0, 30, 0, 0, 8, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 187, 126, 240, 126, 50, 0, + 0, 9, 66, 0, 16, 0, + 15, 0, 0, 0, 42, 0, + 16, 0, 11, 0, 0, 0, + 10, 0, 16, 0, 11, 0, + 0, 0, 58, 0, 16, 0, + 4, 0, 0, 0, 56, 32, + 0, 8, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 128, 129, 0, + 0, 0, 11, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 11, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 56, 0, + 0, 7, 50, 0, 16, 0, + 11, 0, 0, 0, 150, 5, + 16, 0, 15, 0, 0, 0, + 150, 5, 16, 0, 15, 0, + 0, 0, 0, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 11, 0, 0, 0, 10, 0, + 16, 0, 11, 0, 0, 0, + 49, 0, 0, 7, 130, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 56, 85, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 1, 0, + 0, 0, 30, 0, 0, 8, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 116, 125, 52, 95, 55, 0, + 0, 9, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 58, 0, 16, 0, + 0, 0, 0, 0, 55, 0, + 0, 9, 18, 0, 16, 0, + 15, 0, 0, 0, 58, 0, + 16, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 26, 0, 16, 0, + 15, 0, 0, 0, 56, 0, + 0, 7, 50, 0, 16, 0, + 11, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 134, 0, 16, 0, 15, 0, + 0, 0, 56, 0, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 63, + 56, 0, 0, 7, 66, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 15, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 11, 0, 0, 0, + 70, 0, 16, 0, 11, 0, + 0, 0, 52, 0, 0, 9, + 130, 0, 16, 0, 4, 0, + 0, 0, 26, 0, 16, 128, + 129, 0, 0, 0, 11, 0, + 0, 0, 10, 0, 16, 128, + 129, 0, 0, 0, 11, 0, + 0, 0, 30, 0, 0, 8, + 130, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 4, 0, + 0, 0, 1, 64, 0, 0, + 187, 126, 240, 126, 50, 0, + 0, 9, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 191, 50, 0, + 0, 9, 18, 0, 16, 0, + 12, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 50, 0, + 0, 15, 98, 0, 16, 0, + 12, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 191, + 225, 122, 148, 190, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 128, 63, 0, 0, 0, 63, + 0, 0, 0, 0, 30, 0, + 0, 8, 66, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 128, 65, 0, 0, 0, + 12, 0, 0, 0, 1, 64, + 0, 0, 187, 126, 240, 126, + 54, 0, 0, 5, 18, 0, + 16, 0, 13, 0, 0, 0, + 58, 0, 16, 0, 8, 0, + 0, 0, 54, 0, 0, 5, + 34, 0, 16, 0, 13, 0, + 0, 0, 58, 0, 16, 0, + 9, 0, 0, 0, 54, 0, + 0, 5, 66, 0, 16, 0, + 13, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 54, 0, 0, 5, 18, 0, + 16, 0, 15, 0, 0, 0, + 26, 0, 16, 0, 5, 0, + 0, 0, 54, 0, 0, 5, + 34, 0, 16, 0, 15, 0, + 0, 0, 26, 0, 16, 0, + 6, 0, 0, 0, 54, 0, + 0, 5, 66, 0, 16, 0, + 15, 0, 0, 0, 10, 0, + 16, 0, 7, 0, 0, 0, + 51, 0, 0, 7, 114, 0, + 16, 0, 16, 0, 0, 0, + 70, 2, 16, 0, 13, 0, + 0, 0, 70, 2, 16, 0, + 15, 0, 0, 0, 54, 0, + 0, 5, 18, 0, 16, 0, + 17, 0, 0, 0, 42, 0, + 16, 0, 5, 0, 0, 0, + 54, 0, 0, 5, 34, 0, + 16, 0, 17, 0, 0, 0, + 42, 0, 16, 0, 6, 0, + 0, 0, 54, 0, 0, 5, + 66, 0, 16, 0, 17, 0, + 0, 0, 26, 0, 16, 0, + 7, 0, 0, 0, 51, 0, + 0, 7, 114, 0, 16, 0, + 16, 0, 0, 0, 70, 2, + 16, 0, 16, 0, 0, 0, + 70, 2, 16, 0, 17, 0, + 0, 0, 54, 0, 0, 5, + 18, 0, 16, 0, 18, 0, + 0, 0, 10, 0, 16, 0, + 8, 0, 0, 0, 54, 0, + 0, 5, 34, 0, 16, 0, + 18, 0, 0, 0, 10, 0, + 16, 0, 9, 0, 0, 0, + 54, 0, 0, 5, 66, 0, + 16, 0, 18, 0, 0, 0, + 10, 0, 16, 0, 2, 0, + 0, 0, 51, 0, 0, 7, + 114, 0, 16, 0, 16, 0, + 0, 0, 70, 2, 16, 0, + 16, 0, 0, 0, 70, 2, + 16, 0, 18, 0, 0, 0, + 52, 0, 0, 7, 114, 0, + 16, 0, 19, 0, 0, 0, + 70, 2, 16, 0, 13, 0, + 0, 0, 70, 2, 16, 0, + 15, 0, 0, 0, 52, 0, + 0, 7, 114, 0, 16, 0, + 19, 0, 0, 0, 70, 2, + 16, 0, 17, 0, 0, 0, + 70, 2, 16, 0, 19, 0, + 0, 0, 52, 0, 0, 7, + 114, 0, 16, 0, 19, 0, + 0, 0, 70, 2, 16, 0, + 18, 0, 0, 0, 70, 2, + 16, 0, 19, 0, 0, 0, + 15, 0, 0, 7, 18, 0, + 16, 0, 14, 0, 0, 0, + 230, 10, 16, 0, 14, 0, + 0, 0, 70, 0, 16, 0, + 11, 0, 0, 0, 56, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 11, 0, 0, 0, + 58, 0, 16, 0, 14, 0, + 0, 0, 50, 0, 0, 10, + 34, 0, 16, 0, 14, 0, + 0, 0, 42, 0, 16, 0, + 14, 0, 0, 0, 26, 0, + 16, 128, 65, 0, 0, 0, + 11, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 98, 0, + 16, 0, 5, 0, 0, 0, + 6, 1, 16, 0, 12, 0, + 0, 0, 6, 1, 16, 0, + 14, 0, 0, 0, 15, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 150, 5, + 16, 0, 5, 0, 0, 0, + 150, 5, 16, 0, 5, 0, + 0, 0, 51, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 205, 204, 204, 62, 1, 64, + 0, 0, 0, 0, 128, 191, + 50, 0, 0, 9, 130, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 12, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 191, + 56, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 56, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 200, 63, + 1, 64, 0, 0, 0, 0, + 16, 191, 56, 0, 0, 7, + 130, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 54, 0, 0, 5, 18, 0, + 16, 0, 14, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 54, 0, 0, 5, + 34, 0, 16, 0, 14, 0, + 0, 0, 10, 0, 16, 0, + 3, 0, 0, 0, 54, 0, + 0, 5, 66, 0, 16, 0, + 14, 0, 0, 0, 10, 0, + 16, 0, 4, 0, 0, 0, + 0, 0, 0, 11, 242, 0, + 16, 0, 20, 0, 0, 0, + 70, 4, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 63, 0, 0, 128, 191, + 0, 0, 128, 191, 0, 0, + 128, 63, 15, 0, 0, 7, + 34, 0, 16, 0, 21, 0, + 0, 0, 70, 0, 16, 0, + 20, 0, 0, 0, 70, 0, + 16, 0, 11, 0, 0, 0, + 56, 0, 0, 7, 98, 0, + 16, 0, 5, 0, 0, 0, + 6, 0, 16, 0, 11, 0, + 0, 0, 86, 7, 16, 0, + 20, 0, 0, 0, 50, 0, + 0, 10, 194, 0, 16, 0, + 21, 0, 0, 0, 6, 8, + 16, 0, 20, 0, 0, 0, + 86, 5, 16, 128, 65, 0, + 0, 0, 11, 0, 0, 0, + 86, 9, 16, 0, 5, 0, + 0, 0, 56, 0, 0, 7, + 98, 0, 16, 0, 5, 0, + 0, 0, 6, 1, 16, 0, + 12, 0, 0, 0, 86, 6, + 16, 0, 21, 0, 0, 0, + 15, 0, 0, 7, 66, 0, + 16, 0, 1, 0, 0, 0, + 150, 5, 16, 0, 5, 0, + 0, 0, 150, 5, 16, 0, + 5, 0, 0, 0, 51, 0, + 0, 7, 66, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 18, 0, 16, 0, 3, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 205, 204, 204, 62, + 1, 64, 0, 0, 0, 0, + 128, 191, 50, 0, 0, 9, + 66, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 12, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 191, 56, 0, 0, 7, + 18, 0, 16, 0, 3, 0, + 0, 0, 10, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 0, 3, 0, 0, 0, + 56, 0, 0, 7, 66, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 18, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 0, 3, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 200, 63, 1, 64, 0, 0, + 0, 0, 16, 191, 56, 0, + 0, 7, 66, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 10, 0, 16, 0, 3, 0, + 0, 0, 54, 0, 0, 5, + 18, 0, 16, 0, 4, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 54, 0, + 0, 5, 34, 0, 16, 0, + 4, 0, 0, 0, 26, 0, + 16, 0, 3, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 4, 0, 0, 0, + 166, 10, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 0, 14, 0, 0, 0, + 246, 15, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 15, 0, + 0, 7, 18, 0, 16, 0, + 21, 0, 0, 0, 230, 10, + 16, 0, 20, 0, 0, 0, + 70, 0, 16, 0, 11, 0, + 0, 0, 56, 0, 0, 7, + 194, 0, 16, 0, 1, 0, + 0, 0, 6, 4, 16, 0, + 12, 0, 0, 0, 6, 12, + 16, 0, 21, 0, 0, 0, + 15, 0, 0, 7, 66, 0, + 16, 0, 1, 0, 0, 0, + 230, 10, 16, 0, 1, 0, + 0, 0, 230, 10, 16, 0, + 1, 0, 0, 0, 51, 0, + 0, 7, 66, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 205, 204, 204, 62, + 1, 64, 0, 0, 0, 0, + 128, 191, 50, 0, 0, 9, + 66, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 12, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 191, 56, 0, 0, 7, + 194, 0, 16, 0, 1, 0, + 0, 0, 166, 14, 16, 0, + 1, 0, 0, 0, 166, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 200, 63, 1, 64, + 0, 0, 0, 0, 16, 191, + 56, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 54, 0, + 0, 5, 18, 0, 16, 0, + 7, 0, 0, 0, 10, 0, + 16, 0, 5, 0, 0, 0, + 54, 0, 0, 5, 34, 0, + 16, 0, 7, 0, 0, 0, + 10, 0, 16, 0, 6, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 7, 0, 0, 0, 246, 15, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 11, + 242, 0, 16, 0, 14, 0, + 0, 0, 70, 4, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 128, 63, 0, 0, 128, 191, + 0, 0, 0, 0, 15, 0, + 0, 7, 34, 0, 16, 0, + 20, 0, 0, 0, 70, 0, + 16, 0, 14, 0, 0, 0, + 70, 0, 16, 0, 11, 0, + 0, 0, 56, 0, 0, 7, + 194, 0, 16, 0, 1, 0, + 0, 0, 6, 0, 16, 0, + 11, 0, 0, 0, 86, 13, + 16, 0, 14, 0, 0, 0, + 50, 0, 0, 10, 194, 0, + 16, 0, 20, 0, 0, 0, + 6, 8, 16, 0, 14, 0, + 0, 0, 86, 5, 16, 128, + 65, 0, 0, 0, 11, 0, + 0, 0, 166, 14, 16, 0, + 1, 0, 0, 0, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 6, 4, + 16, 0, 12, 0, 0, 0, + 86, 9, 16, 0, 20, 0, + 0, 0, 15, 0, 0, 7, + 66, 0, 16, 0, 1, 0, + 0, 0, 230, 10, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 51, 0, 0, 7, 66, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 205, 204, + 204, 62, 1, 64, 0, 0, + 0, 0, 128, 191, 50, 0, + 0, 9, 66, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 12, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 191, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 166, 14, + 16, 0, 1, 0, 0, 0, + 166, 14, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 200, 63, + 1, 64, 0, 0, 0, 0, + 16, 191, 56, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 9, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 0, 15, 0, + 0, 0, 246, 15, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 15, 0, 0, 8, 18, 0, + 16, 0, 3, 0, 0, 0, + 70, 0, 16, 128, 65, 0, + 0, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 11, 0, + 0, 0, 56, 0, 0, 8, + 66, 0, 16, 0, 1, 0, + 0, 0, 26, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 11, 0, 0, 0, 50, 0, + 0, 9, 34, 0, 16, 0, + 3, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 11, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 6, 4, + 16, 0, 12, 0, 0, 0, + 6, 4, 16, 0, 3, 0, + 0, 0, 15, 0, 0, 7, + 66, 0, 16, 0, 1, 0, + 0, 0, 230, 10, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 51, 0, 0, 7, 66, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 205, 204, + 204, 62, 1, 64, 0, 0, + 0, 0, 128, 191, 50, 0, + 0, 9, 66, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 12, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 191, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 166, 14, + 16, 0, 1, 0, 0, 0, + 166, 14, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 200, 63, + 1, 64, 0, 0, 0, 0, + 16, 191, 56, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 9, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 0, 17, 0, + 0, 0, 246, 15, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 15, 0, 0, 7, 18, 0, + 16, 0, 20, 0, 0, 0, + 230, 10, 16, 0, 14, 0, + 0, 0, 70, 0, 16, 0, + 11, 0, 0, 0, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 6, 4, + 16, 0, 12, 0, 0, 0, + 6, 12, 16, 0, 20, 0, + 0, 0, 15, 0, 0, 7, + 66, 0, 16, 0, 1, 0, + 0, 0, 230, 10, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 51, 0, 0, 7, 66, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 205, 204, + 204, 62, 1, 64, 0, 0, + 0, 0, 128, 191, 50, 0, + 0, 9, 66, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 12, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 191, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 166, 14, + 16, 0, 1, 0, 0, 0, + 166, 14, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 200, 63, + 1, 64, 0, 0, 0, 0, + 16, 191, 56, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 54, 0, 0, 5, 66, 0, + 16, 0, 7, 0, 0, 0, + 58, 0, 16, 0, 5, 0, + 0, 0, 54, 0, 0, 5, + 18, 0, 16, 0, 7, 0, + 0, 0, 58, 0, 16, 0, + 6, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 4, 0, 0, 0, 38, 11, + 16, 0, 7, 0, 0, 0, + 246, 15, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 11, 242, 0, 16, 0, + 5, 0, 0, 0, 70, 4, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 0, 64, 0, 0, 128, 63, + 15, 0, 0, 7, 34, 0, + 16, 0, 6, 0, 0, 0, + 70, 0, 16, 0, 5, 0, + 0, 0, 70, 0, 16, 0, + 11, 0, 0, 0, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 6, 0, + 16, 0, 11, 0, 0, 0, + 86, 13, 16, 0, 5, 0, + 0, 0, 50, 0, 0, 10, + 194, 0, 16, 0, 6, 0, + 0, 0, 6, 8, 16, 0, + 5, 0, 0, 0, 86, 5, + 16, 128, 65, 0, 0, 0, + 11, 0, 0, 0, 166, 14, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 7, 194, 0, + 16, 0, 1, 0, 0, 0, + 6, 4, 16, 0, 12, 0, + 0, 0, 86, 9, 16, 0, + 6, 0, 0, 0, 15, 0, + 0, 7, 66, 0, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 230, 10, 16, 0, 1, 0, + 0, 0, 51, 0, 0, 7, + 66, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 205, 204, 204, 62, 1, 64, + 0, 0, 0, 0, 128, 191, + 50, 0, 0, 9, 66, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 12, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 191, + 56, 0, 0, 7, 194, 0, + 16, 0, 1, 0, 0, 0, + 166, 14, 16, 0, 1, 0, + 0, 0, 166, 14, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 200, 63, 1, 64, 0, 0, + 0, 0, 16, 191, 56, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 18, 0, 0, 0, 246, 15, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 15, 0, 0, 7, + 18, 0, 16, 0, 6, 0, + 0, 0, 230, 10, 16, 0, + 5, 0, 0, 0, 70, 0, + 16, 0, 11, 0, 0, 0, + 56, 0, 0, 7, 194, 0, + 16, 0, 1, 0, 0, 0, + 6, 4, 16, 0, 12, 0, + 0, 0, 6, 12, 16, 0, + 6, 0, 0, 0, 15, 0, + 0, 7, 66, 0, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 230, 10, 16, 0, 1, 0, + 0, 0, 51, 0, 0, 7, + 66, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 205, 204, 204, 62, 1, 64, + 0, 0, 0, 0, 128, 191, + 50, 0, 0, 9, 66, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 12, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 191, + 56, 0, 0, 7, 194, 0, + 16, 0, 1, 0, 0, 0, + 166, 14, 16, 0, 1, 0, + 0, 0, 166, 14, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 200, 63, 1, 64, 0, 0, + 0, 0, 16, 191, 56, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 54, 0, 0, 5, + 18, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 0, + 8, 0, 0, 0, 54, 0, + 0, 5, 34, 0, 16, 0, + 5, 0, 0, 0, 26, 0, + 16, 0, 9, 0, 0, 0, + 54, 0, 0, 5, 66, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 0, 2, 0, + 0, 0, 50, 0, 0, 9, + 114, 0, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 246, 15, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 11, + 242, 0, 16, 0, 5, 0, + 0, 0, 70, 4, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 64, 0, 0, + 0, 0, 0, 0, 128, 63, + 0, 0, 0, 0, 15, 0, + 0, 7, 34, 0, 16, 0, + 6, 0, 0, 0, 70, 0, + 16, 0, 5, 0, 0, 0, + 70, 0, 16, 0, 11, 0, + 0, 0, 56, 0, 0, 7, + 194, 0, 16, 0, 1, 0, + 0, 0, 6, 0, 16, 0, + 11, 0, 0, 0, 86, 13, + 16, 0, 5, 0, 0, 0, + 50, 0, 0, 10, 194, 0, + 16, 0, 6, 0, 0, 0, + 6, 8, 16, 0, 5, 0, + 0, 0, 86, 5, 16, 128, + 65, 0, 0, 0, 11, 0, + 0, 0, 166, 14, 16, 0, + 1, 0, 0, 0, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 6, 4, + 16, 0, 12, 0, 0, 0, + 86, 9, 16, 0, 6, 0, + 0, 0, 15, 0, 0, 7, + 66, 0, 16, 0, 1, 0, + 0, 0, 230, 10, 16, 0, + 1, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 51, 0, 0, 7, 66, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 205, 204, + 204, 62, 1, 64, 0, 0, + 0, 0, 128, 191, 50, 0, + 0, 9, 66, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 12, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 191, 56, 0, + 0, 7, 194, 0, 16, 0, + 1, 0, 0, 0, 166, 14, + 16, 0, 1, 0, 0, 0, + 166, 14, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 200, 63, + 1, 64, 0, 0, 0, 0, + 16, 191, 56, 0, 0, 7, + 130, 0, 16, 0, 2, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 54, 0, 0, 5, 18, 0, + 16, 0, 2, 0, 0, 0, + 42, 0, 16, 0, 8, 0, + 0, 0, 54, 0, 0, 5, + 34, 0, 16, 0, 2, 0, + 0, 0, 42, 0, 16, 0, + 9, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 246, 15, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 15, 0, + 0, 7, 18, 0, 16, 0, + 6, 0, 0, 0, 230, 10, + 16, 0, 5, 0, 0, 0, + 70, 0, 16, 0, 11, 0, + 0, 0, 56, 0, 0, 7, + 194, 0, 16, 0, 1, 0, + 0, 0, 6, 4, 16, 0, + 12, 0, 0, 0, 6, 12, + 16, 0, 6, 0, 0, 0, + 15, 0, 0, 7, 66, 0, + 16, 0, 1, 0, 0, 0, + 230, 10, 16, 0, 1, 0, + 0, 0, 230, 10, 16, 0, + 1, 0, 0, 0, 51, 0, + 0, 7, 66, 0, 16, 0, + 1, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 205, 204, 204, 62, + 1, 64, 0, 0, 0, 0, + 128, 191, 50, 0, 0, 9, + 66, 0, 16, 0, 1, 0, + 0, 0, 42, 0, 16, 0, + 12, 0, 0, 0, 42, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 191, 56, 0, 0, 7, + 194, 0, 16, 0, 1, 0, + 0, 0, 166, 14, 16, 0, + 1, 0, 0, 0, 166, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 9, 130, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 200, 63, 1, 64, + 0, 0, 0, 0, 16, 191, + 56, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 13, 0, 0, 0, + 246, 15, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 50, 0, + 0, 9, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 42, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 11, 242, 0, 16, 0, + 4, 0, 0, 0, 70, 4, + 16, 128, 65, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 0, 64, 0, 0, + 0, 0, 0, 0, 0, 64, + 15, 0, 0, 7, 34, 0, + 16, 0, 5, 0, 0, 0, + 70, 0, 16, 0, 4, 0, + 0, 0, 70, 0, 16, 0, + 11, 0, 0, 0, 56, 0, + 0, 7, 50, 0, 16, 0, + 0, 0, 0, 0, 6, 0, + 16, 0, 11, 0, 0, 0, + 214, 5, 16, 0, 4, 0, + 0, 0, 50, 0, 0, 10, + 194, 0, 16, 0, 5, 0, + 0, 0, 6, 8, 16, 0, + 4, 0, 0, 0, 86, 5, + 16, 128, 65, 0, 0, 0, + 11, 0, 0, 0, 6, 4, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 12, 0, + 0, 0, 150, 5, 16, 0, + 5, 0, 0, 0, 15, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 51, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 9, 34, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 205, 204, 204, 62, 1, 64, + 0, 0, 0, 0, 128, 191, + 50, 0, 0, 9, 18, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 12, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 191, + 56, 0, 0, 7, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 34, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 200, 63, 1, 64, 0, 0, + 0, 0, 16, 191, 56, 0, + 0, 7, 66, 0, 16, 0, + 1, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 18, 0, 16, 0, 10, 0, + 0, 0, 10, 0, 16, 0, + 1, 0, 0, 0, 54, 0, + 0, 5, 34, 0, 16, 0, + 10, 0, 0, 0, 42, 0, + 16, 0, 3, 0, 0, 0, + 50, 0, 0, 9, 210, 0, + 16, 0, 1, 0, 0, 0, + 6, 9, 16, 0, 10, 0, + 0, 0, 166, 10, 16, 0, + 1, 0, 0, 0, 6, 9, + 16, 0, 2, 0, 0, 0, + 50, 0, 0, 9, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 15, 0, 0, 7, 18, 0, + 16, 0, 5, 0, 0, 0, + 230, 10, 16, 0, 4, 0, + 0, 0, 70, 0, 16, 0, + 11, 0, 0, 0, 56, 0, + 0, 7, 162, 0, 16, 0, + 0, 0, 0, 0, 6, 4, + 16, 0, 12, 0, 0, 0, + 6, 12, 16, 0, 5, 0, + 0, 0, 15, 0, 0, 7, + 34, 0, 16, 0, 0, 0, + 0, 0, 214, 5, 16, 0, + 0, 0, 0, 0, 214, 5, + 16, 0, 0, 0, 0, 0, + 51, 0, 0, 7, 34, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 66, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 205, 204, + 204, 62, 1, 64, 0, 0, + 0, 0, 128, 191, 50, 0, + 0, 9, 34, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 12, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 191, 56, 0, + 0, 7, 98, 0, 16, 0, + 0, 0, 0, 0, 86, 6, + 16, 0, 0, 0, 0, 0, + 86, 6, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 9, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 200, 63, + 1, 64, 0, 0, 0, 0, + 16, 191, 56, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 54, 0, 0, 5, 18, 0, + 16, 0, 10, 0, 0, 0, + 26, 0, 16, 0, 1, 0, + 0, 0, 54, 0, 0, 5, + 34, 0, 16, 0, 10, 0, + 0, 0, 58, 0, 16, 0, + 3, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 3, + 16, 0, 10, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 134, 3, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 18, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 129, 0, + 0, 5, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 0, 0, 0, 0, + 6, 0, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 52, 0, + 0, 7, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 16, 0, + 0, 0, 51, 0, 0, 7, + 114, 32, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 19, 0, 0, 0, + 54, 0, 0, 5, 130, 32, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 62, 0, 0, 1, + 83, 84, 65, 84, 148, 0, + 0, 0, 43, 1, 0, 0, + 22, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 239, 0, 0, 0, 9, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_dither_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_dither_ps.h new file mode 100644 index 000000000..71dd6c93b --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_dither_ps.h @@ -0,0 +1,1457 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeFsrRcasConstants +// { +// +// int2 xe_fsr_rcas_output_offset; // Offset: 0 Size: 8 +// float xe_fsr_rcas_sharpness_post_setup;// Offset: 8 Size: 4 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_texture texture float3 2d T0 t0 1 +// XeFsrRcasConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_5_1 +dcl_globalFlags refactoringAllowed +dcl_immediateConstantBuffer { { -0.001003, 0, 0, 0}, + { 0.000881, 0, 0, 0}, + { 0.001693, 0, 0, 0}, + { -0.001555, 0, 0, 0}, + { 0.001279, 0, 0, 0}, + { -0.000605, 0, 0, 0}, + { 0.001065, 0, 0, 0}, + { -0.001386, 0, 0, 0}, + { 0.001356, 0, 0, 0}, + { 0.000513, 0, 0, 0}, + { 0.001218, 0, 0, 0}, + { -0.001601, 0, 0, 0}, + { 0.000590, 0, 0, 0}, + { -0.000283, 0, 0, 0}, + { 0.001111, 0, 0, 0}, + { -0.001417, 0, 0, 0}, + { 0.001448, 0, 0, 0}, + { -0.000544, 0, 0, 0}, + { 0.000130, 0, 0, 0}, + { -0.001203, 0, 0, 0}, + { 0.000437, 0, 0, 0}, + { -0.001049, 0, 0, 0}, + { 0.000620, 0, 0, 0}, + { -0.000483, 0, 0, 0}, + { 0.001877, 0, 0, 0}, + { -0.001095, 0, 0, 0}, + { -0.000100, 0, 0, 0}, + { -0.000528, 0, 0, 0}, + { 0.001432, 0, 0, 0}, + { -0.001938, 0, 0, 0}, + { -0.000697, 0, 0, 0}, + { 0.000038, 0, 0, 0}, + { 0.000712, 0, 0, 0}, + { -0.001310, 0, 0, 0}, + { 0.001095, 0, 0, 0}, + { -0.000299, 0, 0, 0}, + { 0.001754, 0, 0, 0}, + { -0.001677, 0, 0, 0}, + { 0.001478, 0, 0, 0}, + { -0.000038, 0, 0, 0}, + { 0.000789, 0, 0, 0}, + { -0.001831, 0, 0, 0}, + { 0.000299, 0, 0, 0}, + { 0.000988, 0, 0, 0}, + { -0.001172, 0, 0, 0}, + { 0.000176, 0, 0, 0}, + { 0.001647, 0, 0, 0}, + { -0.001585, 0, 0, 0}, + { 0.000345, 0, 0, 0}, + { 0.001861, 0, 0, 0}, + { -0.001769, 0, 0, 0}, + { -0.000866, 0, 0, 0}, + { 0.000896, 0, 0, 0}, + { 0.000161, 0, 0, 0}, + { -0.000927, 0, 0, 0}, + { -0.001524, 0, 0, 0}, + { -0.000651, 0, 0, 0}, + { 0.001294, 0, 0, 0}, + { -0.000804, 0, 0, 0}, + { -0.001463, 0, 0, 0}, + { 0.001800, 0, 0, 0}, + { -0.000850, 0, 0, 0}, + { 0.000850, 0, 0, 0}, + { -0.000452, 0, 0, 0}, + { -0.001065, 0, 0, 0}, + { -0.000146, 0, 0, 0}, + { 0.000237, 0, 0, 0}, + { 0.001417, 0, 0, 0}, + { -0.000590, 0, 0, 0}, + { -0.000191, 0, 0, 0}, + { 0.001601, 0, 0, 0}, + { 0.001019, 0, 0, 0}, + { 0.000406, 0, 0, 0}, + { -0.000207, 0, 0, 0}, + { 0.001585, 0, 0, 0}, + { 0.000651, 0, 0, 0}, + { -0.000069, 0, 0, 0}, + { 0.000421, 0, 0, 0}, + { -0.001647, 0, 0, 0}, + { 0.001371, 0, 0, 0}, + { 0.000927, 0, 0, 0}, + { -0.000666, 0, 0, 0}, + { 0.001187, 0, 0, 0}, + { -0.001448, 0, 0, 0}, + { 0.000574, 0, 0, 0}, + { -0.001892, 0, 0, 0}, + { 0.000758, 0, 0, 0}, + { -0.001294, 0, 0, 0}, + { 0.001922, 0, 0, 0}, + { -0.001662, 0, 0, 0}, + { -0.001034, 0, 0, 0}, + { -0.000498, 0, 0, 0}, + { -0.001861, 0, 0, 0}, + { 0.001203, 0, 0, 0}, + { -0.000329, 0, 0, 0}, + { -0.001371, 0, 0, 0}, + { 0.001631, 0, 0, 0}, + { -0.001846, 0, 0, 0}, + { 0.000728, 0, 0, 0}, + { -0.000911, 0, 0, 0}, + { 0.001815, 0, 0, 0}, + { -0.001141, 0, 0, 0}, + { -0.000375, 0, 0, 0}, + { 0.000100, 0, 0, 0}, + { -0.000743, 0, 0, 0}, + { 0.001172, 0, 0, 0}, + { 0.000069, 0, 0, 0}, + { 0.001494, 0, 0, 0}, + { 0.000973, 0, 0, 0}, + { -0.000957, 0, 0, 0}, + { 0.001938, 0, 0, 0}, + { 0.000528, 0, 0, 0}, + { 0.000054, 0, 0, 0}, + { -0.001248, 0, 0, 0}, + { -0.000268, 0, 0, 0}, + { 0.001540, 0, 0, 0}, + { -0.000008, 0, 0, 0}, + { 0.000314, 0, 0, 0}, + { 0.001340, 0, 0, 0}, + { -0.001754, 0, 0, 0}, + { 0.000498, 0, 0, 0}, + { -0.001187, 0, 0, 0}, + { 0.000774, 0, 0, 0}, + { -0.001340, 0, 0, 0}, + { 0.000268, 0, 0, 0}, + { -0.001478, 0, 0, 0}, + { -0.000130, 0, 0, 0}, + { -0.000774, 0, 0, 0}, + { 0.001310, 0, 0, 0}, + { 0.000391, 0, 0, 0}, + { 0.000957, 0, 0, 0}, + { -0.000467, 0, 0, 0}, + { -0.001540, 0, 0, 0}, + { 0.001034, 0, 0, 0}, + { -0.000682, 0, 0, 0}, + { 0.001677, 0, 0, 0}, + { 0.001003, 0, 0, 0}, + { -0.000421, 0, 0, 0}, + { 0.001785, 0, 0, 0}, + { -0.000237, 0, 0, 0}, + { -0.000620, 0, 0, 0}, + { 0.001662, 0, 0, 0}, + { 0.000835, 0, 0, 0}, + { -0.001723, 0, 0, 0}, + { -0.001080, 0, 0, 0}, + { 0.001769, 0, 0, 0}, + { -0.000789, 0, 0, 0}, + { -0.001785, 0, 0, 0}, + { 0.000682, 0, 0, 0}, + { -0.000988, 0, 0, 0}, + { -0.001325, 0, 0, 0}, + { -0.000176, 0, 0, 0}, + { -0.001509, 0, 0, 0}, + { 0.000329, 0, 0, 0}, + { -0.001953, 0, 0, 0}, + { 0.000666, 0, 0, 0}, + { -0.001616, 0, 0, 0}, + { 0.001157, 0, 0, 0}, + { 0.000452, 0, 0, 0}, + { -0.000437, 0, 0, 0}, + { 0.000191, 0, 0, 0}, + { -0.001494, 0, 0, 0}, + { 0.001141, 0, 0, 0}, + { 0.000084, 0, 0, 0}, + { 0.001892, 0, 0, 0}, + { 0.001402, 0, 0, 0}, + { 0.000559, 0, 0, 0}, + { 0.000115, 0, 0, 0}, + { 0.001264, 0, 0, 0}, + { -0.000574, 0, 0, 0}, + { -0.000973, 0, 0, 0}, + { 0.001325, 0, 0, 0}, + { 0.000222, 0, 0, 0}, + { -0.000758, 0, 0, 0}, + { -0.001356, 0, 0, 0}, + { 0.001463, 0, 0, 0}, + { 0.000866, 0, 0, 0}, + { -0.000360, 0, 0, 0}, + { 0.000544, 0, 0, 0}, + { -0.001126, 0, 0, 0}, + { -0.000253, 0, 0, 0}, + { -0.000559, 0, 0, 0}, + { -0.001815, 0, 0, 0}, + { 0.001723, 0, 0, 0}, + { -0.001157, 0, 0, 0}, + { 0.000743, 0, 0, 0}, + { 0.001570, 0, 0, 0}, + { -0.000115, 0, 0, 0}, + { -0.001218, 0, 0, 0}, + { 0.001831, 0, 0, 0}, + { 0.000023, 0, 0, 0}, + { -0.001922, 0, 0, 0}, + { 0.001739, 0, 0, 0}, + { -0.000712, 0, 0, 0}, + { 0.001555, 0, 0, 0}, + { -0.001708, 0, 0, 0}, + { 0.001233, 0, 0, 0}, + { 0.000207, 0, 0, 0}, + { 0.001049, 0, 0, 0}, + { -0.000728, 0, 0, 0}, + { -0.001631, 0, 0, 0}, + { -0.000314, 0, 0, 0}, + { 0.000483, 0, 0, 0}, + { -0.001800, 0, 0, 0}, + { 0.000942, 0, 0, 0}, + { -0.000345, 0, 0, 0}, + { 0.000697, 0, 0, 0}, + { -0.001019, 0, 0, 0}, + { -0.001570, 0, 0, 0}, + { -0.000023, 0, 0, 0}, + { -0.001279, 0, 0, 0}, + { 0.000804, 0, 0, 0}, + { -0.000896, 0, 0, 0}, + { -0.001432, 0, 0, 0}, + { 0.000605, 0, 0, 0}, + { -0.000084, 0, 0, 0}, + { 0.000911, 0, 0, 0}, + { 0.001953, 0, 0, 0}, + { -0.001402, 0, 0, 0}, + { -0.000636, 0, 0, 0}, + { 0.001509, 0, 0, 0}, + { -0.000820, 0, 0, 0}, + { 0.001248, 0, 0, 0}, + { 0.000253, 0, 0, 0}, + { 0.001524, 0, 0, 0}, + { 0.001126, 0, 0, 0}, + { 0.000360, 0, 0, 0}, + { -0.000391, 0, 0, 0}, + { 0.001907, 0, 0, 0}, + { 0.001386, 0, 0, 0}, + { -0.001111, 0, 0, 0}, + { 0.001616, 0, 0, 0}, + { -0.000881, 0, 0, 0}, + { 0.000146, 0, 0, 0}, + { 0.001080, 0, 0, 0}, + { -0.000054, 0, 0, 0}, + { 0.000283, 0, 0, 0}, + { -0.001693, 0, 0, 0}, + { -0.001264, 0, 0, 0}, + { -0.000513, 0, 0, 0}, + { -0.000161, 0, 0, 0}, + { -0.001877, 0, 0, 0}, + { -0.000835, 0, 0, 0}, + { 0.000636, 0, 0, 0}, + { 0.000008, 0, 0, 0}, + { -0.001907, 0, 0, 0}, + { -0.000222, 0, 0, 0}, + { 0.000375, 0, 0, 0}, + { -0.001739, 0, 0, 0}, + { -0.000406, 0, 0, 0}, + { -0.001233, 0, 0, 0}, + { 0.001708, 0, 0, 0}, + { -0.000942, 0, 0, 0}, + { 0.000820, 0, 0, 0}, + { 0.001846, 0, 0, 0}, + { 0.000467, 0, 0, 0} } +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_input_ps_siv linear noperspective v0.xy, position +dcl_output o0.xyzw +dcl_temps 10 +ftoi r0.xy, v0.xyxx +iadd r0.xy, r0.xyxx, -CB0[0][0].xyxx +iadd r1.xyzw, r0.xyxy, l(-1, 0, 0, -1) +mov r2.xy, r1.zwzz +mov r2.zw, l(0,0,0,0) +ld r2.xyz, r2.xyzw, T0[0].xyzw +mov r1.zw, l(0,0,0,0) +ld r1.xyz, r1.xyzw, T0[0].xyzw +mov r0.zw, l(0,0,0,0) +ld r3.xyz, r0.xyzw, T0[0].xyzw +iadd r4.xyzw, r0.xyxy, l(0, 1, 1, 0) +mov r5.xy, r4.zwzz +mov r5.zw, l(0,0,0,0) +ld r5.xyz, r5.xyzw, T0[0].xyzw +mov r4.zw, l(0,0,0,0) +ld r4.xyz, r4.xyzw, T0[0].xyzw +min r6.xyz, r1.xyzx, r5.xyzx +min r6.xyz, r2.xyzx, r6.xyzx +min r6.xyz, r4.xyzx, r6.xyzx +max r7.xyz, r1.xyzx, r5.xyzx +max r7.xyz, r2.xyzx, r7.xyzx +max r7.xyz, r4.xyzx, r7.xyzx +min r8.xyz, r3.xyzx, r6.xyzx +mul r9.xyz, r7.xyzx, l(4.000000, 4.000000, 4.000000, 0.000000) +rcp r9.xyz, r9.xyzx +mul r8.xyz, r8.xyzx, r9.xyzx +max r7.xyz, r3.xyzx, r7.xyzx +add r7.xyz, -r7.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000) +mad r6.xyz, r6.xyzx, l(4.000000, 4.000000, 4.000000, 0.000000), l(-4.000000, -4.000000, -4.000000, 0.000000) +rcp r6.xyz, r6.xyzx +mul r6.xyz, r6.xyzx, r7.xyzx +max r6.xyz, r6.xyzx, -r8.xyzx +max r0.z, r6.z, r6.y +max r0.z, r0.z, r6.x +min r0.z, r0.z, l(0.000000) +max r0.z, r0.z, l(-0.187500) +mul r0.z, r0.z, CB0[0][0].z +mad r0.w, r0.z, l(4.000000), l(1.000000) +iadd r1.w, -r0.w, l(0x7ef19fff) +mad r0.w, -r1.w, r0.w, l(2.000000) +mul r0.w, r0.w, r1.w +mul r1.xyz, r1.xyzx, r0.zzzz +mad r1.xyz, r0.zzzz, r2.xyzx, r1.xyzx +mad r1.xyz, r0.zzzz, r4.xyzx, r1.xyzx +mad r1.xyz, r0.zzzz, r5.xyzx, r1.xyzx +add r1.xyz, r3.xyzx, r1.xyzx +bfi r0.y, l(4), l(4), r0.y, l(0) +bfi r0.x, l(4), l(0), r0.x, r0.y +mad o0.xyz, r1.xyzx, r0.wwww, icb[r0.x + 0].xxxx +mov o0.w, l(1.000000) +ret +// Approximately 51 instruction slots used +#endif + +const BYTE guest_output_ffx_fsr_rcas_dither_ps[] = +{ + 68, 88, 66, 67, 171, 87, + 217, 222, 87, 57, 211, 199, + 141, 108, 123, 139, 108, 18, + 226, 124, 1, 0, 0, 0, + 192, 25, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 8, 2, 0, 0, 60, 2, + 0, 0, 112, 2, 0, 0, + 36, 25, 0, 0, 82, 68, + 69, 70, 204, 1, 0, 0, + 1, 0, 0, 0, 172, 0, + 0, 0, 2, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 255, 255, 0, 5, 0, 0, + 164, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 140, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 120, 101, 95, 116, + 101, 120, 116, 117, 114, 101, + 0, 88, 101, 70, 115, 114, + 82, 99, 97, 115, 67, 111, + 110, 115, 116, 97, 110, 116, + 115, 0, 171, 171, 151, 0, + 0, 0, 2, 0, 0, 0, + 196, 0, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 1, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 2, 0, + 0, 0, 52, 1, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 88, 1, 0, 0, + 8, 0, 0, 0, 4, 0, + 0, 0, 2, 0, 0, 0, + 128, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 120, 101, 95, 102, 115, 114, + 95, 114, 99, 97, 115, 95, + 111, 117, 116, 112, 117, 116, + 95, 111, 102, 102, 115, 101, + 116, 0, 105, 110, 116, 50, + 0, 171, 1, 0, 2, 0, + 1, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 46, 1, + 0, 0, 120, 101, 95, 102, + 115, 114, 95, 114, 99, 97, + 115, 95, 115, 104, 97, 114, + 112, 110, 101, 115, 115, 95, + 112, 111, 115, 116, 95, 115, + 101, 116, 117, 112, 0, 102, + 108, 111, 97, 116, 0, 171, + 0, 0, 3, 0, 1, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 121, 1, 0, 0, + 77, 105, 99, 114, 111, 115, + 111, 102, 116, 32, 40, 82, + 41, 32, 72, 76, 83, 76, + 32, 83, 104, 97, 100, 101, + 114, 32, 67, 111, 109, 112, + 105, 108, 101, 114, 32, 49, + 48, 46, 49, 0, 73, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 3, + 0, 0, 83, 86, 95, 80, + 111, 115, 105, 116, 105, 111, + 110, 0, 79, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 83, 86, 95, 84, 97, 114, + 103, 101, 116, 0, 171, 171, + 83, 72, 69, 88, 172, 22, + 0, 0, 81, 0, 0, 0, + 171, 5, 0, 0, 106, 8, + 0, 1, 53, 24, 0, 0, + 2, 4, 0, 0, 132, 131, + 131, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 231, 230, 102, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 222, 221, 221, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 204, 203, + 203, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 168, 167, 167, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 159, 158, 30, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 140, 139, + 139, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 182, 181, 181, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 178, 177, 177, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 135, 134, + 6, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 160, 159, 159, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 210, 209, 209, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 155, 154, + 26, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 149, 148, 148, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 146, 145, 145, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 186, 185, + 185, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 190, 189, 189, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 143, 142, 14, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 137, 136, + 8, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 158, 157, 157, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 229, 228, 228, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 138, 137, + 137, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 163, 162, 34, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 253, 252, 252, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 246, 245, + 245, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 144, 143, 143, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 209, 208, 208, 184, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 139, 138, + 10, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 188, 187, 187, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 254, 253, 253, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 183, 182, + 54, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 161, 160, 32, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 187, 186, 58, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 172, 171, + 171, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 144, 143, 143, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 157, 156, 156, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 230, 229, + 229, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 220, 219, 219, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 194, 193, 193, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 161, 160, + 32, 184, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 207, 206, 78, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 240, 239, 239, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 157, 156, + 156, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 130, 129, 129, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 154, 153, 153, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 185, 184, + 56, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 216, 215, 215, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 208, 207, 207, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 181, 180, + 180, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 244, 243, 243, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 232, 231, 231, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 227, 226, + 98, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 235, 234, 106, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 169, 168, 40, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 243, 242, + 114, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 200, 199, 199, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 171, 170, 42, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 170, 169, + 169, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 211, 210, 82, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 192, 191, 191, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 236, 235, + 235, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 223, 222, 94, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 223, 222, 94, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 236, + 236, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 140, 139, 139, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 153, 152, 24, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 249, 248, + 120, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 186, 185, 185, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 155, 154, 26, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 201, 200, + 72, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 210, 209, 209, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 134, 133, 133, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 213, 212, + 212, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 217, 216, 88, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 208, 207, 207, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 171, 170, + 42, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 145, 144, 144, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 221, 220, 220, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 215, + 215, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 180, 179, 179, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 243, 242, 114, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 175, 174, + 46, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 156, 155, 155, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 190, 189, 189, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 150, + 22, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 248, 247, 247, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 199, 198, 70, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 170, 169, + 169, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 252, 251, 251, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 218, 217, 217, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 136, 135, + 135, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 131, 130, 2, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 244, 243, 243, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 158, 157, + 157, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 173, 172, 172, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 180, 179, 179, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 214, 213, + 213, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 242, 241, 241, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 191, 190, 62, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 239, 238, + 110, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 238, 237, 237, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 150, 149, 149, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 197, 196, + 196, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 209, 208, 208, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 195, 194, 66, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 154, 153, + 153, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 145, 144, 144, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 196, 195, 195, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 254, + 126, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 251, 250, 122, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 254, 253, 253, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 139, 138, + 10, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 225, 224, 96, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 164, 163, 163, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 141, 140, + 140, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 202, 201, 201, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 129, 128, 0, 183, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 165, 164, + 164, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 176, 175, 175, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 230, 229, 229, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 131, 130, + 2, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 156, 155, 155, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 203, 202, 74, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 176, 175, + 175, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 141, 140, 140, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 194, 193, 193, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 137, 136, + 8, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 203, 202, 74, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 172, 171, 171, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 205, 204, + 204, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 251, 250, 122, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 245, 244, 244, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 202, 201, + 201, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 136, 135, 135, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 179, 178, 50, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 220, 219, + 219, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 132, 131, 131, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 221, 220, 220, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 234, 233, + 233, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 249, 248, 120, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 163, 162, 34, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 218, 217, + 217, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 219, 218, 90, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 226, 225, 225, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 142, 141, + 141, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 232, 231, 231, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 207, 206, 78, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 234, 233, + 233, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 179, 178, 50, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 130, 129, 129, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 174, 173, + 173, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 185, 184, 56, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 198, 197, 197, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 173, 172, + 172, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 187, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 175, 174, 46, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 212, 211, + 211, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 152, 151, 151, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 237, 236, 236, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 229, 228, + 228, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 201, 200, 72, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 196, 195, 195, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 150, 149, + 149, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 177, 176, 176, 56, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 248, 247, 247, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 184, 183, + 183, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 147, 146, 18, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 241, 240, 240, 56, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 166, 165, + 165, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 151, 150, 22, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 255, 254, 126, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 174, 173, + 173, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 233, 232, 104, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 199, 198, 70, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 178, 177, + 177, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 192, 191, 191, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 227, 226, 98, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 189, 188, + 188, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 143, 142, 14, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 148, 147, 147, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 133, 132, + 132, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 147, 146, 18, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 238, 237, 237, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 226, 225, + 225, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 152, 151, 151, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 195, 194, 66, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 206, 205, + 205, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 241, 240, 240, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 160, 159, 159, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 240, 239, + 239, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 193, 192, 192, 55, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 252, 251, 251, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 228, 227, + 227, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 187, 186, 58, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 204, 203, 203, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 224, 223, + 223, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 162, 161, 161, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 217, 216, 88, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 138, 137, + 137, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 191, 190, 62, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 214, 213, 213, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 165, 164, + 164, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 253, 252, 252, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 236, 235, 235, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 247, 246, + 118, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 181, 180, 180, 185, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 183, 182, 54, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 134, 133, + 133, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 206, 205, 205, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 193, 192, 192, 183, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 168, 167, + 167, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 211, 210, 82, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 235, 234, 106, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 188, 187, + 187, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 159, 158, 30, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 177, 176, 176, 184, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 239, 238, + 110, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 59, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 184, 183, 183, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 167, 166, + 38, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 198, 197, 197, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 215, 214, 86, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 164, 163, + 163, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 133, 132, 132, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 200, 199, 199, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 148, 147, + 147, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 189, 188, 188, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 205, 204, 204, 185, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 250, 249, + 249, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 182, 181, 181, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 146, 145, 145, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 212, 211, + 211, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 231, 230, 102, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 153, 152, 24, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 142, 141, + 141, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 225, 224, 96, 184, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 149, 148, 148, 57, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 222, 221, + 221, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 166, 165, 165, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 135, 134, 6, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 169, 168, + 40, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 246, 245, 245, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 219, 218, 90, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 167, 166, + 38, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 129, 128, 0, 55, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 250, 249, 249, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 233, 232, + 104, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 197, 196, 196, 57, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 228, 227, 227, 186, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 213, 212, + 212, 185, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 162, 161, 161, 186, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 224, 223, 223, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 247, 246, + 118, 186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 215, 214, 86, 58, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 242, 241, 241, 58, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 245, 244, + 244, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 89, 0, 0, 7, + 70, 142, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 88, 24, 0, 7, 70, 126, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 85, 85, 0, 0, + 0, 0, 0, 0, 100, 32, + 0, 4, 50, 16, 16, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 0, 0, + 0, 0, 104, 0, 0, 2, + 10, 0, 0, 0, 27, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 16, + 16, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 70, 128, 48, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 242, 0, 16, 0, + 1, 0, 0, 0, 70, 4, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 54, 0, 0, 5, + 50, 0, 16, 0, 2, 0, + 0, 0, 230, 10, 16, 0, + 1, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 14, 16, 0, 2, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 3, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 242, 0, 16, 0, + 4, 0, 0, 0, 70, 4, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 50, 0, 16, 0, 5, 0, + 0, 0, 230, 10, 16, 0, + 4, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 5, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 5, 0, 0, 0, + 70, 14, 16, 0, 5, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 4, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 4, 0, 0, 0, 70, 14, + 16, 0, 4, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 51, 0, 0, 7, 114, 0, + 16, 0, 6, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 51, 0, + 0, 7, 114, 0, 16, 0, + 6, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 6, 0, + 0, 0, 51, 0, 0, 7, + 114, 0, 16, 0, 6, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 0, 6, 0, 0, 0, + 52, 0, 0, 7, 114, 0, + 16, 0, 7, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 52, 0, + 0, 7, 114, 0, 16, 0, + 7, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 7, 0, + 0, 0, 52, 0, 0, 7, + 114, 0, 16, 0, 7, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 0, 7, 0, 0, 0, + 51, 0, 0, 7, 114, 0, + 16, 0, 8, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 6, 0, 0, 0, 56, 0, + 0, 10, 114, 0, 16, 0, + 9, 0, 0, 0, 70, 2, + 16, 0, 7, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 64, 0, 0, 128, 64, + 0, 0, 128, 64, 0, 0, + 0, 0, 129, 0, 0, 5, + 114, 0, 16, 0, 9, 0, + 0, 0, 70, 2, 16, 0, + 9, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 8, 0, 0, 0, 70, 2, + 16, 0, 8, 0, 0, 0, + 70, 2, 16, 0, 9, 0, + 0, 0, 52, 0, 0, 7, + 114, 0, 16, 0, 7, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 70, 2, + 16, 0, 7, 0, 0, 0, + 0, 0, 0, 11, 114, 0, + 16, 0, 7, 0, 0, 0, + 70, 2, 16, 128, 65, 0, + 0, 0, 7, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 63, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 0, 0, 50, 0, 0, 15, + 114, 0, 16, 0, 6, 0, + 0, 0, 70, 2, 16, 0, + 6, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 64, + 0, 0, 128, 64, 0, 0, + 128, 64, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 192, 0, 0, 128, 192, + 0, 0, 128, 192, 0, 0, + 0, 0, 129, 0, 0, 5, + 114, 0, 16, 0, 6, 0, + 0, 0, 70, 2, 16, 0, + 6, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 6, 0, 0, 0, 70, 2, + 16, 0, 6, 0, 0, 0, + 70, 2, 16, 0, 7, 0, + 0, 0, 52, 0, 0, 8, + 114, 0, 16, 0, 6, 0, + 0, 0, 70, 2, 16, 0, + 6, 0, 0, 0, 70, 2, + 16, 128, 65, 0, 0, 0, + 8, 0, 0, 0, 52, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 6, 0, 0, 0, + 26, 0, 16, 0, 6, 0, + 0, 0, 52, 0, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 6, 0, 0, 0, + 51, 0, 0, 7, 66, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 52, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 64, 190, 56, 0, 0, 9, + 66, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 42, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 64, + 1, 64, 0, 0, 0, 0, + 128, 63, 30, 0, 0, 8, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 255, 159, 241, 126, 50, 0, + 0, 10, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 56, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 166, 10, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 5, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 0, 0, + 0, 7, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 140, 0, 0, 11, + 34, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 4, 0, 0, 0, 1, 64, + 0, 0, 4, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 140, 0, + 0, 11, 18, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 4, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 10, 114, 32, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 246, 15, 16, 0, + 0, 0, 0, 0, 6, 144, + 144, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 130, 32, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 62, 0, 0, 1, 83, 84, + 65, 84, 148, 0, 0, 0, + 51, 0, 0, 0, 10, 0, + 0, 0, 0, 1, 0, 0, + 2, 0, 0, 0, 28, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_ps.h new file mode 100644 index 000000000..f1c967f88 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_ffx_fsr_rcas_ps.h @@ -0,0 +1,498 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeFsrRcasConstants +// { +// +// int2 xe_fsr_rcas_output_offset; // Offset: 0 Size: 8 +// float xe_fsr_rcas_sharpness_post_setup;// Offset: 8 Size: 4 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// xe_texture texture float3 2d T0 t0 1 +// XeFsrRcasConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0 +dcl_input_ps_siv linear noperspective v0.xy, position +dcl_output o0.xyzw +dcl_temps 9 +ftoi r0.xy, v0.xyxx +iadd r0.xy, r0.xyxx, -CB0[0][0].xyxx +iadd r1.xyzw, r0.xyxy, l(-1, 0, 0, -1) +mov r2.xy, r1.zwzz +mov r2.zw, l(0,0,0,0) +ld r2.xyz, r2.xyzw, T0[0].xyzw +mov r1.zw, l(0,0,0,0) +ld r1.xyz, r1.xyzw, T0[0].xyzw +mov r0.zw, l(0,0,0,0) +ld r3.xyz, r0.xyzw, T0[0].xyzw +iadd r0.xyzw, r0.xyxy, l(0, 1, 1, 0) +mov r4.xy, r0.zwzz +mov r4.zw, l(0,0,0,0) +ld r4.xyz, r4.xyzw, T0[0].xyzw +mov r0.zw, l(0,0,0,0) +ld r0.xyz, r0.xyzw, T0[0].xyzw +min r5.xyz, r1.xyzx, r4.xyzx +min r5.xyz, r2.xyzx, r5.xyzx +min r5.xyz, r0.xyzx, r5.xyzx +max r6.xyz, r1.xyzx, r4.xyzx +max r6.xyz, r2.xyzx, r6.xyzx +max r6.xyz, r0.xyzx, r6.xyzx +min r7.xyz, r3.xyzx, r5.xyzx +mul r8.xyz, r6.xyzx, l(4.000000, 4.000000, 4.000000, 0.000000) +rcp r8.xyz, r8.xyzx +mul r7.xyz, r7.xyzx, r8.xyzx +max r6.xyz, r3.xyzx, r6.xyzx +add r6.xyz, -r6.xyzx, l(1.000000, 1.000000, 1.000000, 0.000000) +mad r5.xyz, r5.xyzx, l(4.000000, 4.000000, 4.000000, 0.000000), l(-4.000000, -4.000000, -4.000000, 0.000000) +rcp r5.xyz, r5.xyzx +mul r5.xyz, r5.xyzx, r6.xyzx +max r5.xyz, r5.xyzx, -r7.xyzx +max r0.w, r5.z, r5.y +max r0.w, r0.w, r5.x +min r0.w, r0.w, l(0.000000) +max r0.w, r0.w, l(-0.187500) +mul r0.w, r0.w, CB0[0][0].z +mad r1.w, r0.w, l(4.000000), l(1.000000) +iadd r2.w, -r1.w, l(0x7ef19fff) +mad r1.w, -r2.w, r1.w, l(2.000000) +mul r1.w, r1.w, r2.w +mul r1.xyz, r1.xyzx, r0.wwww +mad r1.xyz, r0.wwww, r2.xyzx, r1.xyzx +mad r0.xyz, r0.wwww, r0.xyzx, r1.xyzx +mad r0.xyz, r0.wwww, r4.xyzx, r0.xyzx +add r0.xyz, r3.xyzx, r0.xyzx +mul o0.xyz, r1.wwww, r0.xyzx +mov o0.w, l(1.000000) +ret +// Approximately 49 instruction slots used +#endif + +const BYTE guest_output_ffx_fsr_rcas_ps[] = +{ + 68, 88, 66, 67, 185, 223, + 242, 110, 212, 184, 173, 198, + 168, 143, 147, 205, 178, 152, + 68, 191, 1, 0, 0, 0, + 84, 9, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 8, 2, 0, 0, 60, 2, + 0, 0, 112, 2, 0, 0, + 184, 8, 0, 0, 82, 68, + 69, 70, 204, 1, 0, 0, + 1, 0, 0, 0, 172, 0, + 0, 0, 2, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 255, 255, 0, 5, 0, 0, + 164, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 140, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 151, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 120, 101, 95, 116, + 101, 120, 116, 117, 114, 101, + 0, 88, 101, 70, 115, 114, + 82, 99, 97, 115, 67, 111, + 110, 115, 116, 97, 110, 116, + 115, 0, 171, 171, 151, 0, + 0, 0, 2, 0, 0, 0, + 196, 0, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 1, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 2, 0, + 0, 0, 52, 1, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 88, 1, 0, 0, + 8, 0, 0, 0, 4, 0, + 0, 0, 2, 0, 0, 0, + 128, 1, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 120, 101, 95, 102, 115, 114, + 95, 114, 99, 97, 115, 95, + 111, 117, 116, 112, 117, 116, + 95, 111, 102, 102, 115, 101, + 116, 0, 105, 110, 116, 50, + 0, 171, 1, 0, 2, 0, + 1, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 46, 1, + 0, 0, 120, 101, 95, 102, + 115, 114, 95, 114, 99, 97, + 115, 95, 115, 104, 97, 114, + 112, 110, 101, 115, 115, 95, + 112, 111, 115, 116, 95, 115, + 101, 116, 117, 112, 0, 102, + 108, 111, 97, 116, 0, 171, + 0, 0, 3, 0, 1, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 121, 1, 0, 0, + 77, 105, 99, 114, 111, 115, + 111, 102, 116, 32, 40, 82, + 41, 32, 72, 76, 83, 76, + 32, 83, 104, 97, 100, 101, + 114, 32, 67, 111, 109, 112, + 105, 108, 101, 114, 32, 49, + 48, 46, 49, 0, 73, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 3, + 0, 0, 83, 86, 95, 80, + 111, 115, 105, 116, 105, 111, + 110, 0, 79, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 83, 86, 95, 84, 97, 114, + 103, 101, 116, 0, 171, 171, + 83, 72, 69, 88, 64, 6, + 0, 0, 81, 0, 0, 0, + 144, 1, 0, 0, 106, 8, + 0, 1, 89, 0, 0, 7, + 70, 142, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 88, 24, 0, 7, 70, 126, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 85, 85, 0, 0, + 0, 0, 0, 0, 100, 32, + 0, 4, 50, 16, 16, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 0, 0, + 0, 0, 104, 0, 0, 2, + 9, 0, 0, 0, 27, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 16, + 16, 0, 0, 0, 0, 0, + 30, 0, 0, 10, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 70, 128, 48, 128, + 65, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 242, 0, 16, 0, + 1, 0, 0, 0, 70, 4, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 54, 0, 0, 5, + 50, 0, 16, 0, 2, 0, + 0, 0, 230, 10, 16, 0, + 1, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 14, 16, 0, 2, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 45, 0, 0, 8, + 114, 0, 16, 0, 3, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 70, 126, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 0, + 0, 10, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 4, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 50, 0, 16, 0, 4, 0, + 0, 0, 230, 10, 16, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 0, 16, 0, + 4, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 45, 0, 0, 8, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 14, 16, 0, 4, 0, + 0, 0, 70, 126, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 0, + 0, 8, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 70, 126, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 51, 0, 0, 7, 114, 0, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 51, 0, + 0, 7, 114, 0, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 5, 0, + 0, 0, 51, 0, 0, 7, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 52, 0, 0, 7, 114, 0, + 16, 0, 6, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 52, 0, + 0, 7, 114, 0, 16, 0, + 6, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 6, 0, + 0, 0, 52, 0, 0, 7, + 114, 0, 16, 0, 6, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 6, 0, 0, 0, + 51, 0, 0, 7, 114, 0, + 16, 0, 7, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 56, 0, + 0, 10, 114, 0, 16, 0, + 8, 0, 0, 0, 70, 2, + 16, 0, 6, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 64, 0, 0, 128, 64, + 0, 0, 128, 64, 0, 0, + 0, 0, 129, 0, 0, 5, + 114, 0, 16, 0, 8, 0, + 0, 0, 70, 2, 16, 0, + 8, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 7, 0, 0, 0, 70, 2, + 16, 0, 7, 0, 0, 0, + 70, 2, 16, 0, 8, 0, + 0, 0, 52, 0, 0, 7, + 114, 0, 16, 0, 6, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 70, 2, + 16, 0, 6, 0, 0, 0, + 0, 0, 0, 11, 114, 0, + 16, 0, 6, 0, 0, 0, + 70, 2, 16, 128, 65, 0, + 0, 0, 6, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 63, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 0, 0, 50, 0, 0, 15, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 64, + 0, 0, 128, 64, 0, 0, + 128, 64, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 192, 0, 0, 128, 192, + 0, 0, 128, 192, 0, 0, + 0, 0, 129, 0, 0, 5, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 56, 0, + 0, 7, 114, 0, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 6, 0, + 0, 0, 52, 0, 0, 8, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 128, 65, 0, 0, 0, + 7, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 5, 0, 0, 0, + 26, 0, 16, 0, 5, 0, + 0, 0, 52, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 5, 0, 0, 0, + 51, 0, 0, 7, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 64, 190, 56, 0, 0, 9, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 42, 128, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 50, 0, 0, 9, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 64, + 1, 64, 0, 0, 0, 0, + 128, 63, 30, 0, 0, 8, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 128, + 65, 0, 0, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 255, 159, 241, 126, 50, 0, + 0, 10, 130, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 128, 65, 0, 0, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 56, 0, 0, 7, + 130, 0, 16, 0, 1, 0, + 0, 0, 58, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 7, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 246, 15, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 0, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 9, 114, 0, 16, 0, + 0, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 7, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 114, 32, 16, 0, 0, 0, + 0, 0, 246, 15, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 54, 0, 0, 5, 130, 32, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 62, 0, 0, 1, + 83, 84, 65, 84, 148, 0, + 0, 0, 49, 0, 0, 0, + 9, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 28, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_triangle_strip_rect_vs.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_triangle_strip_rect_vs.h new file mode 100644 index 000000000..ccdcc1495 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/guest_output_triangle_strip_rect_vs.h @@ -0,0 +1,220 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer XeTriangleStripRectConstants +// { +// +// float2 xe_triangle_strip_rect_offset;// Offset: 0 Size: 8 +// float2 xe_triangle_strip_rect_size;// Offset: 8 Size: 8 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim ID HLSL Bind Count +// ------------------------------ ---------- ------- ----------- ------- -------------- ------ +// XeTriangleStripRectConstants cbuffer NA NA CB0 cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_VertexID 0 x 0 VERTID uint x +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Position 0 xyzw 0 POS float xyzw +// +vs_5_1 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0 +dcl_input_sgv v0.x, vertex_id +dcl_output_siv o0.xyzw, position +dcl_temps 1 +ushr r0.y, v0.x, l(1) +mov r0.x, v0.x +and r0.xy, r0.xyxx, l(1, 1, 0, 0) +utof r0.xy, r0.xyxx +mad o0.xy, r0.xyxx, CB0[0][0].zwzz, CB0[0][0].xyxx +mov o0.zw, l(0,0,0,1.000000) +ret +// Approximately 7 instruction slots used +#endif + +const BYTE guest_output_triangle_strip_rect_vs[] = +{ + 68, 88, 66, 67, 67, 209, + 250, 163, 3, 195, 64, 100, + 167, 54, 190, 31, 173, 113, + 120, 163, 1, 0, 0, 0, + 216, 3, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 184, 1, 0, 0, 236, 1, + 0, 0, 32, 2, 0, 0, + 60, 3, 0, 0, 82, 68, + 69, 70, 124, 1, 0, 0, + 1, 0, 0, 0, 132, 0, + 0, 0, 1, 0, 0, 0, + 60, 0, 0, 0, 1, 5, + 254, 255, 0, 5, 0, 0, + 84, 1, 0, 0, 19, 19, + 68, 37, 60, 0, 0, 0, + 24, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 100, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 88, 101, + 84, 114, 105, 97, 110, 103, + 108, 101, 83, 116, 114, 105, + 112, 82, 101, 99, 116, 67, + 111, 110, 115, 116, 97, 110, + 116, 115, 0, 171, 171, 171, + 100, 0, 0, 0, 2, 0, + 0, 0, 156, 0, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 236, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 0, 0, + 2, 0, 0, 0, 20, 1, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 56, 1, + 0, 0, 8, 0, 0, 0, + 8, 0, 0, 0, 2, 0, + 0, 0, 20, 1, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 120, 101, 95, 116, + 114, 105, 97, 110, 103, 108, + 101, 95, 115, 116, 114, 105, + 112, 95, 114, 101, 99, 116, + 95, 111, 102, 102, 115, 101, + 116, 0, 102, 108, 111, 97, + 116, 50, 0, 171, 171, 171, + 1, 0, 3, 0, 1, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 10, 1, 0, 0, + 120, 101, 95, 116, 114, 105, + 97, 110, 103, 108, 101, 95, + 115, 116, 114, 105, 112, 95, + 114, 101, 99, 116, 95, 115, + 105, 122, 101, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, + 83, 86, 95, 86, 101, 114, + 116, 101, 120, 73, 68, 0, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 80, 111, 115, 105, 116, + 105, 111, 110, 0, 83, 72, + 69, 88, 20, 1, 0, 0, + 81, 0, 1, 0, 69, 0, + 0, 0, 106, 8, 0, 1, + 89, 0, 0, 7, 70, 142, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 96, 0, + 0, 4, 18, 16, 16, 0, + 0, 0, 0, 0, 6, 0, + 0, 0, 103, 0, 0, 4, + 242, 32, 16, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 104, 0, 0, 2, 1, 0, + 0, 0, 85, 0, 0, 7, + 34, 0, 16, 0, 0, 0, + 0, 0, 10, 16, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 1, 0, 0, 0, + 54, 0, 0, 5, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 16, 16, 0, 0, 0, + 0, 0, 1, 0, 0, 10, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 86, 0, 0, 5, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 13, + 50, 32, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 230, 138, + 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 70, 128, 48, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 8, 194, 32, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 128, 63, 62, 0, 0, 1, + 83, 84, 65, 84, 148, 0, + 0, 0, 7, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/immediate_ps.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/immediate_ps.h index 6a0bd31ac..09255e348 100644 --- a/src/xenia/ui/shaders/bytecode/d3d12_5_1/immediate_ps.h +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/immediate_ps.h @@ -42,10 +42,10 @@ ret const BYTE immediate_ps[] = { - 68, 88, 66, 67, 204, 46, - 131, 39, 62, 129, 239, 95, - 188, 170, 211, 224, 226, 155, - 212, 68, 1, 0, 0, 0, + 68, 88, 66, 67, 218, 200, + 108, 196, 58, 28, 70, 226, + 98, 137, 89, 199, 218, 58, + 155, 172, 1, 0, 0, 0, 0, 3, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, 28, 1, 0, 0, 104, 1, @@ -55,7 +55,7 @@ const BYTE immediate_ps[] = 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 255, 255, 0, 5, 4, 0, + 255, 255, 0, 5, 0, 0, 182, 0, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, diff --git a/src/xenia/ui/shaders/bytecode/d3d12_5_1/immediate_vs.h b/src/xenia/ui/shaders/bytecode/d3d12_5_1/immediate_vs.h index 03385fb28..dc36315ba 100644 --- a/src/xenia/ui/shaders/bytecode/d3d12_5_1/immediate_vs.h +++ b/src/xenia/ui/shaders/bytecode/d3d12_5_1/immediate_vs.h @@ -8,7 +8,7 @@ // cbuffer XeImmediateVertexConstants // { // -// float2 xe_viewport_size_inv; // Offset: 0 Size: 8 +// float2 xe_coordinate_space_size_inv;// Offset: 0 Size: 8 // // } // @@ -59,21 +59,21 @@ ret const BYTE immediate_vs[] = { - 68, 88, 66, 67, 88, 56, - 35, 17, 155, 211, 230, 48, - 9, 16, 27, 220, 163, 42, - 194, 218, 1, 0, 0, 0, - 16, 4, 0, 0, 5, 0, + 68, 88, 66, 67, 141, 232, + 76, 204, 152, 38, 127, 131, + 125, 87, 10, 113, 217, 159, + 27, 143, 1, 0, 0, 0, + 24, 4, 0, 0, 5, 0, 0, 0, 52, 0, 0, 0, - 100, 1, 0, 0, 212, 1, - 0, 0, 68, 2, 0, 0, - 116, 3, 0, 0, 82, 68, - 69, 70, 40, 1, 0, 0, + 108, 1, 0, 0, 220, 1, + 0, 0, 76, 2, 0, 0, + 124, 3, 0, 0, 82, 68, + 69, 70, 48, 1, 0, 0, 1, 0, 0, 0, 128, 0, 0, 0, 1, 0, 0, 0, 60, 0, 0, 0, 1, 5, - 254, 255, 0, 5, 4, 0, - 0, 1, 0, 0, 19, 19, + 254, 255, 0, 5, 0, 0, + 8, 1, 0, 0, 19, 19, 68, 37, 60, 0, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, 40, 0, 0, 0, @@ -97,122 +97,123 @@ const BYTE immediate_vs[] = 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, - 220, 0, 0, 0, 0, 0, + 228, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, - 120, 101, 95, 118, 105, 101, - 119, 112, 111, 114, 116, 95, - 115, 105, 122, 101, 95, 105, - 110, 118, 0, 102, 108, 111, - 97, 116, 50, 0, 1, 0, - 3, 0, 1, 0, 2, 0, + 120, 101, 95, 99, 111, 111, + 114, 100, 105, 110, 97, 116, + 101, 95, 115, 112, 97, 99, + 101, 95, 115, 105, 122, 101, + 95, 105, 110, 118, 0, 102, + 108, 111, 97, 116, 50, 0, + 1, 0, 3, 0, 1, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 221, 0, 0, 0, + 77, 105, 99, 114, 111, 115, + 111, 102, 116, 32, 40, 82, + 41, 32, 72, 76, 83, 76, + 32, 83, 104, 97, 100, 101, + 114, 32, 67, 111, 109, 112, + 105, 108, 101, 114, 32, 49, + 48, 46, 49, 0, 73, 83, + 71, 78, 104, 0, 0, 0, + 3, 0, 0, 0, 8, 0, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 213, 0, 0, 0, 77, 105, - 99, 114, 111, 115, 111, 102, - 116, 32, 40, 82, 41, 32, - 72, 76, 83, 76, 32, 83, - 104, 97, 100, 101, 114, 32, - 67, 111, 109, 112, 105, 108, - 101, 114, 32, 49, 48, 46, - 49, 0, 73, 83, 71, 78, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 3, 3, + 0, 0, 89, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 3, 3, + 0, 0, 98, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 2, 0, 0, 0, 15, 15, + 0, 0, 80, 79, 83, 73, + 84, 73, 79, 78, 0, 84, + 69, 88, 67, 79, 79, 82, + 68, 0, 67, 79, 76, 79, + 82, 0, 79, 83, 71, 78, 104, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, - 0, 0, 3, 3, 0, 0, - 89, 0, 0, 0, 0, 0, + 0, 0, 3, 12, 0, 0, + 80, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, - 0, 0, 3, 3, 0, 0, - 98, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 89, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 2, 0, - 0, 0, 15, 15, 0, 0, - 80, 79, 83, 73, 84, 73, - 79, 78, 0, 84, 69, 88, - 67, 79, 79, 82, 68, 0, - 67, 79, 76, 79, 82, 0, - 79, 83, 71, 78, 104, 0, - 0, 0, 3, 0, 0, 0, - 8, 0, 0, 0, 80, 0, + 0, 0, 15, 0, 0, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 83, 86, 95, + 80, 111, 115, 105, 116, 105, + 111, 110, 0, 171, 171, 171, + 83, 72, 69, 88, 40, 1, + 0, 0, 81, 0, 1, 0, + 74, 0, 0, 0, 106, 8, + 0, 1, 89, 0, 0, 7, + 70, 142, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 3, 12, 0, 0, 80, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 15, 0, 0, 0, 89, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 3, 0, - 0, 0, 2, 0, 0, 0, - 15, 0, 0, 0, 84, 69, - 88, 67, 79, 79, 82, 68, - 0, 83, 86, 95, 80, 111, - 115, 105, 116, 105, 111, 110, - 0, 171, 171, 171, 83, 72, - 69, 88, 40, 1, 0, 0, - 81, 0, 1, 0, 74, 0, - 0, 0, 106, 8, 0, 1, - 89, 0, 0, 7, 70, 142, - 48, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 95, 0, - 0, 3, 50, 16, 16, 0, - 0, 0, 0, 0, 95, 0, - 0, 3, 50, 16, 16, 0, - 1, 0, 0, 0, 95, 0, - 0, 3, 242, 16, 16, 0, - 2, 0, 0, 0, 101, 0, - 0, 3, 50, 32, 16, 0, - 0, 0, 0, 0, 101, 0, - 0, 3, 242, 32, 16, 0, - 1, 0, 0, 0, 103, 0, - 0, 4, 242, 32, 16, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 104, 0, 0, 2, - 1, 0, 0, 0, 56, 0, - 0, 9, 50, 0, 16, 0, - 0, 0, 0, 0, 70, 16, + 95, 0, 0, 3, 50, 16, 16, 0, 0, 0, 0, 0, - 70, 128, 48, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 50, 0, - 0, 15, 50, 32, 16, 0, - 2, 0, 0, 0, 70, 0, + 95, 0, 0, 3, 50, 16, + 16, 0, 1, 0, 0, 0, + 95, 0, 0, 3, 242, 16, + 16, 0, 2, 0, 0, 0, + 101, 0, 0, 3, 50, 32, 16, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 0, 0, - 0, 64, 0, 0, 0, 192, + 101, 0, 0, 3, 242, 32, + 16, 0, 1, 0, 0, 0, + 103, 0, 0, 4, 242, 32, + 16, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 104, 0, + 0, 2, 1, 0, 0, 0, + 56, 0, 0, 9, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 16, 16, 0, 0, 0, + 0, 0, 70, 128, 48, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 50, 0, 0, 15, 50, 32, + 16, 0, 2, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 64, 0, 0, + 0, 192, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 191, + 0, 0, 128, 63, 0, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 5, 242, 32, + 16, 0, 1, 0, 0, 0, + 70, 30, 16, 0, 2, 0, + 0, 0, 54, 0, 0, 8, + 194, 32, 16, 0, 2, 0, 0, 0, 2, 64, 0, 0, - 0, 0, 128, 191, 0, 0, - 128, 63, 0, 0, 0, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 242, 32, 16, 0, - 1, 0, 0, 0, 70, 30, - 16, 0, 2, 0, 0, 0, - 54, 0, 0, 8, 194, 32, - 16, 0, 2, 0, 0, 0, - 2, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 128, 63, 54, 0, 0, 5, - 50, 32, 16, 0, 0, 0, - 0, 0, 70, 16, 16, 0, - 1, 0, 0, 0, 62, 0, - 0, 1, 83, 84, 65, 84, - 148, 0, 0, 0, 6, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 6, 0, - 0, 0, 2, 0, 0, 0, + 0, 0, 128, 63, 54, 0, + 0, 5, 50, 32, 16, 0, + 0, 0, 0, 0, 70, 16, + 16, 0, 1, 0, 0, 0, + 62, 0, 0, 1, 83, 84, + 65, 84, 148, 0, 0, 0, + 6, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, + 6, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -220,7 +221,7 @@ const BYTE immediate_vs[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -232,5 +233,5 @@ const BYTE immediate_vs[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 + 0, 0, 0, 0 }; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.h new file mode 100644 index 000000000..57f769dfe --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.h @@ -0,0 +1,592 @@ +// generated from `xb genspirv` +// source: guest_output_bilinear_dither.frag +const uint8_t guest_output_bilinear_dither_frag[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x72, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0A, 0x00, + 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, + 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, + 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, + 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x29, 0x01, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, + 0x72, 0x61, 0x67, 0x43, 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x00, 0x30, 0x01, 0x00, 0x00, 0x58, 0x65, 0x42, 0x69, + 0x6C, 0x69, 0x6E, 0x65, 0x61, 0x72, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, + 0x6E, 0x74, 0x73, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x30, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x62, 0x69, 0x6C, 0x69, 0x6E, + 0x65, 0x61, 0x72, 0x5F, 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, 0x6F, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, + 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x62, + 0x69, 0x6C, 0x69, 0x6E, 0x65, 0x61, 0x72, 0x5F, 0x6F, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x69, 0x6E, 0x76, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x66, + 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x3E, 0x01, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x29, 0x01, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x30, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3A, 0x01, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x3E, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x3E, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x84, 0x83, 0x83, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0xE7, 0xE6, 0x66, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0xDE, 0xDD, 0xDD, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xCC, 0xCB, 0xCB, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xA8, 0xA7, 0xA7, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x9F, 0x9E, 0x1E, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x8C, 0x8B, 0x8B, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xB6, 0xB5, 0xB5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0xB2, 0xB1, 0xB1, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x87, 0x86, 0x06, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0xA0, 0x9F, 0x9F, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0xD2, 0xD1, 0xD1, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x9B, 0x9A, 0x1A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x95, 0x94, 0x94, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x92, 0x91, 0x91, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xBA, 0xB9, 0xB9, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0xBE, 0xBD, 0xBD, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x8F, 0x8E, 0x0E, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x89, 0x88, 0x08, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x9E, 0x9D, 0x9D, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0xE5, 0xE4, 0xE4, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x8A, 0x89, 0x89, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xA3, 0xA2, 0x22, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0xFD, 0xFC, 0xFC, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0xF6, 0xF5, 0xF5, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, + 0x90, 0x8F, 0x8F, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0xD1, 0xD0, 0xD0, 0xB8, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x8B, 0x8A, 0x0A, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xBC, 0xBB, 0xBB, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0xFE, 0xFD, 0xFD, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xB7, 0xB6, 0x36, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0xA1, 0xA0, 0x20, 0x38, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0xBB, 0xBA, 0x3A, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0xAC, 0xAB, 0xAB, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x90, 0x8F, 0x8F, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x9D, 0x9C, 0x9C, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xE6, 0xE5, 0xE5, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, + 0xDC, 0xDB, 0xDB, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0xC2, 0xC1, 0xC1, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0xA1, 0xA0, 0x20, 0xB8, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, + 0xCF, 0xCE, 0x4E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x00, 0x00, 0xF0, 0xEF, 0xEF, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x9D, 0x9C, 0x9C, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, + 0x82, 0x81, 0x81, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x9A, 0x99, 0x99, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0xB9, 0xB8, 0x38, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0xD8, 0xD7, 0xD7, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x00, 0xD0, 0xCF, 0xCF, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0xB5, 0xB4, 0xB4, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0xF4, 0xF3, 0xF3, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0xE8, 0xE7, 0xE7, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xE3, 0xE2, 0x62, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0xEB, 0xEA, 0x6A, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x49, 0x00, 0x00, 0x00, 0xA9, 0xA8, 0x28, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0xF3, 0xF2, 0x72, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, + 0xC8, 0xC7, 0xC7, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4C, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0x2A, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xA9, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0xD3, 0xD2, 0x52, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0xC0, 0xBF, 0xBF, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xEC, 0xEB, 0xEB, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0xDF, 0xDE, 0x5E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x00, 0x00, 0xDF, 0xDE, 0x5E, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xED, 0xEC, 0xEC, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x8C, 0x8B, 0x8B, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x99, 0x98, 0x18, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0xF9, 0xF8, 0x78, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, + 0xBA, 0xB9, 0xB9, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x9B, 0x9A, 0x1A, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0xC9, 0xC8, 0x48, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0xD2, 0xD1, 0xD1, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5B, 0x00, 0x00, 0x00, 0x86, 0x85, 0x85, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0xD5, 0xD4, 0xD4, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0xD9, 0xD8, 0x58, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5E, 0x00, 0x00, 0x00, 0xD0, 0xCF, 0xCF, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0x2A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x91, 0x90, 0x90, 0xB8, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0xDD, 0xDC, 0xDC, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0xD8, 0xD7, 0xD7, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, + 0xB4, 0xB3, 0xB3, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0xF3, 0xF2, 0x72, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xAF, 0xAE, 0x2E, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x9C, 0x9B, 0x9B, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x00, 0xBE, 0xBD, 0xBD, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x97, 0x96, 0x16, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0xF8, 0xF7, 0xF7, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6A, 0x00, 0x00, 0x00, 0xC7, 0xC6, 0x46, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xA9, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, + 0xFC, 0xFB, 0xFB, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6D, 0x00, 0x00, 0x00, 0xDA, 0xD9, 0xD9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x88, 0x87, 0x87, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, + 0x83, 0x82, 0x02, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0xF4, 0xF3, 0xF3, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x9E, 0x9D, 0x9D, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0xAD, 0xAC, 0xAC, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0xB4, 0xB3, 0xB3, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0xD6, 0xD5, 0xD5, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0xF2, 0xF1, 0xF1, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x76, 0x00, 0x00, 0x00, 0xBF, 0xBE, 0x3E, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0xEF, 0xEE, 0x6E, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0xEE, 0xED, 0xED, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x96, 0x95, 0x95, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0xC5, 0xC4, 0xC4, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, + 0xD1, 0xD0, 0xD0, 0x38, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0xC3, 0xC2, 0x42, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x9A, 0x99, 0x99, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x91, 0x90, 0x90, 0x38, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0xC4, 0xC3, 0xC3, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x7E, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0xFB, 0xFA, 0x7A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x00, 0xFE, 0xFD, 0xFD, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x8B, 0x8A, 0x0A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0xE1, 0xE0, 0x60, 0x38, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0xA4, 0xA3, 0xA3, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x8D, 0x8C, 0x8C, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0xCA, 0xC9, 0xC9, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x81, 0x80, 0x00, 0xB7, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0xA5, 0xA4, 0xA4, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, + 0xB0, 0xAF, 0xAF, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8B, 0x00, 0x00, 0x00, 0xE6, 0xE5, 0xE5, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x83, 0x82, 0x02, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, + 0x9C, 0x9B, 0x9B, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8E, 0x00, 0x00, 0x00, 0xCB, 0xCA, 0x4A, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0xB0, 0xAF, 0xAF, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x8D, 0x8C, 0x8C, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x91, 0x00, 0x00, 0x00, 0xC2, 0xC1, 0xC1, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x89, 0x88, 0x08, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0xCB, 0xCA, 0x4A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x00, 0x00, 0xAC, 0xAB, 0xAB, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0xCD, 0xCC, 0xCC, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0xFB, 0xFA, 0x7A, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xF5, 0xF4, 0xF4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xCA, 0xC9, 0xC9, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x88, 0x87, 0x87, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9A, 0x00, 0x00, 0x00, 0xB3, 0xB2, 0x32, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xDC, 0xDB, 0xDB, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, + 0x84, 0x83, 0x83, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9D, 0x00, 0x00, 0x00, 0xDD, 0xDC, 0xDC, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0xEA, 0xE9, 0xE9, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, + 0xF9, 0xF8, 0x78, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA0, 0x00, 0x00, 0x00, 0xA3, 0xA2, 0x22, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0xDA, 0xD9, 0xD9, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00, + 0xDB, 0xDA, 0x5A, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA3, 0x00, 0x00, 0x00, 0xE2, 0xE1, 0xE1, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x8E, 0x8D, 0x8D, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, + 0xE8, 0xE7, 0xE7, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA6, 0x00, 0x00, 0x00, 0xCF, 0xCE, 0x4E, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, 0xEA, 0xE9, 0xE9, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, + 0xB3, 0xB2, 0x32, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA9, 0x00, 0x00, 0x00, 0x82, 0x81, 0x81, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xAE, 0xAD, 0xAD, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, + 0xB9, 0xB8, 0x38, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0xC6, 0xC5, 0xC5, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0xAD, 0xAC, 0xAC, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xBB, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAF, 0x00, 0x00, 0x00, 0xAF, 0xAE, 0x2E, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xD4, 0xD3, 0xD3, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, + 0x98, 0x97, 0x97, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB2, 0x00, 0x00, 0x00, 0xED, 0xEC, 0xEC, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0xE5, 0xE4, 0xE4, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, + 0xC9, 0xC8, 0x48, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB5, 0x00, 0x00, 0x00, 0xC4, 0xC3, 0xC3, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x96, 0x95, 0x95, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, + 0xB1, 0xB0, 0xB0, 0x38, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0xF8, 0xF7, 0xF7, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xB8, 0xB7, 0xB7, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, + 0x93, 0x92, 0x12, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0xF1, 0xF0, 0xF0, 0x38, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0xA6, 0xA5, 0xA5, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, + 0x97, 0x96, 0x16, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBE, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x7E, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xAE, 0xAD, 0xAD, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xE9, 0xE8, 0x68, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0xC7, 0xC6, 0x46, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, 0xB2, 0xB1, 0xB1, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, + 0xC0, 0xBF, 0xBF, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x00, 0x00, 0xE3, 0xE2, 0x62, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0xBD, 0xBC, 0xBC, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0x8F, 0x8E, 0x0E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC7, 0x00, 0x00, 0x00, 0x94, 0x93, 0x93, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x85, 0x84, 0x84, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC9, 0x00, 0x00, 0x00, + 0x93, 0x92, 0x12, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCA, 0x00, 0x00, 0x00, 0xEE, 0xED, 0xED, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, 0xE2, 0xE1, 0xE1, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, + 0x98, 0x97, 0x97, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCD, 0x00, 0x00, 0x00, 0xC3, 0xC2, 0x42, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xCE, 0xCD, 0xCD, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, + 0xF1, 0xF0, 0xF0, 0xB8, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD0, 0x00, 0x00, 0x00, 0xA0, 0x9F, 0x9F, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0xF0, 0xEF, 0xEF, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, + 0xC1, 0xC0, 0xC0, 0x37, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD3, 0x00, 0x00, 0x00, 0xFC, 0xFB, 0xFB, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0xE4, 0xE3, 0xE3, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, + 0xBB, 0xBA, 0x3A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD6, 0x00, 0x00, 0x00, 0xCC, 0xCB, 0xCB, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0xE0, 0xDF, 0xDF, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, + 0xA2, 0xA1, 0xA1, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD9, 0x00, 0x00, 0x00, 0xD9, 0xD8, 0x58, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x8A, 0x89, 0x89, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, + 0xBF, 0xBE, 0x3E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDC, 0x00, 0x00, 0x00, 0xD6, 0xD5, 0xD5, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0xA5, 0xA4, 0xA4, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, + 0xFD, 0xFC, 0xFC, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDF, 0x00, 0x00, 0x00, 0xEC, 0xEB, 0xEB, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF7, 0xF6, 0x76, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, + 0xB5, 0xB4, 0xB4, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE2, 0x00, 0x00, 0x00, 0xB7, 0xB6, 0x36, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0x86, 0x85, 0x85, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, + 0xCE, 0xCD, 0xCD, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE5, 0x00, 0x00, 0x00, 0xC1, 0xC0, 0xC0, 0xB7, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE6, 0x00, 0x00, 0x00, 0xA8, 0xA7, 0xA7, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, + 0xD3, 0xD2, 0x52, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE8, 0x00, 0x00, 0x00, 0xEB, 0xEA, 0x6A, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0xBC, 0xBB, 0xBB, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, + 0x9F, 0x9E, 0x1E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xEB, 0x00, 0x00, 0x00, 0xB1, 0xB0, 0xB0, 0xB8, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0xEF, 0xEE, 0x6E, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3B, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xEE, 0x00, 0x00, 0x00, 0xB8, 0xB7, 0xB7, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0xA7, 0xA6, 0x26, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0xC6, 0xC5, 0xC5, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF1, 0x00, 0x00, 0x00, 0xD7, 0xD6, 0x56, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, 0xA4, 0xA3, 0xA3, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, + 0x85, 0x84, 0x84, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF4, 0x00, 0x00, 0x00, 0xC8, 0xC7, 0xC7, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0x94, 0x93, 0x93, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, + 0xBD, 0xBC, 0xBC, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF7, 0x00, 0x00, 0x00, 0xCD, 0xCC, 0xCC, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xFA, 0xF9, 0xF9, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, + 0xB6, 0xB5, 0xB5, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFA, 0x00, 0x00, 0x00, 0x92, 0x91, 0x91, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, 0xD4, 0xD3, 0xD3, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, + 0xE7, 0xE6, 0x66, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x00, 0x00, 0x99, 0x98, 0x18, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x8E, 0x8D, 0x8D, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0xE1, 0xE0, 0x60, 0xB8, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x95, 0x94, 0x94, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xDE, 0xDD, 0xDD, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, + 0xA6, 0xA5, 0xA5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x87, 0x86, 0x06, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0xA9, 0xA8, 0x28, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, + 0xF6, 0xF5, 0xF5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x01, 0x00, 0x00, 0xDB, 0xDA, 0x5A, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0xA7, 0xA6, 0x26, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, + 0x81, 0x80, 0x00, 0x37, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x09, 0x01, 0x00, 0x00, 0xFA, 0xF9, 0xF9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0xE9, 0xE8, 0x68, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, + 0xC5, 0xC4, 0xC4, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0C, 0x01, 0x00, 0x00, 0xE4, 0xE3, 0xE3, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0xD5, 0xD4, 0xD4, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, + 0xA2, 0xA1, 0xA1, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0F, 0x01, 0x00, 0x00, 0xE0, 0xDF, 0xDF, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0xF7, 0xF6, 0x76, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, + 0xD7, 0xD6, 0x56, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x12, 0x01, 0x00, 0x00, 0xF2, 0xF1, 0xF1, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0xF5, 0xF4, 0xF4, 0x39, + 0x2C, 0x00, 0x03, 0x01, 0x13, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x4A, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, + 0x4D, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, + 0x6B, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, + 0x6E, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x71, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, + 0x7A, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, + 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x89, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, + 0x8C, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, + 0x9E, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, + 0xA1, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0xA4, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, + 0xA7, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, + 0xAA, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, + 0xAD, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, + 0xB0, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, + 0xB3, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, + 0xB6, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, + 0xB9, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, + 0xBC, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, + 0xBF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, + 0xC2, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, + 0xC5, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, + 0xC8, 0x00, 0x00, 0x00, 0xC9, 0x00, 0x00, 0x00, 0xCA, 0x00, 0x00, 0x00, + 0xCB, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, + 0xCE, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, + 0xD1, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, + 0xD4, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, + 0xD7, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x00, 0x00, + 0xDA, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, + 0xDD, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, + 0xE3, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, + 0xE6, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, + 0xE9, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, + 0xEC, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, + 0xEF, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, + 0xF2, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, + 0xF5, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, + 0xFB, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, + 0x07, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, + 0x0A, 0x01, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, + 0x0D, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, + 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, + 0x13, 0x01, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x19, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1F, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x21, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x27, 0x01, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x28, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x28, 0x01, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x2A, 0x01, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x2D, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x2E, 0x01, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x30, 0x01, 0x00, 0x00, + 0x2E, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x31, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x31, 0x01, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x2D, 0x01, 0x00, 0x00, + 0x33, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x34, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x39, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x27, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x39, 0x01, 0x00, 0x00, + 0x3A, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x3B, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, + 0x3C, 0x01, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x3D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x3D, 0x01, 0x00, 0x00, 0x3E, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, + 0x2D, 0x01, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x46, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2A, 0x01, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x4C, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x06, 0x00, 0x4C, 0x01, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, + 0x4A, 0x01, 0x00, 0x00, 0x4A, 0x01, 0x00, 0x00, 0x4A, 0x01, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3F, 0x2C, 0x00, 0x06, 0x00, 0x4C, 0x01, 0x00, 0x00, + 0x59, 0x01, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, + 0x58, 0x01, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5D, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x5E, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, + 0x42, 0x01, 0x00, 0x00, 0x42, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x71, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x1F, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x27, 0x01, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x2A, 0x01, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, + 0x2E, 0x01, 0x00, 0x00, 0x2F, 0x01, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x34, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, + 0x32, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x2E, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x2E, 0x01, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, + 0x2F, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x3F, 0x01, 0x00, 0x00, + 0x3E, 0x01, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x2A, 0x01, 0x00, 0x00, + 0x41, 0x01, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x2A, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, + 0x70, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x46, 0x01, 0x00, 0x00, + 0x47, 0x01, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, + 0x47, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x2A, 0x01, 0x00, 0x00, + 0x49, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, + 0x58, 0x00, 0x07, 0x00, 0x27, 0x01, 0x00, 0x00, 0x4B, 0x01, 0x00, 0x00, + 0x3F, 0x01, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x4A, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x27, 0x01, 0x00, 0x00, + 0x4E, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, + 0x27, 0x01, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x4E, 0x01, 0x00, 0x00, + 0x4B, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x3A, 0x01, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x27, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, + 0x4F, 0x00, 0x08, 0x00, 0x4C, 0x01, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, + 0x50, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, + 0x71, 0x01, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x67, 0x01, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, + 0x67, 0x01, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6A, 0x01, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6B, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x6A, 0x01, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x60, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x21, 0x01, 0x00, 0x00, 0x6C, 0x01, 0x00, 0x00, + 0x60, 0x01, 0x00, 0x00, 0x6B, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, 0x6C, 0x01, 0x00, 0x00, + 0x50, 0x00, 0x06, 0x00, 0x4C, 0x01, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, + 0x6D, 0x01, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x4C, 0x01, 0x00, 0x00, 0x56, 0x01, 0x00, 0x00, + 0x51, 0x01, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x4C, 0x01, 0x00, 0x00, 0x5A, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, + 0x59, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x27, 0x01, 0x00, 0x00, + 0x5B, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, + 0x27, 0x01, 0x00, 0x00, 0x5C, 0x01, 0x00, 0x00, 0x5B, 0x01, 0x00, 0x00, + 0x5A, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x3A, 0x01, 0x00, 0x00, 0x5C, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x5E, 0x01, 0x00, 0x00, 0x5F, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, + 0x5D, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x01, 0x00, 0x00, + 0x58, 0x01, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..ade34879138b54cf7c4faca8e0ae51a055ed6a8c GIT binary patch literal 7056 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 literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_frag.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_frag.txt new file mode 100644 index 000000000..26ba8b3c9 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_frag.txt @@ -0,0 +1,81 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 57 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %xe_frag_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 420 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %gl_FragCoord "gl_FragCoord" + OpName %XeBilinearConstants "XeBilinearConstants" + OpMemberName %XeBilinearConstants 0 "xe_bilinear_output_offset" + OpMemberName %XeBilinearConstants 1 "xe_bilinear_output_size_inv" + OpName %_ "" + OpName %xe_frag_color "xe_frag_color" + OpName %xe_texture "xe_texture" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpMemberDecorate %XeBilinearConstants 0 Offset 16 + OpMemberDecorate %XeBilinearConstants 1 Offset 24 + OpDecorate %XeBilinearConstants Block + OpDecorate %xe_frag_color Location 0 + OpDecorate %xe_texture DescriptorSet 0 + OpDecorate %xe_texture Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%XeBilinearConstants = OpTypeStruct %v2int %v2float +%_ptr_PushConstant_XeBilinearConstants = OpTypePointer PushConstant %XeBilinearConstants + %_ = OpVariable %_ptr_PushConstant_XeBilinearConstants PushConstant + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_v2int = OpTypePointer PushConstant %v2int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%xe_frag_color = OpVariable %_ptr_Output_v4float Output + %31 = OpTypeImage %float 2D 0 0 0 1 Unknown + %32 = OpTypeSampledImage %31 +%_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32 + %xe_texture = OpVariable %_ptr_UniformConstant_32 UniformConstant + %float_0_5 = OpConstant %float 0.5 + %int_1 = OpConstant %int 1 +%_ptr_PushConstant_v2float = OpTypePointer PushConstant %v2float + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_3 = OpConstant %uint 3 +%_ptr_Output_float = OpTypePointer Output %float + %56 = OpConstantComposite %v2float %float_0_5 %float_0_5 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpLoad %v4float %gl_FragCoord + %16 = OpVectorShuffle %v2float %15 %15 0 1 + %19 = OpConvertFToS %v2int %16 + %25 = OpAccessChain %_ptr_PushConstant_v2int %_ %int_0 + %26 = OpLoad %v2int %25 + %27 = OpISub %v2int %19 %26 + %28 = OpBitcast %v2uint %27 + %35 = OpLoad %32 %xe_texture + %37 = OpConvertUToF %v2float %28 + %40 = OpFAdd %v2float %37 %56 + %43 = OpAccessChain %_ptr_PushConstant_v2float %_ %int_1 + %44 = OpLoad %v2float %43 + %45 = OpFMul %v2float %40 %44 + %47 = OpImageSampleExplicitLod %v4float %35 %45 Lod %float_0 + %50 = OpLoad %v4float %xe_frag_color + %51 = OpVectorShuffle %v4float %50 %47 4 5 6 3 + OpStore %xe_frag_color %51 + %55 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_3 + OpStore %55 %float_1 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.h new file mode 100644 index 000000000..1feff3326 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.h @@ -0,0 +1,1216 @@ +// generated from `xb genspirv` +// source: guest_output_ffx_cas_resample_dither.frag +const uint8_t guest_output_ffx_cas_resample_dither_frag[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x7E, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0xB4, 0x09, 0x00, 0x00, 0xE1, 0x09, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0A, 0x00, + 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, + 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, + 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, + 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0xB4, 0x09, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, + 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, + 0xB8, 0x09, 0x00, 0x00, 0x58, 0x65, 0x43, 0x61, 0x73, 0x52, 0x65, 0x73, + 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, + 0x74, 0x73, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, 0xB8, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x6F, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0B, 0x00, 0xB8, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x69, + 0x6E, 0x70, 0x75, 0x74, 0x5F, 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, + 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, 0x00, + 0x06, 0x00, 0x0A, 0x00, 0xB8, 0x09, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x73, 0x68, 0x61, 0x72, 0x70, + 0x6E, 0x65, 0x73, 0x73, 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x73, 0x65, + 0x74, 0x75, 0x70, 0x00, 0x05, 0x00, 0x03, 0x00, 0xBA, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0xE1, 0x09, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xAB, 0x01, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xAB, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xB4, 0x09, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xB8, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0xB8, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0xB8, 0x09, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0xB8, 0x09, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xE1, 0x09, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x1C, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x84, 0x83, 0x83, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xE7, 0xE6, 0x66, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0xDE, 0xDD, 0xDD, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0xCC, 0xCB, 0xCB, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0xA8, 0xA7, 0xA7, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x9F, 0x9E, 0x1E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x8C, 0x8B, 0x8B, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xB6, 0xB5, 0xB5, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xB2, 0xB1, 0xB1, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5A, 0x00, 0x00, 0x00, 0x87, 0x86, 0x06, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xA0, 0x9F, 0x9F, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0xD2, 0xD1, 0xD1, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5D, 0x00, 0x00, 0x00, 0x9B, 0x9A, 0x1A, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x95, 0x94, 0x94, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x92, 0x91, 0x91, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xBA, 0xB9, 0xB9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0xBE, 0xBD, 0xBD, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x8F, 0x8E, 0x0E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x89, 0x88, 0x08, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x9E, 0x9D, 0x9D, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0xE5, 0xE4, 0xE4, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x8A, 0x89, 0x89, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0xA3, 0xA2, 0x22, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0xFD, 0xFC, 0xFC, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x69, 0x00, 0x00, 0x00, 0xF6, 0xF5, 0xF5, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x90, 0x8F, 0x8F, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, + 0xD1, 0xD0, 0xD0, 0xB8, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6C, 0x00, 0x00, 0x00, 0x8B, 0x8A, 0x0A, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0xBC, 0xBB, 0xBB, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, + 0xFE, 0xFD, 0xFD, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0xB7, 0xB6, 0x36, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xA1, 0xA0, 0x20, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, + 0xBB, 0xBA, 0x3A, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0xAC, 0xAB, 0xAB, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x90, 0x8F, 0x8F, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x9D, 0x9C, 0x9C, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x75, 0x00, 0x00, 0x00, 0xE6, 0xE5, 0xE5, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0xDC, 0xDB, 0xDB, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0xC2, 0xC1, 0xC1, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0xA1, 0xA0, 0x20, 0xB8, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0xCF, 0xCE, 0x4E, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, + 0xF0, 0xEF, 0xEF, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7B, 0x00, 0x00, 0x00, 0x9D, 0x9C, 0x9C, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x82, 0x81, 0x81, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, + 0x9A, 0x99, 0x99, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0xB9, 0xB8, 0x38, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xD8, 0xD7, 0xD7, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xD0, 0xCF, 0xCF, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0xB5, 0xB4, 0xB4, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0xF4, 0xF3, 0xF3, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, + 0xE8, 0xE7, 0xE7, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0xE3, 0xE2, 0x62, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0xEB, 0xEA, 0x6A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0xA9, 0xA8, 0x28, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0xF3, 0xF2, 0x72, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xC8, 0xC7, 0xC7, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0xAB, 0xAA, 0x2A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8A, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xA9, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0xD3, 0xD2, 0x52, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, + 0xC0, 0xBF, 0xBF, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8D, 0x00, 0x00, 0x00, 0xEC, 0xEB, 0xEB, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0xDF, 0xDE, 0x5E, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0xDF, 0xDE, 0x5E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0xED, 0xEC, 0xEC, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x8C, 0x8B, 0x8B, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x99, 0x98, 0x18, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0xF9, 0xF8, 0x78, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0xBA, 0xB9, 0xB9, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x9B, 0x9A, 0x1A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0xC9, 0xC8, 0x48, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0xD2, 0xD1, 0xD1, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x86, 0x85, 0x85, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0xD5, 0xD4, 0xD4, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0xD9, 0xD8, 0x58, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, + 0xD0, 0xCF, 0xCF, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9C, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0x2A, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x91, 0x90, 0x90, 0xB8, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, + 0xDD, 0xDC, 0xDC, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9F, 0x00, 0x00, 0x00, 0xD8, 0xD7, 0xD7, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0xB4, 0xB3, 0xB3, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0xF3, 0xF2, 0x72, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA2, 0x00, 0x00, 0x00, 0xAF, 0xAE, 0x2E, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x9C, 0x9B, 0x9B, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, + 0xBE, 0xBD, 0xBD, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA5, 0x00, 0x00, 0x00, 0x97, 0x96, 0x16, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, 0xF8, 0xF7, 0xF7, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, + 0xC7, 0xC6, 0x46, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA8, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xA9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xFC, 0xFB, 0xFB, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, + 0xDA, 0xD9, 0xD9, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAB, 0x00, 0x00, 0x00, 0x88, 0x87, 0x87, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x83, 0x82, 0x02, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0xF4, 0xF3, 0xF3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAE, 0x00, 0x00, 0x00, 0x9E, 0x9D, 0x9D, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xAD, 0xAC, 0xAC, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, + 0xB4, 0xB3, 0xB3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0xD6, 0xD5, 0xD5, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xF1, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, + 0xBF, 0xBE, 0x3E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB4, 0x00, 0x00, 0x00, 0xEF, 0xEE, 0x6E, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0xEE, 0xED, 0xED, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, + 0x96, 0x95, 0x95, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB7, 0x00, 0x00, 0x00, 0xC5, 0xC4, 0xC4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0xD1, 0xD0, 0xD0, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, + 0xC3, 0xC2, 0x42, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBA, 0x00, 0x00, 0x00, 0x9A, 0x99, 0x99, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x91, 0x90, 0x90, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0xC4, 0xC3, 0xC3, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBD, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x7E, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, 0xFB, 0xFA, 0x7A, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, + 0xFE, 0xFD, 0xFD, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x8B, 0x8A, 0x0A, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xE1, 0xE0, 0x60, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, + 0xA4, 0xA3, 0xA3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x00, 0x00, 0x8D, 0x8C, 0x8C, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0xCA, 0xC9, 0xC9, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, + 0x81, 0x80, 0x00, 0xB7, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC6, 0x00, 0x00, 0x00, 0xA5, 0xA4, 0xA4, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0xB0, 0xAF, 0xAF, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, + 0xE6, 0xE5, 0xE5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC9, 0x00, 0x00, 0x00, 0x83, 0x82, 0x02, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCA, 0x00, 0x00, 0x00, 0x9C, 0x9B, 0x9B, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, + 0xCB, 0xCA, 0x4A, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCC, 0x00, 0x00, 0x00, 0xB0, 0xAF, 0xAF, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x8D, 0x8C, 0x8C, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, + 0xC2, 0xC1, 0xC1, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCF, 0x00, 0x00, 0x00, 0x89, 0x88, 0x08, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0xCB, 0xCA, 0x4A, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, + 0xAC, 0xAB, 0xAB, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD2, 0x00, 0x00, 0x00, 0xCD, 0xCC, 0xCC, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0xFB, 0xFA, 0x7A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, + 0xF5, 0xF4, 0xF4, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD5, 0x00, 0x00, 0x00, 0xCA, 0xC9, 0xC9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x88, 0x87, 0x87, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, + 0xB3, 0xB2, 0x32, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD8, 0x00, 0x00, 0x00, 0xDC, 0xDB, 0xDB, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x00, 0x00, 0x84, 0x83, 0x83, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, + 0xDD, 0xDC, 0xDC, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDB, 0x00, 0x00, 0x00, 0xEA, 0xE9, 0xE9, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0xF9, 0xF8, 0x78, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, + 0xA3, 0xA2, 0x22, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0x00, 0xDA, 0xD9, 0xD9, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0xDB, 0xDA, 0x5A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, + 0xE2, 0xE1, 0xE1, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE1, 0x00, 0x00, 0x00, 0x8E, 0x8D, 0x8D, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xE8, 0xE7, 0xE7, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0xCF, 0xCE, 0x4E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE4, 0x00, 0x00, 0x00, 0xEA, 0xE9, 0xE9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xB3, 0xB2, 0x32, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE6, 0x00, 0x00, 0x00, + 0x82, 0x81, 0x81, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE7, 0x00, 0x00, 0x00, 0xAE, 0xAD, 0xAD, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xB9, 0xB8, 0x38, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, + 0xC6, 0xC5, 0xC5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xEA, 0x00, 0x00, 0x00, 0xAD, 0xAC, 0xAC, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0xAF, 0xAE, 0x2E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xED, 0x00, 0x00, 0x00, 0xD4, 0xD3, 0xD3, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0x98, 0x97, 0x97, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, + 0xED, 0xEC, 0xEC, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x00, 0xE5, 0xE4, 0xE4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xC9, 0xC8, 0x48, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, + 0xC4, 0xC3, 0xC3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF3, 0x00, 0x00, 0x00, 0x96, 0x95, 0x95, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0xB1, 0xB0, 0xB0, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, + 0xF8, 0xF7, 0xF7, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF6, 0x00, 0x00, 0x00, 0xB8, 0xB7, 0xB7, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0x93, 0x92, 0x12, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, + 0xF1, 0xF0, 0xF0, 0x38, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x00, 0x00, 0xA6, 0xA5, 0xA5, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x97, 0x96, 0x16, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, + 0xFF, 0xFE, 0x7E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0xAE, 0xAD, 0xAD, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xE9, 0xE8, 0x68, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0xC7, 0xC6, 0x46, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0xB2, 0xB1, 0xB1, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xC0, 0xBF, 0xBF, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0xE3, 0xE2, 0x62, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x00, 0x00, 0xBD, 0xBC, 0xBC, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x8F, 0x8E, 0x0E, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x94, 0x93, 0x93, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x05, 0x01, 0x00, 0x00, 0x85, 0x84, 0x84, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x93, 0x92, 0x12, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, + 0xEE, 0xED, 0xED, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x08, 0x01, 0x00, 0x00, 0xE2, 0xE1, 0xE1, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x98, 0x97, 0x97, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, + 0xC3, 0xC2, 0x42, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0B, 0x01, 0x00, 0x00, 0xCE, 0xCD, 0xCD, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0xF1, 0xF0, 0xF0, 0xB8, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, + 0xA0, 0x9F, 0x9F, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0E, 0x01, 0x00, 0x00, 0xF0, 0xEF, 0xEF, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0xC1, 0xC0, 0xC0, 0x37, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, + 0xFC, 0xFB, 0xFB, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x11, 0x01, 0x00, 0x00, 0xE4, 0xE3, 0xE3, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0xBB, 0xBA, 0x3A, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, + 0xCC, 0xCB, 0xCB, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0xE0, 0xDF, 0xDF, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0xA2, 0xA1, 0xA1, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, + 0xD9, 0xD8, 0x58, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x17, 0x01, 0x00, 0x00, 0x8A, 0x89, 0x89, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0xBF, 0xBE, 0x3E, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, + 0xD6, 0xD5, 0xD5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1A, 0x01, 0x00, 0x00, 0xA5, 0xA4, 0xA4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xFD, 0xFC, 0xFC, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, + 0xEC, 0xEB, 0xEB, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1D, 0x01, 0x00, 0x00, 0xF7, 0xF6, 0x76, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0xB5, 0xB4, 0xB4, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, + 0xB7, 0xB6, 0x36, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x00, 0x00, 0x86, 0x85, 0x85, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0xCE, 0xCD, 0xCD, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, + 0xC1, 0xC0, 0xC0, 0xB7, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x23, 0x01, 0x00, 0x00, 0xA8, 0xA7, 0xA7, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xD3, 0xD2, 0x52, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, + 0xEB, 0xEA, 0x6A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x26, 0x01, 0x00, 0x00, 0xBC, 0xBB, 0xBB, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, 0x9F, 0x9E, 0x1E, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, + 0xB1, 0xB0, 0xB0, 0xB8, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x00, 0xEF, 0xEE, 0x6E, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, + 0xB8, 0xB7, 0xB7, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0xA7, 0xA6, 0x26, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, 0xC6, 0xC5, 0xC5, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x00, + 0xD7, 0xD6, 0x56, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2F, 0x01, 0x00, 0x00, 0xA4, 0xA3, 0xA3, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x85, 0x84, 0x84, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, + 0xC8, 0xC7, 0xC7, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x32, 0x01, 0x00, 0x00, 0x94, 0x93, 0x93, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, 0xBD, 0xBC, 0xBC, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, + 0xCD, 0xCC, 0xCC, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x35, 0x01, 0x00, 0x00, 0xFA, 0xF9, 0xF9, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0xB6, 0xB5, 0xB5, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, + 0x92, 0x91, 0x91, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x38, 0x01, 0x00, 0x00, 0xD4, 0xD3, 0xD3, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0xE7, 0xE6, 0x66, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, + 0x99, 0x98, 0x18, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3B, 0x01, 0x00, 0x00, 0x8E, 0x8D, 0x8D, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0xE1, 0xE0, 0x60, 0xB8, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3D, 0x01, 0x00, 0x00, + 0x95, 0x94, 0x94, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3E, 0x01, 0x00, 0x00, 0xDE, 0xDD, 0xDD, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3F, 0x01, 0x00, 0x00, 0xA6, 0xA5, 0xA5, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, + 0x87, 0x86, 0x06, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x41, 0x01, 0x00, 0x00, 0xA9, 0xA8, 0x28, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x42, 0x01, 0x00, 0x00, 0xF6, 0xF5, 0xF5, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, + 0xDB, 0xDA, 0x5A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x44, 0x01, 0x00, 0x00, 0xA7, 0xA6, 0x26, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x81, 0x80, 0x00, 0x37, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x46, 0x01, 0x00, 0x00, + 0xFA, 0xF9, 0xF9, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x47, 0x01, 0x00, 0x00, 0xE9, 0xE8, 0x68, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0xC5, 0xC4, 0xC4, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, + 0xE4, 0xE3, 0xE3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4A, 0x01, 0x00, 0x00, 0xD5, 0xD4, 0xD4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4B, 0x01, 0x00, 0x00, 0xA2, 0xA1, 0xA1, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00, + 0xE0, 0xDF, 0xDF, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4D, 0x01, 0x00, 0x00, 0xF7, 0xF6, 0x76, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4E, 0x01, 0x00, 0x00, 0xD7, 0xD6, 0x56, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, + 0xF2, 0xF1, 0xF1, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x50, 0x01, 0x00, 0x00, 0xF5, 0xF4, 0xF4, 0x39, 0x2C, 0x00, 0x03, 0x01, + 0x50, 0x00, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x5B, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x5E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x6A, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, + 0x6D, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, + 0x8B, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, + 0x8E, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x91, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x9A, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, + 0x9D, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, + 0xA0, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00, + 0xA3, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, + 0xA6, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, + 0xA9, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, + 0xAF, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, + 0xB2, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, + 0xB5, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, + 0xBE, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0xC7, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0xC9, 0x00, 0x00, 0x00, + 0xCA, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, + 0xCD, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, + 0xD0, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, + 0xD3, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, + 0xD6, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, + 0xD9, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, + 0xDC, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, + 0xDF, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, + 0xE2, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, + 0xE5, 0x00, 0x00, 0x00, 0xE6, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, + 0xE8, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, + 0xEB, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, + 0xEE, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0xF1, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, + 0xF4, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, + 0xF7, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, + 0xFA, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, + 0x06, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, + 0x09, 0x01, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, + 0x0C, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, + 0x0F, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, + 0x12, 0x01, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, + 0x15, 0x01, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, + 0x18, 0x01, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x1A, 0x01, 0x00, 0x00, + 0x1B, 0x01, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x1D, 0x01, 0x00, 0x00, + 0x1E, 0x01, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, 0x23, 0x01, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, + 0x27, 0x01, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, + 0x2A, 0x01, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, + 0x2D, 0x01, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x00, 0x2F, 0x01, 0x00, 0x00, + 0x30, 0x01, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, + 0x33, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, + 0x36, 0x01, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, + 0x39, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, + 0x3C, 0x01, 0x00, 0x00, 0x3D, 0x01, 0x00, 0x00, 0x3E, 0x01, 0x00, 0x00, + 0x3F, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, + 0x42, 0x01, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x45, 0x01, 0x00, 0x00, 0x46, 0x01, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, + 0x48, 0x01, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x4A, 0x01, 0x00, 0x00, + 0x4B, 0x01, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00, 0x4D, 0x01, 0x00, 0x00, + 0x4E, 0x01, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x55, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5B, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, + 0x39, 0x46, 0xBC, 0x1F, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x8B, 0x01, 0x00, 0x00, 0xBB, 0x7E, 0xF0, 0x7E, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0xFF, 0x9F, 0xF1, 0x7E, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA1, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x19, 0x00, 0x09, 0x00, 0xA8, 0x01, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0xA9, 0x01, 0x00, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xAA, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xA9, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0xAA, 0x01, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xCE, 0x01, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xD5, 0x01, 0x00, 0x00, + 0xD4, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0xDB, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xE5, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xEB, 0x01, 0x00, 0x00, + 0xC7, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0xF1, 0x01, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0xD4, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xF7, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x9E, 0x03, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0xD4, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, + 0xD4, 0x03, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0xF6, 0x03, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0xD4, 0x03, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0xD4, 0x03, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x04, 0x00, 0x00, + 0xD4, 0x01, 0x00, 0x00, 0xD4, 0x03, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, + 0x20, 0x00, 0x04, 0x00, 0xB3, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xB3, 0x09, 0x00, 0x00, + 0xB4, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00, + 0xB8, 0x09, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x9E, 0x03, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xB9, 0x09, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB8, 0x09, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0xB9, 0x09, 0x00, 0x00, 0xBA, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xBB, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xC1, 0x09, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9E, 0x03, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC5, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0x20, 0x00, 0x04, 0x00, 0xD2, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xE0, 0x09, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0xE0, 0x09, 0x00, 0x00, 0xE1, 0x09, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xEF, 0x09, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x01, 0x0A, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x02, 0x0A, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x79, 0x01, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x0A, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x6D, 0x1A, 0x00, 0x00, 0xC5, 0x09, 0x00, 0x00, + 0xC5, 0x09, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x7D, 0x1A, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x5B, 0x01, 0x00, 0x00, + 0xA9, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0xB5, 0x09, 0x00, 0x00, 0xB4, 0x09, 0x00, 0x00, + 0x4F, 0x00, 0x07, 0x00, 0x9E, 0x03, 0x00, 0x00, 0xB6, 0x09, 0x00, 0x00, + 0xB5, 0x09, 0x00, 0x00, 0xB5, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xB7, 0x09, 0x00, 0x00, 0xB6, 0x09, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xBB, 0x09, 0x00, 0x00, 0xBC, 0x09, 0x00, 0x00, 0xBA, 0x09, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xBD, 0x09, 0x00, 0x00, 0xBC, 0x09, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0xBE, 0x09, 0x00, 0x00, 0xB7, 0x09, 0x00, 0x00, + 0xBD, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xBF, 0x09, 0x00, 0x00, 0xBE, 0x09, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xC1, 0x09, 0x00, 0x00, 0xC2, 0x09, 0x00, 0x00, 0xBA, 0x09, 0x00, 0x00, + 0xD4, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x9E, 0x03, 0x00, 0x00, + 0xC3, 0x09, 0x00, 0x00, 0xC2, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0xC4, 0x09, 0x00, 0x00, 0xC3, 0x09, 0x00, 0x00, + 0x8E, 0x00, 0x05, 0x00, 0x9E, 0x03, 0x00, 0x00, 0xC8, 0x09, 0x00, 0x00, + 0xC3, 0x09, 0x00, 0x00, 0xC5, 0x09, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0xCA, 0x09, 0x00, 0x00, 0xC8, 0x09, 0x00, 0x00, + 0x6D, 0x1A, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xCB, 0x09, 0x00, 0x00, 0xCA, 0x09, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xD2, 0x09, 0x00, 0x00, 0xD3, 0x09, 0x00, 0x00, 0xBA, 0x09, 0x00, 0x00, + 0xD4, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD4, 0x09, 0x00, 0x00, 0xD3, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD5, 0x09, 0x00, 0x00, 0xD4, 0x09, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0x01, 0x0C, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x01, 0x0C, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x9E, 0x03, 0x00, 0x00, + 0x60, 0x0D, 0x00, 0x00, 0xBF, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x66, 0x0D, 0x00, 0x00, 0xC4, 0x09, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x9E, 0x03, 0x00, 0x00, 0x67, 0x0D, 0x00, 0x00, + 0x60, 0x0D, 0x00, 0x00, 0x66, 0x0D, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x6D, 0x0D, 0x00, 0x00, 0xCB, 0x09, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x9E, 0x03, 0x00, 0x00, 0x6E, 0x0D, 0x00, 0x00, + 0x67, 0x0D, 0x00, 0x00, 0x6D, 0x0D, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x6E, 0x0D, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x73, 0x0D, 0x00, 0x00, 0x6E, 0x0D, 0x00, 0x00, + 0x70, 0x0D, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x75, 0x0D, 0x00, 0x00, 0x70, 0x0D, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x7A, 0x0D, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, + 0xCE, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xA9, 0x01, 0x00, 0x00, + 0xB0, 0x13, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xB2, 0x13, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xB3, 0x13, 0x00, 0x00, + 0xB2, 0x13, 0x00, 0x00, 0x7A, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x7D, 0x0D, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, 0xDB, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, 0xB9, 0x13, 0x00, 0x00, + 0xB0, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xBA, 0x13, 0x00, 0x00, 0xB9, 0x13, 0x00, 0x00, 0x7D, 0x0D, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xC0, 0x13, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xC1, 0x13, 0x00, 0x00, + 0xC0, 0x13, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x82, 0x0D, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, 0xD5, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, 0xC7, 0x13, 0x00, 0x00, + 0xB0, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xC8, 0x13, 0x00, 0x00, 0xC7, 0x13, 0x00, 0x00, 0x82, 0x0D, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x88, 0x0D, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, + 0xE5, 0x01, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, + 0xD5, 0x13, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0xD6, 0x13, 0x00, 0x00, 0xD5, 0x13, 0x00, 0x00, + 0x88, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x8B, 0x0D, 0x00, 0x00, + 0x75, 0x0D, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xDC, 0x13, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xDD, 0x13, 0x00, 0x00, + 0xDC, 0x13, 0x00, 0x00, 0x8B, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x8E, 0x0D, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, 0xEB, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, 0xE3, 0x13, 0x00, 0x00, + 0xB0, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xE4, 0x13, 0x00, 0x00, 0xE3, 0x13, 0x00, 0x00, 0x8E, 0x0D, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x91, 0x0D, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, + 0xF1, 0x01, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, + 0xEA, 0x13, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0xEB, 0x13, 0x00, 0x00, 0xEA, 0x13, 0x00, 0x00, + 0x91, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x97, 0x0D, 0x00, 0x00, + 0x75, 0x0D, 0x00, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xF8, 0x13, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xF9, 0x13, 0x00, 0x00, + 0xF8, 0x13, 0x00, 0x00, 0x97, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x9A, 0x0D, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, 0xF7, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, 0xFF, 0x13, 0x00, 0x00, + 0xB0, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0xFF, 0x13, 0x00, 0x00, 0x9A, 0x0D, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x9D, 0x0D, 0x00, 0x00, 0x75, 0x0D, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, + 0x06, 0x14, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, + 0x9D, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xA0, 0x0D, 0x00, 0x00, + 0x75, 0x0D, 0x00, 0x00, 0x07, 0x04, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0x0D, 0x14, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0x0E, 0x14, 0x00, 0x00, + 0x0D, 0x14, 0x00, 0x00, 0xA0, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB3, 0x0D, 0x00, 0x00, 0xB3, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB5, 0x0D, 0x00, 0x00, + 0xB3, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB7, 0x0D, 0x00, 0x00, 0xB3, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x24, 0x14, 0x00, 0x00, 0xB3, 0x0D, 0x00, 0x00, 0xB3, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x27, 0x14, 0x00, 0x00, + 0xB5, 0x0D, 0x00, 0x00, 0xB5, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2A, 0x14, 0x00, 0x00, 0xB7, 0x0D, 0x00, 0x00, + 0xB7, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC0, 0x0D, 0x00, 0x00, 0xC8, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC2, 0x0D, 0x00, 0x00, + 0xC8, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC4, 0x0D, 0x00, 0x00, 0xC8, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2E, 0x14, 0x00, 0x00, 0xC0, 0x0D, 0x00, 0x00, 0xC0, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x31, 0x14, 0x00, 0x00, + 0xC2, 0x0D, 0x00, 0x00, 0xC2, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x34, 0x14, 0x00, 0x00, 0xC4, 0x0D, 0x00, 0x00, + 0xC4, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDA, 0x0D, 0x00, 0x00, 0xBA, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xDC, 0x0D, 0x00, 0x00, + 0xBA, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDE, 0x0D, 0x00, 0x00, 0xBA, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x42, 0x14, 0x00, 0x00, 0xDA, 0x0D, 0x00, 0x00, 0xDA, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x45, 0x14, 0x00, 0x00, + 0xDC, 0x0D, 0x00, 0x00, 0xDC, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, 0xDE, 0x0D, 0x00, 0x00, + 0xDE, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE7, 0x0D, 0x00, 0x00, 0xC1, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE9, 0x0D, 0x00, 0x00, + 0xC1, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xEB, 0x0D, 0x00, 0x00, 0xC1, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4C, 0x14, 0x00, 0x00, 0xE7, 0x0D, 0x00, 0x00, 0xE7, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4F, 0x14, 0x00, 0x00, + 0xE9, 0x0D, 0x00, 0x00, 0xE9, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x52, 0x14, 0x00, 0x00, 0xEB, 0x0D, 0x00, 0x00, + 0xEB, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF4, 0x0D, 0x00, 0x00, 0xD6, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF6, 0x0D, 0x00, 0x00, + 0xD6, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF8, 0x0D, 0x00, 0x00, 0xD6, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x56, 0x14, 0x00, 0x00, 0xF4, 0x0D, 0x00, 0x00, 0xF4, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x59, 0x14, 0x00, 0x00, + 0xF6, 0x0D, 0x00, 0x00, 0xF6, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5C, 0x14, 0x00, 0x00, 0xF8, 0x0D, 0x00, 0x00, + 0xF8, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x01, 0x0E, 0x00, 0x00, 0xDD, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x0E, 0x00, 0x00, + 0xDD, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x05, 0x0E, 0x00, 0x00, 0xDD, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x60, 0x14, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x14, 0x00, 0x00, + 0x03, 0x0E, 0x00, 0x00, 0x03, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x66, 0x14, 0x00, 0x00, 0x05, 0x0E, 0x00, 0x00, + 0x05, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0E, 0x0E, 0x00, 0x00, 0xE4, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x00, 0x00, + 0xE4, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x12, 0x0E, 0x00, 0x00, 0xE4, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6A, 0x14, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6D, 0x14, 0x00, 0x00, + 0x10, 0x0E, 0x00, 0x00, 0x10, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0x12, 0x0E, 0x00, 0x00, + 0x12, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1B, 0x0E, 0x00, 0x00, 0xEB, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1D, 0x0E, 0x00, 0x00, + 0xEB, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1F, 0x0E, 0x00, 0x00, 0xEB, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x74, 0x14, 0x00, 0x00, 0x1B, 0x0E, 0x00, 0x00, 0x1B, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x77, 0x14, 0x00, 0x00, + 0x1D, 0x0E, 0x00, 0x00, 0x1D, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7A, 0x14, 0x00, 0x00, 0x1F, 0x0E, 0x00, 0x00, + 0x1F, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x28, 0x0E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2A, 0x0E, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2C, 0x0E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7E, 0x14, 0x00, 0x00, 0x28, 0x0E, 0x00, 0x00, 0x28, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x81, 0x14, 0x00, 0x00, + 0x2A, 0x0E, 0x00, 0x00, 0x2A, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x84, 0x14, 0x00, 0x00, 0x2C, 0x0E, 0x00, 0x00, + 0x2C, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x35, 0x0E, 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x37, 0x0E, 0x00, 0x00, + 0x07, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x39, 0x0E, 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x88, 0x14, 0x00, 0x00, 0x35, 0x0E, 0x00, 0x00, 0x35, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8B, 0x14, 0x00, 0x00, + 0x37, 0x0E, 0x00, 0x00, 0x37, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8E, 0x14, 0x00, 0x00, 0x39, 0x0E, 0x00, 0x00, + 0x39, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4F, 0x0E, 0x00, 0x00, 0xF9, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x51, 0x0E, 0x00, 0x00, + 0xF9, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x53, 0x0E, 0x00, 0x00, 0xF9, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9C, 0x14, 0x00, 0x00, 0x4F, 0x0E, 0x00, 0x00, 0x4F, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9F, 0x14, 0x00, 0x00, + 0x51, 0x0E, 0x00, 0x00, 0x51, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA2, 0x14, 0x00, 0x00, 0x53, 0x0E, 0x00, 0x00, + 0x53, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5C, 0x0E, 0x00, 0x00, 0x0E, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5E, 0x0E, 0x00, 0x00, + 0x0E, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x60, 0x0E, 0x00, 0x00, 0x0E, 0x14, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA6, 0x14, 0x00, 0x00, 0x5C, 0x0E, 0x00, 0x00, 0x5C, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA9, 0x14, 0x00, 0x00, + 0x5E, 0x0E, 0x00, 0x00, 0x5E, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAC, 0x14, 0x00, 0x00, 0x60, 0x0E, 0x00, 0x00, + 0x60, 0x0E, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCA, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x45, 0x14, 0x00, 0x00, 0x4F, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCB, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x27, 0x14, 0x00, 0x00, 0xCA, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD1, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x59, 0x14, 0x00, 0x00, + 0x77, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD2, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xCB, 0x14, 0x00, 0x00, 0xD1, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF4, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x45, 0x14, 0x00, 0x00, 0x4F, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF5, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x14, 0x00, 0x00, + 0xF4, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFB, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x59, 0x14, 0x00, 0x00, 0x77, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0xF5, 0x14, 0x00, 0x00, 0xFB, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x4F, 0x14, 0x00, 0x00, + 0x59, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1F, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x31, 0x14, 0x00, 0x00, 0x1E, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x25, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x63, 0x14, 0x00, 0x00, 0x81, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x1F, 0x15, 0x00, 0x00, + 0x25, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x48, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x4F, 0x14, 0x00, 0x00, 0x59, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x49, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x31, 0x14, 0x00, 0x00, 0x48, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4F, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x63, 0x14, 0x00, 0x00, + 0x81, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x50, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x49, 0x15, 0x00, 0x00, 0x4F, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x72, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x6D, 0x14, 0x00, 0x00, 0x77, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x73, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x4F, 0x14, 0x00, 0x00, + 0x72, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x79, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x81, 0x14, 0x00, 0x00, 0x9F, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7A, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x73, 0x15, 0x00, 0x00, 0x79, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9C, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x6D, 0x14, 0x00, 0x00, + 0x77, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9D, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x4F, 0x14, 0x00, 0x00, 0x9C, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA3, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x81, 0x14, 0x00, 0x00, 0x9F, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA4, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x9D, 0x15, 0x00, 0x00, + 0xA3, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC6, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x77, 0x14, 0x00, 0x00, 0x81, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC7, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x59, 0x14, 0x00, 0x00, 0xC6, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCD, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x8B, 0x14, 0x00, 0x00, + 0xA9, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCE, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xC7, 0x15, 0x00, 0x00, 0xCD, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF0, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x77, 0x14, 0x00, 0x00, 0x81, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF1, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x59, 0x14, 0x00, 0x00, + 0xF0, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF7, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x8B, 0x14, 0x00, 0x00, 0xA9, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF8, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0xF1, 0x15, 0x00, 0x00, 0xF7, 0x15, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x16, 0x00, 0x00, + 0xFC, 0x14, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x18, 0x16, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, 0x17, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x19, 0x16, 0x00, 0x00, + 0x18, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x16, 0x00, 0x00, 0x50, 0x15, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x39, 0x16, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, + 0x38, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3A, 0x16, 0x00, 0x00, 0x39, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x59, 0x16, 0x00, 0x00, 0xA4, 0x15, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5A, 0x16, 0x00, 0x00, + 0x8B, 0x01, 0x00, 0x00, 0x59, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5B, 0x16, 0x00, 0x00, 0x5A, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7A, 0x16, 0x00, 0x00, + 0xF8, 0x15, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7B, 0x16, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, 0x7A, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7C, 0x16, 0x00, 0x00, + 0x7B, 0x16, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB8, 0x0F, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB9, 0x0F, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xD2, 0x14, 0x00, 0x00, + 0xB8, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBB, 0x0F, 0x00, 0x00, 0xB9, 0x0F, 0x00, 0x00, 0x19, 0x16, 0x00, 0x00, + 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA6, 0x16, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xBB, 0x0F, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD0, 0x0F, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x50, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD1, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x26, 0x15, 0x00, 0x00, 0xD0, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD3, 0x0F, 0x00, 0x00, 0xD1, 0x0F, 0x00, 0x00, + 0x3A, 0x16, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD9, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xD3, 0x0F, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE8, 0x0F, 0x00, 0x00, + 0x79, 0x01, 0x00, 0x00, 0xA4, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE9, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x7A, 0x15, 0x00, 0x00, 0xE8, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xEB, 0x0F, 0x00, 0x00, + 0xE9, 0x0F, 0x00, 0x00, 0x5B, 0x16, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0C, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0xEB, 0x0F, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, + 0x79, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0xF8, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xCE, 0x15, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x7C, 0x16, 0x00, 0x00, + 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3F, 0x17, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6D, 0x17, 0x00, 0x00, 0xA6, 0x16, 0x00, 0x00, + 0xC2, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6F, 0x17, 0x00, 0x00, + 0x6D, 0x17, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x71, 0x17, 0x00, 0x00, 0x6F, 0x17, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x72, 0x17, 0x00, 0x00, 0x71, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA0, 0x17, 0x00, 0x00, 0xD9, 0x16, 0x00, 0x00, + 0xC2, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA2, 0x17, 0x00, 0x00, + 0xA0, 0x17, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA4, 0x17, 0x00, 0x00, 0xA2, 0x17, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA5, 0x17, 0x00, 0x00, 0xA4, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD3, 0x17, 0x00, 0x00, 0x0C, 0x17, 0x00, 0x00, + 0xC2, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD5, 0x17, 0x00, 0x00, + 0xD3, 0x17, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD7, 0x17, 0x00, 0x00, 0xD5, 0x17, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD8, 0x17, 0x00, 0x00, 0xD7, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x3F, 0x17, 0x00, 0x00, + 0xC2, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x18, 0x00, 0x00, + 0x06, 0x18, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0A, 0x18, 0x00, 0x00, 0x08, 0x18, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0B, 0x18, 0x00, 0x00, 0x0A, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0xD5, 0x09, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2D, 0x10, 0x00, 0x00, + 0x72, 0x17, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0xA5, 0x17, 0x00, 0x00, + 0x27, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3F, 0x10, 0x00, 0x00, 0xD8, 0x17, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x48, 0x10, 0x00, 0x00, + 0x0B, 0x18, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4E, 0x10, 0x00, 0x00, 0x73, 0x0D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4F, 0x10, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x4E, 0x10, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x52, 0x10, 0x00, 0x00, + 0x73, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x53, 0x10, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x52, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x54, 0x10, 0x00, 0x00, 0x4F, 0x10, 0x00, 0x00, 0x53, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5B, 0x10, 0x00, 0x00, + 0x4E, 0x10, 0x00, 0x00, 0x53, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x00, 0x4F, 0x10, 0x00, 0x00, + 0x52, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x67, 0x10, 0x00, 0x00, 0x4E, 0x10, 0x00, 0x00, 0x52, 0x10, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6B, 0x10, 0x00, 0x00, + 0xFC, 0x14, 0x00, 0x00, 0xD2, 0x14, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6C, 0x10, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x00, + 0x6B, 0x10, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x34, 0x18, 0x00, 0x00, 0x6C, 0x10, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x35, 0x18, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, + 0x34, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x36, 0x18, 0x00, 0x00, 0x35, 0x18, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6F, 0x10, 0x00, 0x00, 0x54, 0x10, 0x00, 0x00, + 0x36, 0x18, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x73, 0x10, 0x00, 0x00, 0x50, 0x15, 0x00, 0x00, 0x26, 0x15, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x74, 0x10, 0x00, 0x00, + 0x0A, 0x08, 0x00, 0x00, 0x73, 0x10, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3F, 0x18, 0x00, 0x00, 0x74, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x18, 0x00, 0x00, + 0x8B, 0x01, 0x00, 0x00, 0x3F, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x41, 0x18, 0x00, 0x00, 0x40, 0x18, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x77, 0x10, 0x00, 0x00, + 0x5B, 0x10, 0x00, 0x00, 0x41, 0x18, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7B, 0x10, 0x00, 0x00, 0xA4, 0x15, 0x00, 0x00, + 0x7A, 0x15, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7C, 0x10, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x00, 0x7B, 0x10, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x18, 0x00, 0x00, + 0x7C, 0x10, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4B, 0x18, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, 0x4A, 0x18, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4C, 0x18, 0x00, 0x00, + 0x4B, 0x18, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7F, 0x10, 0x00, 0x00, 0x62, 0x10, 0x00, 0x00, 0x4C, 0x18, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, + 0xF8, 0x15, 0x00, 0x00, 0xCE, 0x15, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x00, + 0x83, 0x10, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x55, 0x18, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x56, 0x18, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, + 0x55, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x57, 0x18, 0x00, 0x00, 0x56, 0x18, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x87, 0x10, 0x00, 0x00, 0x67, 0x10, 0x00, 0x00, + 0x57, 0x18, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8D, 0x10, 0x00, 0x00, 0x2D, 0x10, 0x00, 0x00, 0x6F, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x96, 0x10, 0x00, 0x00, + 0x36, 0x10, 0x00, 0x00, 0x77, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, 0x3F, 0x10, 0x00, 0x00, + 0x7F, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA9, 0x10, 0x00, 0x00, 0x96, 0x10, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xAB, 0x10, 0x00, 0x00, + 0xA9, 0x10, 0x00, 0x00, 0x6F, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC3, 0x10, 0x00, 0x00, 0x48, 0x10, 0x00, 0x00, + 0x87, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC4, 0x10, 0x00, 0x00, 0x8D, 0x10, 0x00, 0x00, 0xC3, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC6, 0x10, 0x00, 0x00, + 0xC4, 0x10, 0x00, 0x00, 0x77, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE1, 0x10, 0x00, 0x00, 0xC4, 0x10, 0x00, 0x00, + 0x7F, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFC, 0x10, 0x00, 0x00, 0xA9, 0x10, 0x00, 0x00, 0x87, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6E, 0x1A, 0x00, 0x00, + 0x8D, 0x10, 0x00, 0x00, 0x96, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6F, 0x1A, 0x00, 0x00, 0x6E, 0x1A, 0x00, 0x00, + 0xA8, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x70, 0x1A, 0x00, 0x00, 0x6F, 0x1A, 0x00, 0x00, 0xC3, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x11, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x70, 0x1A, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x28, 0x11, 0x00, 0x00, 0x26, 0x11, 0x00, 0x00, + 0xAB, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2A, 0x11, 0x00, 0x00, 0x28, 0x11, 0x00, 0x00, 0xC6, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2C, 0x11, 0x00, 0x00, + 0x2A, 0x11, 0x00, 0x00, 0xE1, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2E, 0x11, 0x00, 0x00, 0x2C, 0x11, 0x00, 0x00, + 0xFC, 0x10, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6E, 0x18, 0x00, 0x00, 0x2E, 0x11, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6F, 0x18, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, + 0x6E, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x00, 0x00, 0x6F, 0x18, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x73, 0x18, 0x00, 0x00, 0x70, 0x18, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x75, 0x18, 0x00, 0x00, + 0x73, 0x18, 0x00, 0x00, 0x2E, 0x11, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x77, 0x18, 0x00, 0x00, 0x75, 0x18, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x78, 0x18, 0x00, 0x00, 0x70, 0x18, 0x00, 0x00, 0x77, 0x18, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x71, 0x1A, 0x00, 0x00, + 0x24, 0x14, 0x00, 0x00, 0x42, 0x14, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x38, 0x11, 0x00, 0x00, 0x8D, 0x10, 0x00, 0x00, + 0x71, 0x1A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3C, 0x11, 0x00, 0x00, 0x2E, 0x14, 0x00, 0x00, 0x96, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3D, 0x11, 0x00, 0x00, + 0x38, 0x11, 0x00, 0x00, 0x3C, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x41, 0x11, 0x00, 0x00, 0x60, 0x14, 0x00, 0x00, + 0x96, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x42, 0x11, 0x00, 0x00, 0x3D, 0x11, 0x00, 0x00, 0x41, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x46, 0x11, 0x00, 0x00, + 0x6A, 0x14, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x42, 0x11, 0x00, 0x00, + 0x46, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4B, 0x11, 0x00, 0x00, 0x9C, 0x14, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4C, 0x11, 0x00, 0x00, + 0x47, 0x11, 0x00, 0x00, 0x4B, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x50, 0x11, 0x00, 0x00, 0x88, 0x14, 0x00, 0x00, + 0xC3, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x51, 0x11, 0x00, 0x00, 0x4C, 0x11, 0x00, 0x00, 0x50, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x55, 0x11, 0x00, 0x00, + 0xA6, 0x14, 0x00, 0x00, 0xC3, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x56, 0x11, 0x00, 0x00, 0x51, 0x11, 0x00, 0x00, + 0x55, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5A, 0x11, 0x00, 0x00, 0x4C, 0x14, 0x00, 0x00, 0xAB, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5B, 0x11, 0x00, 0x00, + 0x56, 0x11, 0x00, 0x00, 0x5A, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5F, 0x11, 0x00, 0x00, 0x56, 0x14, 0x00, 0x00, + 0xC6, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x60, 0x11, 0x00, 0x00, 0x5B, 0x11, 0x00, 0x00, 0x5F, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x64, 0x11, 0x00, 0x00, + 0x74, 0x14, 0x00, 0x00, 0xE1, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x65, 0x11, 0x00, 0x00, 0x60, 0x11, 0x00, 0x00, + 0x64, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x69, 0x11, 0x00, 0x00, 0x7E, 0x14, 0x00, 0x00, 0xFC, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6A, 0x11, 0x00, 0x00, + 0x65, 0x11, 0x00, 0x00, 0x69, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6C, 0x11, 0x00, 0x00, 0x6A, 0x11, 0x00, 0x00, + 0x78, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x86, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x6C, 0x11, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x72, 0x1A, 0x00, 0x00, + 0x27, 0x14, 0x00, 0x00, 0x45, 0x14, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x76, 0x11, 0x00, 0x00, 0x8D, 0x10, 0x00, 0x00, + 0x72, 0x1A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7A, 0x11, 0x00, 0x00, 0x31, 0x14, 0x00, 0x00, 0x96, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7B, 0x11, 0x00, 0x00, + 0x76, 0x11, 0x00, 0x00, 0x7A, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7F, 0x11, 0x00, 0x00, 0x63, 0x14, 0x00, 0x00, + 0x96, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x80, 0x11, 0x00, 0x00, 0x7B, 0x11, 0x00, 0x00, 0x7F, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x84, 0x11, 0x00, 0x00, + 0x6D, 0x14, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x85, 0x11, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, + 0x84, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x89, 0x11, 0x00, 0x00, 0x9F, 0x14, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8A, 0x11, 0x00, 0x00, + 0x85, 0x11, 0x00, 0x00, 0x89, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8E, 0x11, 0x00, 0x00, 0x8B, 0x14, 0x00, 0x00, + 0xC3, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8F, 0x11, 0x00, 0x00, 0x8A, 0x11, 0x00, 0x00, 0x8E, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x93, 0x11, 0x00, 0x00, + 0xA9, 0x14, 0x00, 0x00, 0xC3, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x94, 0x11, 0x00, 0x00, 0x8F, 0x11, 0x00, 0x00, + 0x93, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x98, 0x11, 0x00, 0x00, 0x4F, 0x14, 0x00, 0x00, 0xAB, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x99, 0x11, 0x00, 0x00, + 0x94, 0x11, 0x00, 0x00, 0x98, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9D, 0x11, 0x00, 0x00, 0x59, 0x14, 0x00, 0x00, + 0xC6, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9E, 0x11, 0x00, 0x00, 0x99, 0x11, 0x00, 0x00, 0x9D, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA2, 0x11, 0x00, 0x00, + 0x77, 0x14, 0x00, 0x00, 0xE1, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA3, 0x11, 0x00, 0x00, 0x9E, 0x11, 0x00, 0x00, + 0xA2, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA7, 0x11, 0x00, 0x00, 0x81, 0x14, 0x00, 0x00, 0xFC, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA8, 0x11, 0x00, 0x00, + 0xA3, 0x11, 0x00, 0x00, 0xA7, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAA, 0x11, 0x00, 0x00, 0xA8, 0x11, 0x00, 0x00, + 0x78, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x94, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xAA, 0x11, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x73, 0x1A, 0x00, 0x00, + 0x2A, 0x14, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB4, 0x11, 0x00, 0x00, 0x8D, 0x10, 0x00, 0x00, + 0x73, 0x1A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB8, 0x11, 0x00, 0x00, 0x34, 0x14, 0x00, 0x00, 0x96, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB9, 0x11, 0x00, 0x00, + 0xB4, 0x11, 0x00, 0x00, 0xB8, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBD, 0x11, 0x00, 0x00, 0x66, 0x14, 0x00, 0x00, + 0x96, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBE, 0x11, 0x00, 0x00, 0xB9, 0x11, 0x00, 0x00, 0xBD, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC2, 0x11, 0x00, 0x00, + 0x70, 0x14, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC3, 0x11, 0x00, 0x00, 0xBE, 0x11, 0x00, 0x00, + 0xC2, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC7, 0x11, 0x00, 0x00, 0xA2, 0x14, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC8, 0x11, 0x00, 0x00, + 0xC3, 0x11, 0x00, 0x00, 0xC7, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCC, 0x11, 0x00, 0x00, 0x8E, 0x14, 0x00, 0x00, + 0xC3, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCD, 0x11, 0x00, 0x00, 0xC8, 0x11, 0x00, 0x00, 0xCC, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD1, 0x11, 0x00, 0x00, + 0xAC, 0x14, 0x00, 0x00, 0xC3, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD2, 0x11, 0x00, 0x00, 0xCD, 0x11, 0x00, 0x00, + 0xD1, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD6, 0x11, 0x00, 0x00, 0x52, 0x14, 0x00, 0x00, 0xAB, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD7, 0x11, 0x00, 0x00, + 0xD2, 0x11, 0x00, 0x00, 0xD6, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDB, 0x11, 0x00, 0x00, 0x5C, 0x14, 0x00, 0x00, + 0xC6, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDC, 0x11, 0x00, 0x00, 0xD7, 0x11, 0x00, 0x00, 0xDB, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE0, 0x11, 0x00, 0x00, + 0x7A, 0x14, 0x00, 0x00, 0xE1, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE1, 0x11, 0x00, 0x00, 0xDC, 0x11, 0x00, 0x00, + 0xE0, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE5, 0x11, 0x00, 0x00, 0x84, 0x14, 0x00, 0x00, 0xFC, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE6, 0x11, 0x00, 0x00, + 0xE1, 0x11, 0x00, 0x00, 0xE5, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE8, 0x11, 0x00, 0x00, 0xE6, 0x11, 0x00, 0x00, + 0x78, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA2, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xE8, 0x11, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0xEF, 0x09, 0x00, 0x00, 0xF0, 0x09, 0x00, 0x00, + 0xE1, 0x09, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0xF0, 0x09, 0x00, 0x00, 0x86, 0x18, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xEF, 0x09, 0x00, 0x00, 0xF2, 0x09, 0x00, 0x00, 0xE1, 0x09, 0x00, 0x00, + 0x52, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF2, 0x09, 0x00, 0x00, + 0x94, 0x18, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xEF, 0x09, 0x00, 0x00, + 0xF4, 0x09, 0x00, 0x00, 0xE1, 0x09, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0xF4, 0x09, 0x00, 0x00, 0xA2, 0x18, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xF5, 0x09, 0x00, 0x00, + 0xE1, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, + 0xF6, 0x09, 0x00, 0x00, 0xF5, 0x09, 0x00, 0x00, 0xF5, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x32, 0x00, 0x00, 0x00, 0xF7, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF6, 0x09, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xF8, 0x09, 0x00, 0x00, + 0xE1, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xF9, 0x09, 0x00, 0x00, 0xF8, 0x09, 0x00, 0x00, 0xF7, 0x09, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE1, 0x09, 0x00, 0x00, + 0xF9, 0x09, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xFA, 0x09, 0x00, 0x00, 0xE1, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, + 0x32, 0x00, 0x00, 0x00, 0xFB, 0x09, 0x00, 0x00, 0xFA, 0x09, 0x00, 0x00, + 0xFA, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xAE, 0x18, 0x00, 0x00, 0xBF, 0x09, 0x00, 0x00, 0x7D, 0x1A, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB0, 0x18, 0x00, 0x00, + 0xAE, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB1, 0x18, 0x00, 0x00, 0xB0, 0x18, 0x00, 0x00, + 0x55, 0x01, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB3, 0x18, 0x00, 0x00, 0xAE, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB4, 0x18, 0x00, 0x00, + 0xB1, 0x18, 0x00, 0x00, 0xB3, 0x18, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0xA9, 0x18, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0xB5, 0x18, 0x00, 0x00, 0xA9, 0x18, 0x00, 0x00, + 0xB4, 0x18, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB6, 0x18, 0x00, 0x00, 0xB5, 0x18, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x32, 0x00, 0x00, 0x00, 0xFF, 0x09, 0x00, 0x00, 0xB6, 0x18, 0x00, 0x00, + 0xB6, 0x18, 0x00, 0x00, 0xB6, 0x18, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0xFB, 0x09, 0x00, 0x00, + 0xFF, 0x09, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x03, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, 0x02, 0x0A, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, 0x04, 0x0A, 0x00, 0x00, + 0xE1, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0x05, 0x0A, 0x00, 0x00, 0x04, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE1, 0x09, 0x00, 0x00, + 0x05, 0x0A, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xEF, 0x09, 0x00, 0x00, + 0x07, 0x0A, 0x00, 0x00, 0xE1, 0x09, 0x00, 0x00, 0x06, 0x0A, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x07, 0x0A, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..caa33928396f2bc3911018e38773461394561f83 GIT binary patch literal 14540 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 literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.txt new file mode 100644 index 000000000..947758262 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.txt @@ -0,0 +1,728 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 6782 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %xe_frag_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 420 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %xe_texture "xe_texture" + OpName %gl_FragCoord "gl_FragCoord" + OpName %XeCasResampleConstants "XeCasResampleConstants" + OpMemberName %XeCasResampleConstants 0 "xe_cas_output_offset" + OpMemberName %XeCasResampleConstants 1 "xe_cas_input_output_size_ratio" + OpMemberName %XeCasResampleConstants 2 "xe_cas_sharpness_post_setup" + OpName %_ "" + OpName %xe_frag_color "xe_frag_color" + OpDecorate %xe_texture DescriptorSet 0 + OpDecorate %xe_texture Binding 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpMemberDecorate %XeCasResampleConstants 0 Offset 16 + OpMemberDecorate %XeCasResampleConstants 1 Offset 24 + OpMemberDecorate %XeCasResampleConstants 2 Offset 32 + OpDecorate %XeCasResampleConstants Block + OpDecorate %xe_frag_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %v3float = OpTypeVector %float 3 + %uint_15 = OpConstant %uint 15 + %uint_256 = OpConstant %uint 256 +%_arr_float_uint_256 = OpTypeArray %float %uint_256 +%float_n0_00100337015 = OpConstant %float -0.00100337015 +%float_0_000880821084 = OpConstant %float 0.000880821084 +%float_0_00169270835 = OpConstant %float 0.00169270835 +%float_n0_00155484071 = OpConstant %float -0.00155484071 +%float_0_00127910543 = OpConstant %float 0.00127910543 +%float_n0_000605085806 = OpConstant %float -0.000605085806 +%float_0_00106464466 = OpConstant %float 0.00106464466 +%float_n0_00138633582 = OpConstant %float -0.00138633582 +%float_0_00135569857 = OpConstant %float 0.00135569857 +%float_0_000513174047 = OpConstant %float 0.000513174047 +%float_0_00121783093 = OpConstant %float 0.00121783093 +%float_n0_00160079659 = OpConstant %float -0.00160079659 +%float_0_00058976718 = OpConstant %float 0.00058976718 +%float_n0_00028339462 = OpConstant %float -0.00028339462 +%float_0_00111060054 = OpConstant %float 0.00111060054 +%float_n0_00141697307 = OpConstant %float -0.00141697307 +%float_0_00144761032 = OpConstant %float 0.00144761032 +%float_n0_0005438113 = OpConstant %float -0.0005438113 +%float_0_00013020834 = OpConstant %float 0.00013020834 +%float_n0_0012025123 = OpConstant %float -0.0012025123 +%float_0_000436580885 = OpConstant %float 0.000436580885 +%float_n0_00104932603 = OpConstant %float -0.00104932603 +%float_0_000620404433 = OpConstant %float 0.000620404433 +%float_n0_000482536765 = OpConstant %float -0.000482536765 +%float_0_00187653187 = OpConstant %float 0.00187653187 +%float_n0_00109528191 = OpConstant %float -0.00109528191 +%float_n9_95710798en05 = OpConstant %float -9.95710798e-05 +%float_n0_000528492674 = OpConstant %float -0.000528492674 +%float_0_0014322917 = OpConstant %float 0.0014322917 +%float_n0_00193780637 = OpConstant %float -0.00193780637 +%float_n0_000696997566 = OpConstant %float -0.000696997566 +%float_3_829657en05 = OpConstant %float 3.829657e-05 +%float_0_000712316192 = OpConstant %float 0.000712316192 +%float_n0_00130974269 = OpConstant %float -0.00130974269 +%float_0_00109528191 = OpConstant %float 0.00109528191 +%float_n0_000298713247 = OpConstant %float -0.000298713247 +%float_0_00175398286 = OpConstant %float 0.00175398286 +%float_n0_00167738972 = OpConstant %float -0.00167738972 +%float_0_00147824758 = OpConstant %float 0.00147824758 +%float_n3_829657en05 = OpConstant %float -3.829657e-05 +%float_0_000788909325 = OpConstant %float 0.000788909325 +%float_n0_00183057599 = OpConstant %float -0.00183057599 +%float_0_000298713247 = OpConstant %float 0.000298713247 +%float_0_000988051528 = OpConstant %float 0.000988051528 +%float_n0_00117187505 = OpConstant %float -0.00117187505 +%float_0_00017616422 = OpConstant %float 0.00017616422 +%float_0_00164675247 = OpConstant %float 0.00164675247 +%float_n0_00158547796 = OpConstant %float -0.00158547796 +%float_0_000344669126 = OpConstant %float 0.000344669126 +%float_0_00186121324 = OpConstant %float 0.00186121324 +%float_n0_00176930148 = OpConstant %float -0.00176930148 +%float_n0_000865502458 = OpConstant %float -0.000865502458 +%float_0_000896139711 = OpConstant %float 0.000896139711 +%float_0_000160845593 = OpConstant %float 0.000160845593 +%float_n0_000926776964 = OpConstant %float -0.000926776964 +%float_n0_00152420346 = OpConstant %float -0.00152420346 +%float_n0_000651041686 = OpConstant %float -0.000651041686 +%float_0_00129442406 = OpConstant %float 0.00129442406 +%float_n0_000804227951 = OpConstant %float -0.000804227951 +%float_n0_00146292895 = OpConstant %float -0.00146292895 +%float_0_00179993873 = OpConstant %float 0.00179993873 +%float_n0_000850183831 = OpConstant %float -0.000850183831 +%float_0_000850183831 = OpConstant %float 0.000850183831 +%float_n0_000451899512 = OpConstant %float -0.000451899512 +%float_n0_00106464466 = OpConstant %float -0.00106464466 +%float_n0_000145526967 = OpConstant %float -0.000145526967 +%float_0_000237438726 = OpConstant %float 0.000237438726 +%float_0_00141697307 = OpConstant %float 0.00141697307 +%float_n0_00058976718 = OpConstant %float -0.00058976718 +%float_n0_000191482846 = OpConstant %float -0.000191482846 +%float_0_00160079659 = OpConstant %float 0.00160079659 +%float_0_00101868878 = OpConstant %float 0.00101868878 +%float_0_000405943632 = OpConstant %float 0.000405943632 +%float_n0_000206801473 = OpConstant %float -0.000206801473 +%float_0_00158547796 = OpConstant %float 0.00158547796 +%float_0_000651041686 = OpConstant %float 0.000651041686 +%float_n6_89338267en05 = OpConstant %float -6.89338267e-05 +%float_0_000421262259 = OpConstant %float 0.000421262259 +%float_n0_00164675247 = OpConstant %float -0.00164675247 +%float_0_00137101719 = OpConstant %float 0.00137101719 +%float_0_000926776964 = OpConstant %float 0.000926776964 +%float_n0_000666360313 = OpConstant %float -0.000666360313 +%float_0_00118719367 = OpConstant %float 0.00118719367 +%float_n0_00144761032 = OpConstant %float -0.00144761032 +%float_0_000574448553 = OpConstant %float 0.000574448553 +%float_n0_00189185049 = OpConstant %float -0.00189185049 +%float_0_000758272072 = OpConstant %float 0.000758272072 +%float_n0_00129442406 = OpConstant %float -0.00129442406 +%float_0_00192248775 = OpConstant %float 0.00192248775 +%float_n0_0016620711 = OpConstant %float -0.0016620711 +%float_n0_00103400741 = OpConstant %float -0.00103400741 +%float_n0_000497855421 = OpConstant %float -0.000497855421 +%float_n0_00186121324 = OpConstant %float -0.00186121324 +%float_0_0012025123 = OpConstant %float 0.0012025123 +%float_n0_0003293505 = OpConstant %float -0.0003293505 +%float_n0_00137101719 = OpConstant %float -0.00137101719 +%float_0_00163143384 = OpConstant %float 0.00163143384 +%float_n0_00184589461 = OpConstant %float -0.00184589461 +%float_0_000727634819 = OpConstant %float 0.000727634819 +%float_n0_000911458337 = OpConstant %float -0.000911458337 +%float_0_00181525736 = OpConstant %float 0.00181525736 +%float_n0_00114123779 = OpConstant %float -0.00114123779 +%float_n0_000375306379 = OpConstant %float -0.000375306379 +%float_9_95710798en05 = OpConstant %float 9.95710798e-05 +%float_n0_000742953445 = OpConstant %float -0.000742953445 +%float_0_00117187505 = OpConstant %float 0.00117187505 +%float_6_89338267en05 = OpConstant %float 6.89338267e-05 +%float_0_0014935662 = OpConstant %float 0.0014935662 +%float_0_000972732843 = OpConstant %float 0.000972732843 +%float_n0_000957414217 = OpConstant %float -0.000957414217 +%float_0_00193780637 = OpConstant %float 0.00193780637 +%float_0_000528492674 = OpConstant %float 0.000528492674 +%float_5_36151965en05 = OpConstant %float 5.36151965e-05 +%float_n0_00124846818 = OpConstant %float -0.00124846818 +%float_n0_000268075994 = OpConstant %float -0.000268075994 +%float_0_00153952208 = OpConstant %float 0.00153952208 +%float_n7_65931418en06 = OpConstant %float -7.65931418e-06 +%float_0_000314031873 = OpConstant %float 0.000314031873 +%float_0_00134037994 = OpConstant %float 0.00134037994 +%float_n0_00175398286 = OpConstant %float -0.00175398286 +%float_0_000497855421 = OpConstant %float 0.000497855421 +%float_n0_00118719367 = OpConstant %float -0.00118719367 +%float_0_000773590698 = OpConstant %float 0.000773590698 +%float_n0_00134037994 = OpConstant %float -0.00134037994 +%float_0_000268075994 = OpConstant %float 0.000268075994 +%float_n0_00147824758 = OpConstant %float -0.00147824758 +%float_n0_00013020834 = OpConstant %float -0.00013020834 +%float_n0_000773590698 = OpConstant %float -0.000773590698 +%float_0_00130974269 = OpConstant %float 0.00130974269 +%float_0_000390625006 = OpConstant %float 0.000390625006 +%float_0_000957414217 = OpConstant %float 0.000957414217 +%float_n0_000467218139 = OpConstant %float -0.000467218139 +%float_n0_00153952208 = OpConstant %float -0.00153952208 +%float_0_00103400741 = OpConstant %float 0.00103400741 +%float_n0_000681678939 = OpConstant %float -0.000681678939 +%float_0_00167738972 = OpConstant %float 0.00167738972 +%float_0_00100337015 = OpConstant %float 0.00100337015 +%float_n0_000421262259 = OpConstant %float -0.000421262259 +%float_0_00178462011 = OpConstant %float 0.00178462011 +%float_n0_000237438726 = OpConstant %float -0.000237438726 +%float_n0_000620404433 = OpConstant %float -0.000620404433 +%float_0_0016620711 = OpConstant %float 0.0016620711 +%float_0_000834865205 = OpConstant %float 0.000834865205 +%float_n0_0017233456 = OpConstant %float -0.0017233456 +%float_n0_00107996329 = OpConstant %float -0.00107996329 +%float_0_00176930148 = OpConstant %float 0.00176930148 +%float_n0_000788909325 = OpConstant %float -0.000788909325 +%float_n0_00178462011 = OpConstant %float -0.00178462011 +%float_0_000681678939 = OpConstant %float 0.000681678939 +%float_n0_000988051528 = OpConstant %float -0.000988051528 +%float_n0_00132506131 = OpConstant %float -0.00132506131 +%float_n0_00017616422 = OpConstant %float -0.00017616422 +%float_n0_00150888483 = OpConstant %float -0.00150888483 +%float_0_0003293505 = OpConstant %float 0.0003293505 +%float_n0_001953125 = OpConstant %float -0.001953125 +%float_0_000666360313 = OpConstant %float 0.000666360313 +%float_n0_00161611522 = OpConstant %float -0.00161611522 +%float_0_00115655642 = OpConstant %float 0.00115655642 +%float_0_000451899512 = OpConstant %float 0.000451899512 +%float_n0_000436580885 = OpConstant %float -0.000436580885 +%float_0_000191482846 = OpConstant %float 0.000191482846 +%float_n0_0014935662 = OpConstant %float -0.0014935662 +%float_0_00114123779 = OpConstant %float 0.00114123779 +%float_8_42524532en05 = OpConstant %float 8.42524532e-05 +%float_0_00189185049 = OpConstant %float 0.00189185049 +%float_0_00140165444 = OpConstant %float 0.00140165444 +%float_0_000559129927 = OpConstant %float 0.000559129927 +%float_0_000114889706 = OpConstant %float 0.000114889706 +%float_0_00126378681 = OpConstant %float 0.00126378681 +%float_n0_000574448553 = OpConstant %float -0.000574448553 +%float_n0_000972732843 = OpConstant %float -0.000972732843 +%float_0_00132506131 = OpConstant %float 0.00132506131 +%float_0_000222120099 = OpConstant %float 0.000222120099 +%float_n0_000758272072 = OpConstant %float -0.000758272072 +%float_n0_00135569857 = OpConstant %float -0.00135569857 +%float_0_00146292895 = OpConstant %float 0.00146292895 +%float_0_000865502458 = OpConstant %float 0.000865502458 +%float_n0_000359987753 = OpConstant %float -0.000359987753 +%float_0_0005438113 = OpConstant %float 0.0005438113 +%float_n0_00112591917 = OpConstant %float -0.00112591917 +%float_n0_000252757367 = OpConstant %float -0.000252757367 +%float_n0_000559129927 = OpConstant %float -0.000559129927 +%float_n0_00181525736 = OpConstant %float -0.00181525736 +%float_0_0017233456 = OpConstant %float 0.0017233456 +%float_n0_00115655642 = OpConstant %float -0.00115655642 +%float_0_000742953445 = OpConstant %float 0.000742953445 +%float_0_00157015934 = OpConstant %float 0.00157015934 +%float_n0_000114889706 = OpConstant %float -0.000114889706 +%float_n0_00121783093 = OpConstant %float -0.00121783093 +%float_0_00183057599 = OpConstant %float 0.00183057599 +%float_2_29779416en05 = OpConstant %float 2.29779416e-05 +%float_n0_00192248775 = OpConstant %float -0.00192248775 +%float_0_00173866423 = OpConstant %float 0.00173866423 +%float_n0_000712316192 = OpConstant %float -0.000712316192 +%float_0_00155484071 = OpConstant %float 0.00155484071 +%float_n0_00170802698 = OpConstant %float -0.00170802698 +%float_0_00123314955 = OpConstant %float 0.00123314955 +%float_0_000206801473 = OpConstant %float 0.000206801473 +%float_0_00104932603 = OpConstant %float 0.00104932603 +%float_n0_000727634819 = OpConstant %float -0.000727634819 +%float_n0_00163143384 = OpConstant %float -0.00163143384 +%float_n0_000314031873 = OpConstant %float -0.000314031873 +%float_0_000482536765 = OpConstant %float 0.000482536765 +%float_n0_00179993873 = OpConstant %float -0.00179993873 +%float_0_00094209559 = OpConstant %float 0.00094209559 +%float_n0_000344669126 = OpConstant %float -0.000344669126 +%float_0_000696997566 = OpConstant %float 0.000696997566 +%float_n0_00101868878 = OpConstant %float -0.00101868878 +%float_n0_00157015934 = OpConstant %float -0.00157015934 +%float_n2_29779416en05 = OpConstant %float -2.29779416e-05 +%float_n0_00127910543 = OpConstant %float -0.00127910543 +%float_0_000804227951 = OpConstant %float 0.000804227951 +%float_n0_000896139711 = OpConstant %float -0.000896139711 +%float_n0_0014322917 = OpConstant %float -0.0014322917 +%float_0_000605085806 = OpConstant %float 0.000605085806 +%float_n8_42524532en05 = OpConstant %float -8.42524532e-05 +%float_0_000911458337 = OpConstant %float 0.000911458337 +%float_0_001953125 = OpConstant %float 0.001953125 +%float_n0_00140165444 = OpConstant %float -0.00140165444 +%float_n0_00063572306 = OpConstant %float -0.00063572306 +%float_0_00150888483 = OpConstant %float 0.00150888483 +%float_n0_000819546578 = OpConstant %float -0.000819546578 +%float_0_00124846818 = OpConstant %float 0.00124846818 +%float_0_000252757367 = OpConstant %float 0.000252757367 +%float_0_00152420346 = OpConstant %float 0.00152420346 +%float_0_00112591917 = OpConstant %float 0.00112591917 +%float_0_000359987753 = OpConstant %float 0.000359987753 +%float_n0_000390625006 = OpConstant %float -0.000390625006 +%float_0_00190716912 = OpConstant %float 0.00190716912 +%float_0_00138633582 = OpConstant %float 0.00138633582 +%float_n0_00111060054 = OpConstant %float -0.00111060054 +%float_0_00161611522 = OpConstant %float 0.00161611522 +%float_n0_000880821084 = OpConstant %float -0.000880821084 +%float_0_000145526967 = OpConstant %float 0.000145526967 +%float_0_00107996329 = OpConstant %float 0.00107996329 +%float_n5_36151965en05 = OpConstant %float -5.36151965e-05 +%float_0_00028339462 = OpConstant %float 0.00028339462 +%float_n0_00169270835 = OpConstant %float -0.00169270835 +%float_n0_00126378681 = OpConstant %float -0.00126378681 +%float_n0_000513174047 = OpConstant %float -0.000513174047 +%float_n0_000160845593 = OpConstant %float -0.000160845593 +%float_n0_00187653187 = OpConstant %float -0.00187653187 +%float_n0_000834865205 = OpConstant %float -0.000834865205 +%float_0_00063572306 = OpConstant %float 0.00063572306 +%float_7_65931418en06 = OpConstant %float 7.65931418e-06 +%float_n0_00190716912 = OpConstant %float -0.00190716912 +%float_n0_000222120099 = OpConstant %float -0.000222120099 +%float_0_000375306379 = OpConstant %float 0.000375306379 +%float_n0_00173866423 = OpConstant %float -0.00173866423 +%float_n0_000405943632 = OpConstant %float -0.000405943632 +%float_n0_00123314955 = OpConstant %float -0.00123314955 +%float_0_00170802698 = OpConstant %float 0.00170802698 +%float_n0_00094209559 = OpConstant %float -0.00094209559 +%float_0_000819546578 = OpConstant %float 0.000819546578 +%float_0_00184589461 = OpConstant %float 0.00184589461 +%float_0_000467218139 = OpConstant %float 0.000467218139 + %337 = OpConstantComposite %_arr_float_uint_256 %float_n0_00100337015 %float_0_000880821084 %float_0_00169270835 %float_n0_00155484071 %float_0_00127910543 %float_n0_000605085806 %float_0_00106464466 %float_n0_00138633582 %float_0_00135569857 %float_0_000513174047 %float_0_00121783093 %float_n0_00160079659 %float_0_00058976718 %float_n0_00028339462 %float_0_00111060054 %float_n0_00141697307 %float_0_00144761032 %float_n0_0005438113 %float_0_00013020834 %float_n0_0012025123 %float_0_000436580885 %float_n0_00104932603 %float_0_000620404433 %float_n0_000482536765 %float_0_00187653187 %float_n0_00109528191 %float_n9_95710798en05 %float_n0_000528492674 %float_0_0014322917 %float_n0_00193780637 %float_n0_000696997566 %float_3_829657en05 %float_0_000712316192 %float_n0_00130974269 %float_0_00109528191 %float_n0_000298713247 %float_0_00175398286 %float_n0_00167738972 %float_0_00147824758 %float_n3_829657en05 %float_0_000788909325 %float_n0_00183057599 %float_0_000298713247 %float_0_000988051528 %float_n0_00117187505 %float_0_00017616422 %float_0_00164675247 %float_n0_00158547796 %float_0_000344669126 %float_0_00186121324 %float_n0_00176930148 %float_n0_000865502458 %float_0_000896139711 %float_0_000160845593 %float_n0_000926776964 %float_n0_00152420346 %float_n0_000651041686 %float_0_00129442406 %float_n0_000804227951 %float_n0_00146292895 %float_0_00179993873 %float_n0_000850183831 %float_0_000850183831 %float_n0_000451899512 %float_n0_00106464466 %float_n0_000145526967 %float_0_000237438726 %float_0_00141697307 %float_n0_00058976718 %float_n0_000191482846 %float_0_00160079659 %float_0_00101868878 %float_0_000405943632 %float_n0_000206801473 %float_0_00158547796 %float_0_000651041686 %float_n6_89338267en05 %float_0_000421262259 %float_n0_00164675247 %float_0_00137101719 %float_0_000926776964 %float_n0_000666360313 %float_0_00118719367 %float_n0_00144761032 %float_0_000574448553 %float_n0_00189185049 %float_0_000758272072 %float_n0_00129442406 %float_0_00192248775 %float_n0_0016620711 %float_n0_00103400741 %float_n0_000497855421 %float_n0_00186121324 %float_0_0012025123 %float_n0_0003293505 %float_n0_00137101719 %float_0_00163143384 %float_n0_00184589461 %float_0_000727634819 %float_n0_000911458337 %float_0_00181525736 %float_n0_00114123779 %float_n0_000375306379 %float_9_95710798en05 %float_n0_000742953445 %float_0_00117187505 %float_6_89338267en05 %float_0_0014935662 %float_0_000972732843 %float_n0_000957414217 %float_0_00193780637 %float_0_000528492674 %float_5_36151965en05 %float_n0_00124846818 %float_n0_000268075994 %float_0_00153952208 %float_n7_65931418en06 %float_0_000314031873 %float_0_00134037994 %float_n0_00175398286 %float_0_000497855421 %float_n0_00118719367 %float_0_000773590698 %float_n0_00134037994 %float_0_000268075994 %float_n0_00147824758 %float_n0_00013020834 %float_n0_000773590698 %float_0_00130974269 %float_0_000390625006 %float_0_000957414217 %float_n0_000467218139 %float_n0_00153952208 %float_0_00103400741 %float_n0_000681678939 %float_0_00167738972 %float_0_00100337015 %float_n0_000421262259 %float_0_00178462011 %float_n0_000237438726 %float_n0_000620404433 %float_0_0016620711 %float_0_000834865205 %float_n0_0017233456 %float_n0_00107996329 %float_0_00176930148 %float_n0_000788909325 %float_n0_00178462011 %float_0_000681678939 %float_n0_000988051528 %float_n0_00132506131 %float_n0_00017616422 %float_n0_00150888483 %float_0_0003293505 %float_n0_001953125 %float_0_000666360313 %float_n0_00161611522 %float_0_00115655642 %float_0_000451899512 %float_n0_000436580885 %float_0_000191482846 %float_n0_0014935662 %float_0_00114123779 %float_8_42524532en05 %float_0_00189185049 %float_0_00140165444 %float_0_000559129927 %float_0_000114889706 %float_0_00126378681 %float_n0_000574448553 %float_n0_000972732843 %float_0_00132506131 %float_0_000222120099 %float_n0_000758272072 %float_n0_00135569857 %float_0_00146292895 %float_0_000865502458 %float_n0_000359987753 %float_0_0005438113 %float_n0_00112591917 %float_n0_000252757367 %float_n0_000559129927 %float_n0_00181525736 %float_0_0017233456 %float_n0_00115655642 %float_0_000742953445 %float_0_00157015934 %float_n0_000114889706 %float_n0_00121783093 %float_0_00183057599 %float_2_29779416en05 %float_n0_00192248775 %float_0_00173866423 %float_n0_000712316192 %float_0_00155484071 %float_n0_00170802698 %float_0_00123314955 %float_0_000206801473 %float_0_00104932603 %float_n0_000727634819 %float_n0_00163143384 %float_n0_000314031873 %float_0_000482536765 %float_n0_00179993873 %float_0_00094209559 %float_n0_000344669126 %float_0_000696997566 %float_n0_00101868878 %float_n0_00157015934 %float_n2_29779416en05 %float_n0_00127910543 %float_0_000804227951 %float_n0_000896139711 %float_n0_0014322917 %float_0_000605085806 %float_n8_42524532en05 %float_0_000911458337 %float_0_001953125 %float_n0_00140165444 %float_n0_00063572306 %float_0_00150888483 %float_n0_000819546578 %float_0_00124846818 %float_0_000252757367 %float_0_00152420346 %float_0_00112591917 %float_0_000359987753 %float_n0_000390625006 %float_0_00190716912 %float_0_00138633582 %float_n0_00111060054 %float_0_00161611522 %float_n0_000880821084 %float_0_000145526967 %float_0_00107996329 %float_n5_36151965en05 %float_0_00028339462 %float_n0_00169270835 %float_n0_00126378681 %float_n0_000513174047 %float_n0_000160845593 %float_n0_00187653187 %float_n0_000834865205 %float_0_00063572306 %float_7_65931418en06 %float_n0_00190716912 %float_n0_000222120099 %float_0_000375306379 %float_n0_00173866423 %float_n0_000405943632 %float_n0_00123314955 %float_0_00170802698 %float_n0_00094209559 %float_0_000819546578 %float_0_00184589461 %float_0_000467218139 + %uint_1 = OpConstant %uint 1 + %uint_16 = OpConstant %uint 16 + %uint_0 = OpConstant %uint 0 +%_ptr_Function__arr_float_uint_256 = OpTypePointer Function %_arr_float_uint_256 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 +%uint_532432441 = OpConstant %uint 532432441 +%uint_2129690299 = OpConstant %uint 2129690299 +%uint_2129764351 = OpConstant %uint 2129764351 + %float_2 = OpConstant %float 2 + %424 = OpTypeImage %float 2D 0 0 0 1 Unknown + %425 = OpTypeSampledImage %424 +%_ptr_UniformConstant_425 = OpTypePointer UniformConstant %425 + %xe_texture = OpVariable %_ptr_UniformConstant_425 UniformConstant + %int_0 = OpConstant %int 0 + %v4float = OpTypeVector %float 4 + %int_n1 = OpConstant %int -1 + %462 = OpConstantComposite %v2int %int_0 %int_n1 + %int_1 = OpConstant %int 1 + %469 = OpConstantComposite %v2int %int_1 %int_n1 + %475 = OpConstantComposite %v2int %int_n1 %int_0 + %485 = OpConstantComposite %v2int %int_1 %int_0 + %491 = OpConstantComposite %v2int %int_n1 %int_1 + %497 = OpConstantComposite %v2int %int_0 %int_1 + %503 = OpConstantComposite %v2int %int_1 %int_1 + %uint_2 = OpConstant %uint 2 + %v2float = OpTypeVector %float 2 + %int_2 = OpConstant %int 2 + %992 = OpConstantComposite %v2int %int_2 %int_0 + %1014 = OpConstantComposite %v2int %int_0 %int_2 + %1025 = OpConstantComposite %v2int %int_2 %int_1 + %1031 = OpConstantComposite %v2int %int_1 %int_2 +%float_0_03125 = OpConstant %float 0.03125 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%XeCasResampleConstants = OpTypeStruct %v2int %v2float %float +%_ptr_PushConstant_XeCasResampleConstants = OpTypePointer PushConstant %XeCasResampleConstants + %_ = OpVariable %_ptr_PushConstant_XeCasResampleConstants PushConstant +%_ptr_PushConstant_v2int = OpTypePointer PushConstant %v2int +%_ptr_PushConstant_v2float = OpTypePointer PushConstant %v2float + %float_0_5 = OpConstant %float 0.5 +%_ptr_PushConstant_float = OpTypePointer PushConstant %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%xe_frag_color = OpVariable %_ptr_Output_v4float Output +%_ptr_Output_float = OpTypePointer Output %float + %2561 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %2562 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %uint_3 = OpConstant %uint 3 + %6765 = OpConstantComposite %v2float %float_0_5 %float_0_5 + %6781 = OpConstantComposite %v2uint %uint_15 %uint_15 + %main = OpFunction %void None %3 + %5 = OpLabel + %6313 = OpVariable %_ptr_Function__arr_float_uint_256 Function + %2485 = OpLoad %v4float %gl_FragCoord + %2486 = OpVectorShuffle %v2float %2485 %2485 0 1 + %2487 = OpConvertFToS %v2int %2486 + %2492 = OpAccessChain %_ptr_PushConstant_v2int %_ %int_0 + %2493 = OpLoad %v2int %2492 + %2494 = OpISub %v2int %2487 %2493 + %2495 = OpBitcast %v2uint %2494 + %2498 = OpAccessChain %_ptr_PushConstant_v2float %_ %int_1 + %2499 = OpLoad %v2float %2498 + %2500 = OpBitcast %v2uint %2499 + %2504 = OpVectorTimesScalar %v2float %2499 %float_0_5 + %2506 = OpFSub %v2float %2504 %6765 + %2507 = OpBitcast %v2uint %2506 + %2515 = OpAccessChain %_ptr_PushConstant_float %_ %int_2 + %2516 = OpLoad %float %2515 + %2517 = OpBitcast %uint %2516 + OpBranch %3073 + %3073 = OpLabel + %3424 = OpConvertUToF %v2float %2495 + %3430 = OpBitcast %v2float %2500 + %3431 = OpFMul %v2float %3424 %3430 + %3437 = OpBitcast %v2float %2507 + %3438 = OpFAdd %v2float %3431 %3437 + %3440 = OpExtInst %v2float %1 Floor %3438 + %3443 = OpFSub %v2float %3438 %3440 + %3445 = OpConvertFToS %v2int %3440 + %3450 = OpIAdd %v2int %3445 %462 + %5040 = OpLoad %425 %xe_texture + %5042 = OpImage %424 %5040 + %5043 = OpImageFetch %v4float %5042 %3450 Lod %int_0 + %3453 = OpIAdd %v2int %3445 %475 + %5049 = OpImage %424 %5040 + %5050 = OpImageFetch %v4float %5049 %3453 Lod %int_0 + %5056 = OpImage %424 %5040 + %5057 = OpImageFetch %v4float %5056 %3445 Lod %int_0 + %3458 = OpIAdd %v2int %3445 %469 + %5063 = OpImage %424 %5040 + %5064 = OpImageFetch %v4float %5063 %3458 Lod %int_0 + %3464 = OpIAdd %v2int %3445 %485 + %5077 = OpImage %424 %5040 + %5078 = OpImageFetch %v4float %5077 %3464 Lod %int_0 + %3467 = OpIAdd %v2int %3445 %992 + %5084 = OpImage %424 %5040 + %5085 = OpImageFetch %v4float %5084 %3467 Lod %int_0 + %3470 = OpIAdd %v2int %3445 %491 + %5091 = OpImage %424 %5040 + %5092 = OpImageFetch %v4float %5091 %3470 Lod %int_0 + %3473 = OpIAdd %v2int %3445 %497 + %5098 = OpImage %424 %5040 + %5099 = OpImageFetch %v4float %5098 %3473 Lod %int_0 + %3479 = OpIAdd %v2int %3445 %1014 + %5112 = OpImage %424 %5040 + %5113 = OpImageFetch %v4float %5112 %3479 Lod %int_0 + %3482 = OpIAdd %v2int %3445 %503 + %5119 = OpImage %424 %5040 + %5120 = OpImageFetch %v4float %5119 %3482 Lod %int_0 + %3485 = OpIAdd %v2int %3445 %1025 + %5126 = OpImage %424 %5040 + %5127 = OpImageFetch %v4float %5126 %3485 Lod %int_0 + %3488 = OpIAdd %v2int %3445 %1031 + %5133 = OpImage %424 %5040 + %5134 = OpImageFetch %v4float %5133 %3488 Lod %int_0 + %3507 = OpCompositeExtract %float %5043 0 + %3509 = OpCompositeExtract %float %5043 1 + %3511 = OpCompositeExtract %float %5043 2 + %5156 = OpFMul %float %3507 %3507 + %5159 = OpFMul %float %3509 %3509 + %5162 = OpFMul %float %3511 %3511 + %3520 = OpCompositeExtract %float %5064 0 + %3522 = OpCompositeExtract %float %5064 1 + %3524 = OpCompositeExtract %float %5064 2 + %5166 = OpFMul %float %3520 %3520 + %5169 = OpFMul %float %3522 %3522 + %5172 = OpFMul %float %3524 %3524 + %3546 = OpCompositeExtract %float %5050 0 + %3548 = OpCompositeExtract %float %5050 1 + %3550 = OpCompositeExtract %float %5050 2 + %5186 = OpFMul %float %3546 %3546 + %5189 = OpFMul %float %3548 %3548 + %5192 = OpFMul %float %3550 %3550 + %3559 = OpCompositeExtract %float %5057 0 + %3561 = OpCompositeExtract %float %5057 1 + %3563 = OpCompositeExtract %float %5057 2 + %5196 = OpFMul %float %3559 %3559 + %5199 = OpFMul %float %3561 %3561 + %5202 = OpFMul %float %3563 %3563 + %3572 = OpCompositeExtract %float %5078 0 + %3574 = OpCompositeExtract %float %5078 1 + %3576 = OpCompositeExtract %float %5078 2 + %5206 = OpFMul %float %3572 %3572 + %5209 = OpFMul %float %3574 %3574 + %5212 = OpFMul %float %3576 %3576 + %3585 = OpCompositeExtract %float %5085 0 + %3587 = OpCompositeExtract %float %5085 1 + %3589 = OpCompositeExtract %float %5085 2 + %5216 = OpFMul %float %3585 %3585 + %5219 = OpFMul %float %3587 %3587 + %5222 = OpFMul %float %3589 %3589 + %3598 = OpCompositeExtract %float %5092 0 + %3600 = OpCompositeExtract %float %5092 1 + %3602 = OpCompositeExtract %float %5092 2 + %5226 = OpFMul %float %3598 %3598 + %5229 = OpFMul %float %3600 %3600 + %5232 = OpFMul %float %3602 %3602 + %3611 = OpCompositeExtract %float %5099 0 + %3613 = OpCompositeExtract %float %5099 1 + %3615 = OpCompositeExtract %float %5099 2 + %5236 = OpFMul %float %3611 %3611 + %5239 = OpFMul %float %3613 %3613 + %5242 = OpFMul %float %3615 %3615 + %3624 = OpCompositeExtract %float %5120 0 + %3626 = OpCompositeExtract %float %5120 1 + %3628 = OpCompositeExtract %float %5120 2 + %5246 = OpFMul %float %3624 %3624 + %5249 = OpFMul %float %3626 %3626 + %5252 = OpFMul %float %3628 %3628 + %3637 = OpCompositeExtract %float %5127 0 + %3639 = OpCompositeExtract %float %5127 1 + %3641 = OpCompositeExtract %float %5127 2 + %5256 = OpFMul %float %3637 %3637 + %5259 = OpFMul %float %3639 %3639 + %5262 = OpFMul %float %3641 %3641 + %3663 = OpCompositeExtract %float %5113 0 + %3665 = OpCompositeExtract %float %5113 1 + %3667 = OpCompositeExtract %float %5113 2 + %5276 = OpFMul %float %3663 %3663 + %5279 = OpFMul %float %3665 %3665 + %5282 = OpFMul %float %3667 %3667 + %3676 = OpCompositeExtract %float %5134 0 + %3678 = OpCompositeExtract %float %5134 1 + %3680 = OpCompositeExtract %float %5134 2 + %5286 = OpFMul %float %3676 %3676 + %5289 = OpFMul %float %3678 %3678 + %5292 = OpFMul %float %3680 %3680 + %5322 = OpExtInst %float %1 FMin %5189 %5199 + %5323 = OpExtInst %float %1 FMin %5159 %5322 + %5329 = OpExtInst %float %1 FMin %5209 %5239 + %5330 = OpExtInst %float %1 FMin %5323 %5329 + %5364 = OpExtInst %float %1 FMax %5189 %5199 + %5365 = OpExtInst %float %1 FMax %5159 %5364 + %5371 = OpExtInst %float %1 FMax %5209 %5239 + %5372 = OpExtInst %float %1 FMax %5365 %5371 + %5406 = OpExtInst %float %1 FMin %5199 %5209 + %5407 = OpExtInst %float %1 FMin %5169 %5406 + %5413 = OpExtInst %float %1 FMin %5219 %5249 + %5414 = OpExtInst %float %1 FMin %5407 %5413 + %5448 = OpExtInst %float %1 FMax %5199 %5209 + %5449 = OpExtInst %float %1 FMax %5169 %5448 + %5455 = OpExtInst %float %1 FMax %5219 %5249 + %5456 = OpExtInst %float %1 FMax %5449 %5455 + %5490 = OpExtInst %float %1 FMin %5229 %5239 + %5491 = OpExtInst %float %1 FMin %5199 %5490 + %5497 = OpExtInst %float %1 FMin %5249 %5279 + %5498 = OpExtInst %float %1 FMin %5491 %5497 + %5532 = OpExtInst %float %1 FMax %5229 %5239 + %5533 = OpExtInst %float %1 FMax %5199 %5532 + %5539 = OpExtInst %float %1 FMax %5249 %5279 + %5540 = OpExtInst %float %1 FMax %5533 %5539 + %5574 = OpExtInst %float %1 FMin %5239 %5249 + %5575 = OpExtInst %float %1 FMin %5209 %5574 + %5581 = OpExtInst %float %1 FMin %5259 %5289 + %5582 = OpExtInst %float %1 FMin %5575 %5581 + %5616 = OpExtInst %float %1 FMax %5239 %5249 + %5617 = OpExtInst %float %1 FMax %5209 %5616 + %5623 = OpExtInst %float %1 FMax %5259 %5289 + %5624 = OpExtInst %float %1 FMax %5617 %5623 + %5655 = OpBitcast %uint %5372 + %5656 = OpISub %uint %uint_2129690299 %5655 + %5657 = OpBitcast %float %5656 + %5688 = OpBitcast %uint %5456 + %5689 = OpISub %uint %uint_2129690299 %5688 + %5690 = OpBitcast %float %5689 + %5721 = OpBitcast %uint %5540 + %5722 = OpISub %uint %uint_2129690299 %5721 + %5723 = OpBitcast %float %5722 + %5754 = OpBitcast %uint %5624 + %5755 = OpISub %uint %uint_2129690299 %5754 + %5756 = OpBitcast %float %5755 + %4024 = OpFSub %float %float_1 %5372 + %4025 = OpExtInst %float %1 FMin %5330 %4024 + %4027 = OpFMul %float %4025 %5657 + %5798 = OpExtInst %float %1 FClamp %4027 %float_0 %float_1 + %4048 = OpFSub %float %float_1 %5456 + %4049 = OpExtInst %float %1 FMin %5414 %4048 + %4051 = OpFMul %float %4049 %5690 + %5849 = OpExtInst %float %1 FClamp %4051 %float_0 %float_1 + %4072 = OpFSub %float %float_1 %5540 + %4073 = OpExtInst %float %1 FMin %5498 %4072 + %4075 = OpFMul %float %4073 %5723 + %5900 = OpExtInst %float %1 FClamp %4075 %float_0 %float_1 + %4096 = OpFSub %float %float_1 %5624 + %4097 = OpExtInst %float %1 FMin %5582 %4096 + %4099 = OpFMul %float %4097 %5756 + %5951 = OpExtInst %float %1 FClamp %4099 %float_0 %float_1 + %5997 = OpBitcast %uint %5798 + %5999 = OpShiftRightLogical %uint %5997 %uint_1 + %6001 = OpIAdd %uint %5999 %uint_532432441 + %6002 = OpBitcast %float %6001 + %6048 = OpBitcast %uint %5849 + %6050 = OpShiftRightLogical %uint %6048 %uint_1 + %6052 = OpIAdd %uint %6050 %uint_532432441 + %6053 = OpBitcast %float %6052 + %6099 = OpBitcast %uint %5900 + %6101 = OpShiftRightLogical %uint %6099 %uint_1 + %6103 = OpIAdd %uint %6101 %uint_532432441 + %6104 = OpBitcast %float %6103 + %6150 = OpBitcast %uint %5951 + %6152 = OpShiftRightLogical %uint %6150 %uint_1 + %6154 = OpIAdd %uint %6152 %uint_532432441 + %6155 = OpBitcast %float %6154 + %4135 = OpBitcast %float %2517 + %4141 = OpFMul %float %6002 %4135 + %4150 = OpFMul %float %6053 %4135 + %4159 = OpFMul %float %6104 %4135 + %4168 = OpFMul %float %6155 %4135 + %4174 = OpCompositeExtract %float %3443 0 + %4175 = OpFSub %float %float_1 %4174 + %4178 = OpCompositeExtract %float %3443 1 + %4179 = OpFSub %float %float_1 %4178 + %4180 = OpFMul %float %4175 %4179 + %4187 = OpFMul %float %4174 %4179 + %4194 = OpFMul %float %4175 %4178 + %4199 = OpFMul %float %4174 %4178 + %4203 = OpFSub %float %5372 %5330 + %4204 = OpFAdd %float %float_0_03125 %4203 + %6196 = OpBitcast %uint %4204 + %6197 = OpISub %uint %uint_2129690299 %6196 + %6198 = OpBitcast %float %6197 + %4207 = OpFMul %float %4180 %6198 + %4211 = OpFSub %float %5456 %5414 + %4212 = OpFAdd %float %float_0_03125 %4211 + %6207 = OpBitcast %uint %4212 + %6208 = OpISub %uint %uint_2129690299 %6207 + %6209 = OpBitcast %float %6208 + %4215 = OpFMul %float %4187 %6209 + %4219 = OpFSub %float %5540 %5498 + %4220 = OpFAdd %float %float_0_03125 %4219 + %6218 = OpBitcast %uint %4220 + %6219 = OpISub %uint %uint_2129690299 %6218 + %6220 = OpBitcast %float %6219 + %4223 = OpFMul %float %4194 %6220 + %4227 = OpFSub %float %5624 %5582 + %4228 = OpFAdd %float %float_0_03125 %4227 + %6229 = OpBitcast %uint %4228 + %6230 = OpISub %uint %uint_2129690299 %6229 + %6231 = OpBitcast %float %6230 + %4231 = OpFMul %float %4199 %6231 + %4237 = OpFMul %float %4141 %4207 + %4246 = OpFMul %float %4150 %4215 + %4264 = OpFMul %float %4159 %4223 + %4265 = OpFAdd %float %4246 %4264 + %4267 = OpFAdd %float %4265 %4207 + %4291 = OpFMul %float %4168 %4231 + %4292 = OpFAdd %float %4237 %4291 + %4294 = OpFAdd %float %4292 %4215 + %4321 = OpFAdd %float %4292 %4223 + %4348 = OpFAdd %float %4265 %4231 + %6766 = OpFAdd %float %4237 %4246 + %6767 = OpFAdd %float %6766 %4264 + %6768 = OpFAdd %float %6767 %4291 + %4390 = OpFMul %float %float_2 %6768 + %4392 = OpFAdd %float %4390 %4267 + %4394 = OpFAdd %float %4392 %4294 + %4396 = OpFAdd %float %4394 %4321 + %4398 = OpFAdd %float %4396 %4348 + %6254 = OpBitcast %uint %4398 + %6255 = OpISub %uint %uint_2129764351 %6254 + %6256 = OpBitcast %float %6255 + %6259 = OpFNegate %float %6256 + %6261 = OpFMul %float %6259 %4398 + %6263 = OpFAdd %float %6261 %float_2 + %6264 = OpFMul %float %6256 %6263 + %6769 = OpFAdd %float %5156 %5186 + %4408 = OpFMul %float %4237 %6769 + %4412 = OpFMul %float %5166 %4246 + %4413 = OpFAdd %float %4408 %4412 + %4417 = OpFMul %float %5216 %4246 + %4418 = OpFAdd %float %4413 %4417 + %4422 = OpFMul %float %5226 %4264 + %4423 = OpFAdd %float %4418 %4422 + %4427 = OpFMul %float %5276 %4264 + %4428 = OpFAdd %float %4423 %4427 + %4432 = OpFMul %float %5256 %4291 + %4433 = OpFAdd %float %4428 %4432 + %4437 = OpFMul %float %5286 %4291 + %4438 = OpFAdd %float %4433 %4437 + %4442 = OpFMul %float %5196 %4267 + %4443 = OpFAdd %float %4438 %4442 + %4447 = OpFMul %float %5206 %4294 + %4448 = OpFAdd %float %4443 %4447 + %4452 = OpFMul %float %5236 %4321 + %4453 = OpFAdd %float %4448 %4452 + %4457 = OpFMul %float %5246 %4348 + %4458 = OpFAdd %float %4453 %4457 + %4460 = OpFMul %float %4458 %6264 + %6278 = OpExtInst %float %1 FClamp %4460 %float_0 %float_1 + %6770 = OpFAdd %float %5159 %5189 + %4470 = OpFMul %float %4237 %6770 + %4474 = OpFMul %float %5169 %4246 + %4475 = OpFAdd %float %4470 %4474 + %4479 = OpFMul %float %5219 %4246 + %4480 = OpFAdd %float %4475 %4479 + %4484 = OpFMul %float %5229 %4264 + %4485 = OpFAdd %float %4480 %4484 + %4489 = OpFMul %float %5279 %4264 + %4490 = OpFAdd %float %4485 %4489 + %4494 = OpFMul %float %5259 %4291 + %4495 = OpFAdd %float %4490 %4494 + %4499 = OpFMul %float %5289 %4291 + %4500 = OpFAdd %float %4495 %4499 + %4504 = OpFMul %float %5199 %4267 + %4505 = OpFAdd %float %4500 %4504 + %4509 = OpFMul %float %5209 %4294 + %4510 = OpFAdd %float %4505 %4509 + %4514 = OpFMul %float %5239 %4321 + %4515 = OpFAdd %float %4510 %4514 + %4519 = OpFMul %float %5249 %4348 + %4520 = OpFAdd %float %4515 %4519 + %4522 = OpFMul %float %4520 %6264 + %6292 = OpExtInst %float %1 FClamp %4522 %float_0 %float_1 + %6771 = OpFAdd %float %5162 %5192 + %4532 = OpFMul %float %4237 %6771 + %4536 = OpFMul %float %5172 %4246 + %4537 = OpFAdd %float %4532 %4536 + %4541 = OpFMul %float %5222 %4246 + %4542 = OpFAdd %float %4537 %4541 + %4546 = OpFMul %float %5232 %4264 + %4547 = OpFAdd %float %4542 %4546 + %4551 = OpFMul %float %5282 %4264 + %4552 = OpFAdd %float %4547 %4551 + %4556 = OpFMul %float %5262 %4291 + %4557 = OpFAdd %float %4552 %4556 + %4561 = OpFMul %float %5292 %4291 + %4562 = OpFAdd %float %4557 %4561 + %4566 = OpFMul %float %5202 %4267 + %4567 = OpFAdd %float %4562 %4566 + %4571 = OpFMul %float %5212 %4294 + %4572 = OpFAdd %float %4567 %4571 + %4576 = OpFMul %float %5242 %4321 + %4577 = OpFAdd %float %4572 %4576 + %4581 = OpFMul %float %5252 %4348 + %4582 = OpFAdd %float %4577 %4581 + %4584 = OpFMul %float %4582 %6264 + %6306 = OpExtInst %float %1 FClamp %4584 %float_0 %float_1 + %2544 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_0 + OpStore %2544 %6278 + %2546 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_1 + OpStore %2546 %6292 + %2548 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_2 + OpStore %2548 %6306 + %2549 = OpLoad %v4float %xe_frag_color + %2550 = OpVectorShuffle %v3float %2549 %2549 0 1 2 + %2551 = OpExtInst %v3float %1 Sqrt %2550 + %2552 = OpLoad %v4float %xe_frag_color + %2553 = OpVectorShuffle %v4float %2552 %2551 4 5 6 3 + OpStore %xe_frag_color %2553 + %2554 = OpLoad %v4float %xe_frag_color + %2555 = OpVectorShuffle %v3float %2554 %2554 0 1 2 + %6318 = OpBitwiseAnd %v2uint %2495 %6781 + %6320 = OpCompositeExtract %uint %6318 1 + %6321 = OpIMul %uint %6320 %uint_16 + %6323 = OpCompositeExtract %uint %6318 0 + %6324 = OpIAdd %uint %6321 %6323 + OpStore %6313 %337 + %6325 = OpAccessChain %_ptr_Function_float %6313 %6324 + %6326 = OpLoad %float %6325 + %2559 = OpCompositeConstruct %v3float %6326 %6326 %6326 + %2560 = OpFAdd %v3float %2555 %2559 + %2563 = OpExtInst %v3float %1 FClamp %2560 %2561 %2562 + %2564 = OpLoad %v4float %xe_frag_color + %2565 = OpVectorShuffle %v4float %2564 %2563 4 5 6 3 + OpStore %xe_frag_color %2565 + %2567 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_3 + OpStore %2567 %float_1 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.h new file mode 100644 index 000000000..39b69a5f1 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.h @@ -0,0 +1,745 @@ +// generated from `xb genspirv` +// source: guest_output_ffx_cas_resample.frag +const uint8_t guest_output_ffx_cas_resample_frag[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x40, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x9C, 0x08, 0x00, 0x00, 0xC9, 0x08, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0A, 0x00, + 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, + 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, + 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, + 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x92, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x9C, 0x08, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, + 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, + 0xA0, 0x08, 0x00, 0x00, 0x58, 0x65, 0x43, 0x61, 0x73, 0x52, 0x65, 0x73, + 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, + 0x74, 0x73, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, 0xA0, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x6F, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0B, 0x00, 0xA0, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x69, + 0x6E, 0x70, 0x75, 0x74, 0x5F, 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, + 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, 0x00, + 0x06, 0x00, 0x0A, 0x00, 0xA0, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x73, 0x68, 0x61, 0x72, 0x70, + 0x6E, 0x65, 0x73, 0x73, 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x73, 0x65, + 0x74, 0x75, 0x70, 0x00, 0x05, 0x00, 0x03, 0x00, 0xA2, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0xC9, 0x08, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x9C, 0x08, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xA0, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0xA0, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0xA0, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0xA0, 0x08, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xC9, 0x08, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x2B, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, + 0x39, 0x46, 0xBC, 0x1F, 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0xBB, 0x7E, 0xF0, 0x7E, 0x2B, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xFF, 0x9F, 0xF1, 0x7E, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x19, 0x00, 0x09, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x91, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xCC, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, + 0xAE, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xEA, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x86, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xC8, 0x02, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0xE9, 0x02, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xEF, 0x02, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3D, 0x20, 0x00, 0x04, 0x00, 0x9B, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x9B, 0x08, 0x00, 0x00, 0x9C, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x05, 0x00, 0xA0, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x86, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xA1, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA0, 0x08, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0xA1, 0x08, 0x00, 0x00, 0xA2, 0x08, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xA3, 0x08, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xA9, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x86, 0x02, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAD, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3F, 0x20, 0x00, 0x04, 0x00, 0xBA, 0x08, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xC8, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0xC8, 0x08, 0x00, 0x00, 0xC9, 0x08, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xD7, 0x08, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xE2, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, 0x39, 0x19, 0x00, 0x00, + 0xAD, 0x08, 0x00, 0x00, 0xAD, 0x08, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, 0x9D, 0x08, 0x00, 0x00, + 0x9C, 0x08, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x86, 0x02, 0x00, 0x00, + 0x9E, 0x08, 0x00, 0x00, 0x9D, 0x08, 0x00, 0x00, 0x9D, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x9F, 0x08, 0x00, 0x00, 0x9E, 0x08, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0xA3, 0x08, 0x00, 0x00, 0xA4, 0x08, 0x00, 0x00, + 0xA2, 0x08, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0xA5, 0x08, 0x00, 0x00, 0xA4, 0x08, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xA6, 0x08, 0x00, 0x00, + 0x9F, 0x08, 0x00, 0x00, 0xA5, 0x08, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x37, 0x00, 0x00, 0x00, 0xA7, 0x08, 0x00, 0x00, 0xA6, 0x08, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0xA9, 0x08, 0x00, 0x00, 0xAA, 0x08, 0x00, 0x00, + 0xA2, 0x08, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x86, 0x02, 0x00, 0x00, 0xAB, 0x08, 0x00, 0x00, 0xAA, 0x08, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0xAC, 0x08, 0x00, 0x00, + 0xAB, 0x08, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, + 0xB0, 0x08, 0x00, 0x00, 0xAB, 0x08, 0x00, 0x00, 0xAD, 0x08, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, 0xB2, 0x08, 0x00, 0x00, + 0xB0, 0x08, 0x00, 0x00, 0x39, 0x19, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x37, 0x00, 0x00, 0x00, 0xB3, 0x08, 0x00, 0x00, 0xB2, 0x08, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0xBA, 0x08, 0x00, 0x00, 0xBB, 0x08, 0x00, 0x00, + 0xA2, 0x08, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xBC, 0x08, 0x00, 0x00, 0xBB, 0x08, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xBD, 0x08, 0x00, 0x00, + 0xBC, 0x08, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xDD, 0x0A, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xDD, 0x0A, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, + 0x86, 0x02, 0x00, 0x00, 0x3C, 0x0C, 0x00, 0x00, 0xA7, 0x08, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x86, 0x02, 0x00, 0x00, 0x42, 0x0C, 0x00, 0x00, + 0xAC, 0x08, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, + 0x43, 0x0C, 0x00, 0x00, 0x3C, 0x0C, 0x00, 0x00, 0x42, 0x0C, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x86, 0x02, 0x00, 0x00, 0x49, 0x0C, 0x00, 0x00, + 0xB3, 0x08, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, + 0x4A, 0x0C, 0x00, 0x00, 0x43, 0x0C, 0x00, 0x00, 0x49, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x86, 0x02, 0x00, 0x00, 0x4C, 0x0C, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4A, 0x0C, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, 0x4F, 0x0C, 0x00, 0x00, + 0x4A, 0x0C, 0x00, 0x00, 0x4C, 0x0C, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, 0x4C, 0x0C, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x56, 0x0C, 0x00, 0x00, + 0x51, 0x0C, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x8E, 0x12, 0x00, 0x00, + 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x8F, 0x12, 0x00, 0x00, 0x8E, 0x12, 0x00, 0x00, 0x56, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x59, 0x0C, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, + 0xC2, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0x95, 0x12, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x96, 0x12, 0x00, 0x00, 0x95, 0x12, 0x00, 0x00, + 0x59, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x9C, 0x12, 0x00, 0x00, + 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x9D, 0x12, 0x00, 0x00, 0x9C, 0x12, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x5E, 0x0C, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, + 0xBC, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0xA3, 0x12, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xA4, 0x12, 0x00, 0x00, 0xA3, 0x12, 0x00, 0x00, + 0x5E, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x64, 0x0C, 0x00, 0x00, + 0x51, 0x0C, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0xB1, 0x12, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, 0xB2, 0x12, 0x00, 0x00, + 0xB1, 0x12, 0x00, 0x00, 0x64, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x67, 0x0C, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, 0xC8, 0x02, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0xB8, 0x12, 0x00, 0x00, + 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xB9, 0x12, 0x00, 0x00, 0xB8, 0x12, 0x00, 0x00, 0x67, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x6A, 0x0C, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, + 0xD2, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0xBF, 0x12, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xC0, 0x12, 0x00, 0x00, 0xBF, 0x12, 0x00, 0x00, + 0x6A, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x6D, 0x0C, 0x00, 0x00, + 0x51, 0x0C, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0xC6, 0x12, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, 0xC7, 0x12, 0x00, 0x00, + 0xC6, 0x12, 0x00, 0x00, 0x6D, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x73, 0x0C, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0xD4, 0x12, 0x00, 0x00, + 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xD5, 0x12, 0x00, 0x00, 0xD4, 0x12, 0x00, 0x00, 0x73, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x76, 0x0C, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0xDB, 0x12, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xDC, 0x12, 0x00, 0x00, 0xDB, 0x12, 0x00, 0x00, + 0x76, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x79, 0x0C, 0x00, 0x00, + 0x51, 0x0C, 0x00, 0x00, 0xE9, 0x02, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0xE2, 0x12, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, 0xE3, 0x12, 0x00, 0x00, + 0xE2, 0x12, 0x00, 0x00, 0x79, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x7C, 0x0C, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, 0xEF, 0x02, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0xE9, 0x12, 0x00, 0x00, + 0x8C, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xEA, 0x12, 0x00, 0x00, 0xE9, 0x12, 0x00, 0x00, 0x7C, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8F, 0x0C, 0x00, 0x00, 0x8F, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x91, 0x0C, 0x00, 0x00, 0x8F, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x93, 0x0C, 0x00, 0x00, + 0x8F, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x8F, 0x0C, 0x00, 0x00, + 0x8F, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x13, 0x00, 0x00, 0x91, 0x0C, 0x00, 0x00, 0x91, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, + 0x93, 0x0C, 0x00, 0x00, 0x93, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9C, 0x0C, 0x00, 0x00, 0xA4, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9E, 0x0C, 0x00, 0x00, 0xA4, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA0, 0x0C, 0x00, 0x00, + 0xA4, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0A, 0x13, 0x00, 0x00, 0x9C, 0x0C, 0x00, 0x00, + 0x9C, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0D, 0x13, 0x00, 0x00, 0x9E, 0x0C, 0x00, 0x00, 0x9E, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x13, 0x00, 0x00, + 0xA0, 0x0C, 0x00, 0x00, 0xA0, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB6, 0x0C, 0x00, 0x00, 0x96, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB8, 0x0C, 0x00, 0x00, 0x96, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBA, 0x0C, 0x00, 0x00, + 0x96, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1E, 0x13, 0x00, 0x00, 0xB6, 0x0C, 0x00, 0x00, + 0xB6, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x13, 0x00, 0x00, 0xB8, 0x0C, 0x00, 0x00, 0xB8, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x13, 0x00, 0x00, + 0xBA, 0x0C, 0x00, 0x00, 0xBA, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC3, 0x0C, 0x00, 0x00, 0x9D, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC5, 0x0C, 0x00, 0x00, 0x9D, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC7, 0x0C, 0x00, 0x00, + 0x9D, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x28, 0x13, 0x00, 0x00, 0xC3, 0x0C, 0x00, 0x00, + 0xC3, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2B, 0x13, 0x00, 0x00, 0xC5, 0x0C, 0x00, 0x00, 0xC5, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x13, 0x00, 0x00, + 0xC7, 0x0C, 0x00, 0x00, 0xC7, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD0, 0x0C, 0x00, 0x00, 0xB2, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xD2, 0x0C, 0x00, 0x00, 0xB2, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD4, 0x0C, 0x00, 0x00, + 0xB2, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x13, 0x00, 0x00, 0xD0, 0x0C, 0x00, 0x00, + 0xD0, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x35, 0x13, 0x00, 0x00, 0xD2, 0x0C, 0x00, 0x00, 0xD2, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x13, 0x00, 0x00, + 0xD4, 0x0C, 0x00, 0x00, 0xD4, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xDD, 0x0C, 0x00, 0x00, 0xB9, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDF, 0x0C, 0x00, 0x00, 0xB9, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xE1, 0x0C, 0x00, 0x00, + 0xB9, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3C, 0x13, 0x00, 0x00, 0xDD, 0x0C, 0x00, 0x00, + 0xDD, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3F, 0x13, 0x00, 0x00, 0xDF, 0x0C, 0x00, 0x00, 0xDF, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x42, 0x13, 0x00, 0x00, + 0xE1, 0x0C, 0x00, 0x00, 0xE1, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xEA, 0x0C, 0x00, 0x00, 0xC0, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xEC, 0x0C, 0x00, 0x00, 0xC0, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xEE, 0x0C, 0x00, 0x00, + 0xC0, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x46, 0x13, 0x00, 0x00, 0xEA, 0x0C, 0x00, 0x00, + 0xEA, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x49, 0x13, 0x00, 0x00, 0xEC, 0x0C, 0x00, 0x00, 0xEC, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4C, 0x13, 0x00, 0x00, + 0xEE, 0x0C, 0x00, 0x00, 0xEE, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0xC7, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF9, 0x0C, 0x00, 0x00, 0xC7, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFB, 0x0C, 0x00, 0x00, + 0xC7, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x50, 0x13, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, + 0xF7, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x53, 0x13, 0x00, 0x00, 0xF9, 0x0C, 0x00, 0x00, 0xF9, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x56, 0x13, 0x00, 0x00, + 0xFB, 0x0C, 0x00, 0x00, 0xFB, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x0D, 0x00, 0x00, 0xDC, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x0D, 0x00, 0x00, 0xDC, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x0D, 0x00, 0x00, + 0xDC, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5A, 0x13, 0x00, 0x00, 0x04, 0x0D, 0x00, 0x00, + 0x04, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5D, 0x13, 0x00, 0x00, 0x06, 0x0D, 0x00, 0x00, 0x06, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x13, 0x00, 0x00, + 0x08, 0x0D, 0x00, 0x00, 0x08, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x11, 0x0D, 0x00, 0x00, 0xE3, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x13, 0x0D, 0x00, 0x00, 0xE3, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x0D, 0x00, 0x00, + 0xE3, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x64, 0x13, 0x00, 0x00, 0x11, 0x0D, 0x00, 0x00, + 0x11, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x67, 0x13, 0x00, 0x00, 0x13, 0x0D, 0x00, 0x00, 0x13, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6A, 0x13, 0x00, 0x00, + 0x15, 0x0D, 0x00, 0x00, 0x15, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x0D, 0x00, 0x00, 0xD5, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2D, 0x0D, 0x00, 0x00, 0xD5, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x0D, 0x00, 0x00, + 0xD5, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x78, 0x13, 0x00, 0x00, 0x2B, 0x0D, 0x00, 0x00, + 0x2B, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7B, 0x13, 0x00, 0x00, 0x2D, 0x0D, 0x00, 0x00, 0x2D, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7E, 0x13, 0x00, 0x00, + 0x2F, 0x0D, 0x00, 0x00, 0x2F, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x38, 0x0D, 0x00, 0x00, 0xEA, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3A, 0x0D, 0x00, 0x00, 0xEA, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x0D, 0x00, 0x00, + 0xEA, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x82, 0x13, 0x00, 0x00, 0x38, 0x0D, 0x00, 0x00, + 0x38, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x85, 0x13, 0x00, 0x00, 0x3A, 0x0D, 0x00, 0x00, 0x3A, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, + 0x3C, 0x0D, 0x00, 0x00, 0x3C, 0x0D, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA6, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x21, 0x13, 0x00, 0x00, 0x2B, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA7, 0x13, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x13, 0x00, 0x00, + 0xA6, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xAD, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x35, 0x13, 0x00, 0x00, 0x53, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xAE, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0xA7, 0x13, 0x00, 0x00, 0xAD, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD0, 0x13, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x21, 0x13, 0x00, 0x00, + 0x2B, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xD1, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x03, 0x13, 0x00, 0x00, 0xD0, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD7, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x35, 0x13, 0x00, 0x00, 0x53, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD8, 0x13, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xD1, 0x13, 0x00, 0x00, + 0xD7, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xFA, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x2B, 0x13, 0x00, 0x00, 0x35, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xFB, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x0D, 0x13, 0x00, 0x00, 0xFA, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3F, 0x13, 0x00, 0x00, + 0x5D, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xFB, 0x13, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x24, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x2B, 0x13, 0x00, 0x00, 0x35, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0D, 0x13, 0x00, 0x00, + 0x24, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2B, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x3F, 0x13, 0x00, 0x00, 0x5D, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2C, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x25, 0x14, 0x00, 0x00, 0x2B, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4E, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x49, 0x13, 0x00, 0x00, + 0x53, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4F, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x2B, 0x13, 0x00, 0x00, 0x4E, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x55, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x5D, 0x13, 0x00, 0x00, 0x7B, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x56, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x4F, 0x14, 0x00, 0x00, + 0x55, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x78, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x49, 0x13, 0x00, 0x00, 0x53, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x79, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x2B, 0x13, 0x00, 0x00, 0x78, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7F, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x5D, 0x13, 0x00, 0x00, + 0x7B, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x80, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x79, 0x14, 0x00, 0x00, 0x7F, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA2, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x53, 0x13, 0x00, 0x00, 0x5D, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA3, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x35, 0x13, 0x00, 0x00, + 0xA2, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA9, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x67, 0x13, 0x00, 0x00, 0x85, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xAA, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0xA3, 0x14, 0x00, 0x00, 0xA9, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCC, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x53, 0x13, 0x00, 0x00, + 0x5D, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCD, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x35, 0x13, 0x00, 0x00, 0xCC, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD3, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x67, 0x13, 0x00, 0x00, 0x85, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD4, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xCD, 0x14, 0x00, 0x00, + 0xD3, 0x14, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xF3, 0x14, 0x00, 0x00, 0xD8, 0x13, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xF4, 0x14, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0xF3, 0x14, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF5, 0x14, 0x00, 0x00, 0xF4, 0x14, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x00, 0x2C, 0x14, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x15, 0x15, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x16, 0x15, 0x00, 0x00, 0x15, 0x15, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x35, 0x15, 0x00, 0x00, + 0x80, 0x14, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x36, 0x15, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x35, 0x15, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x37, 0x15, 0x00, 0x00, + 0x36, 0x15, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x56, 0x15, 0x00, 0x00, 0xD4, 0x14, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x57, 0x15, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x56, 0x15, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x58, 0x15, 0x00, 0x00, 0x57, 0x15, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x94, 0x0E, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0xD8, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x95, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xAE, 0x13, 0x00, 0x00, 0x94, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x97, 0x0E, 0x00, 0x00, 0x95, 0x0E, 0x00, 0x00, + 0xF5, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x82, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x97, 0x0E, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAC, 0x0E, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x2C, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xAD, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0xAC, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAF, 0x0E, 0x00, 0x00, + 0xAD, 0x0E, 0x00, 0x00, 0x16, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB5, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0xAF, 0x0E, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC4, 0x0E, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x80, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC5, 0x0E, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x56, 0x14, 0x00, 0x00, + 0xC4, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC7, 0x0E, 0x00, 0x00, 0xC5, 0x0E, 0x00, 0x00, 0x37, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0xE8, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xC7, 0x0E, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xDC, 0x0E, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0xD4, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDD, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xAA, 0x14, 0x00, 0x00, 0xDC, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xDF, 0x0E, 0x00, 0x00, 0xDD, 0x0E, 0x00, 0x00, + 0x58, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1B, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xDF, 0x0E, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x49, 0x16, 0x00, 0x00, + 0x82, 0x15, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x4B, 0x16, 0x00, 0x00, 0x49, 0x16, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x4D, 0x16, 0x00, 0x00, + 0x4B, 0x16, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4E, 0x16, 0x00, 0x00, 0x4D, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x7C, 0x16, 0x00, 0x00, + 0xB5, 0x15, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x7E, 0x16, 0x00, 0x00, 0x7C, 0x16, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x16, 0x00, 0x00, + 0x7E, 0x16, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x81, 0x16, 0x00, 0x00, 0x80, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xAF, 0x16, 0x00, 0x00, + 0xE8, 0x15, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xB1, 0x16, 0x00, 0x00, 0xAF, 0x16, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xB3, 0x16, 0x00, 0x00, + 0xB1, 0x16, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB4, 0x16, 0x00, 0x00, 0xB3, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE2, 0x16, 0x00, 0x00, + 0x1B, 0x16, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xE4, 0x16, 0x00, 0x00, 0xE2, 0x16, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE6, 0x16, 0x00, 0x00, + 0xE4, 0x16, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xE7, 0x16, 0x00, 0x00, 0xE6, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x00, 0x00, + 0xBD, 0x08, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x09, 0x0F, 0x00, 0x00, 0x4E, 0x16, 0x00, 0x00, 0x03, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x0F, 0x00, 0x00, + 0x81, 0x16, 0x00, 0x00, 0x03, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1B, 0x0F, 0x00, 0x00, 0xB4, 0x16, 0x00, 0x00, + 0x03, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x24, 0x0F, 0x00, 0x00, 0xE7, 0x16, 0x00, 0x00, 0x03, 0x0F, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x0F, 0x00, 0x00, + 0x4F, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x0F, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x2A, 0x0F, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2E, 0x0F, 0x00, 0x00, 0x4F, 0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x0F, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x2E, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x30, 0x0F, 0x00, 0x00, 0x2B, 0x0F, 0x00, 0x00, + 0x2F, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x37, 0x0F, 0x00, 0x00, 0x2A, 0x0F, 0x00, 0x00, 0x2F, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3E, 0x0F, 0x00, 0x00, + 0x2B, 0x0F, 0x00, 0x00, 0x2E, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x43, 0x0F, 0x00, 0x00, 0x2A, 0x0F, 0x00, 0x00, + 0x2E, 0x0F, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x47, 0x0F, 0x00, 0x00, 0xD8, 0x13, 0x00, 0x00, 0xAE, 0x13, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x0F, 0x00, 0x00, + 0xF2, 0x06, 0x00, 0x00, 0x47, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x10, 0x17, 0x00, 0x00, 0x48, 0x0F, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x11, 0x17, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x10, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x12, 0x17, 0x00, 0x00, 0x11, 0x17, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x0F, 0x00, 0x00, + 0x30, 0x0F, 0x00, 0x00, 0x12, 0x17, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x00, 0x00, 0x2C, 0x14, 0x00, 0x00, + 0x02, 0x14, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x50, 0x0F, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, 0x4F, 0x0F, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1B, 0x17, 0x00, 0x00, + 0x50, 0x0F, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x1C, 0x17, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x1B, 0x17, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x17, 0x00, 0x00, + 0x1C, 0x17, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x53, 0x0F, 0x00, 0x00, 0x37, 0x0F, 0x00, 0x00, 0x1D, 0x17, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x57, 0x0F, 0x00, 0x00, + 0x80, 0x14, 0x00, 0x00, 0x56, 0x14, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x58, 0x0F, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, + 0x57, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x26, 0x17, 0x00, 0x00, 0x58, 0x0F, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x27, 0x17, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x26, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x28, 0x17, 0x00, 0x00, 0x27, 0x17, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5B, 0x0F, 0x00, 0x00, 0x3E, 0x0F, 0x00, 0x00, + 0x28, 0x17, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5F, 0x0F, 0x00, 0x00, 0xD4, 0x14, 0x00, 0x00, 0xAA, 0x14, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x0F, 0x00, 0x00, + 0xF2, 0x06, 0x00, 0x00, 0x5F, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x31, 0x17, 0x00, 0x00, 0x60, 0x0F, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x32, 0x17, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x31, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x33, 0x17, 0x00, 0x00, 0x32, 0x17, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x63, 0x0F, 0x00, 0x00, + 0x43, 0x0F, 0x00, 0x00, 0x33, 0x17, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x69, 0x0F, 0x00, 0x00, 0x09, 0x0F, 0x00, 0x00, + 0x4B, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x0F, 0x00, 0x00, 0x12, 0x0F, 0x00, 0x00, 0x53, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x0F, 0x00, 0x00, + 0x1B, 0x0F, 0x00, 0x00, 0x5B, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x85, 0x0F, 0x00, 0x00, 0x72, 0x0F, 0x00, 0x00, + 0x84, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x87, 0x0F, 0x00, 0x00, 0x85, 0x0F, 0x00, 0x00, 0x4B, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9F, 0x0F, 0x00, 0x00, + 0x24, 0x0F, 0x00, 0x00, 0x63, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA0, 0x0F, 0x00, 0x00, 0x69, 0x0F, 0x00, 0x00, + 0x9F, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA2, 0x0F, 0x00, 0x00, 0xA0, 0x0F, 0x00, 0x00, 0x53, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBD, 0x0F, 0x00, 0x00, + 0xA0, 0x0F, 0x00, 0x00, 0x5B, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD8, 0x0F, 0x00, 0x00, 0x85, 0x0F, 0x00, 0x00, + 0x63, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3A, 0x19, 0x00, 0x00, 0x69, 0x0F, 0x00, 0x00, 0x72, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x19, 0x00, 0x00, + 0x3A, 0x19, 0x00, 0x00, 0x84, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3C, 0x19, 0x00, 0x00, 0x3B, 0x19, 0x00, 0x00, + 0x9F, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x10, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3C, 0x19, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, + 0x02, 0x10, 0x00, 0x00, 0x87, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, + 0xA2, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x08, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0xBD, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0A, 0x10, 0x00, 0x00, + 0x08, 0x10, 0x00, 0x00, 0xD8, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x4A, 0x17, 0x00, 0x00, 0x0A, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x4B, 0x17, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x4A, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4C, 0x17, 0x00, 0x00, 0x4B, 0x17, 0x00, 0x00, + 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4F, 0x17, 0x00, 0x00, + 0x4C, 0x17, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x51, 0x17, 0x00, 0x00, 0x4F, 0x17, 0x00, 0x00, 0x0A, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x53, 0x17, 0x00, 0x00, + 0x51, 0x17, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x54, 0x17, 0x00, 0x00, 0x4C, 0x17, 0x00, 0x00, + 0x53, 0x17, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3D, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x1E, 0x13, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x10, 0x00, 0x00, + 0x69, 0x0F, 0x00, 0x00, 0x3D, 0x19, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x18, 0x10, 0x00, 0x00, 0x0A, 0x13, 0x00, 0x00, + 0x72, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x19, 0x10, 0x00, 0x00, 0x14, 0x10, 0x00, 0x00, 0x18, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x10, 0x00, 0x00, + 0x3C, 0x13, 0x00, 0x00, 0x72, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1E, 0x10, 0x00, 0x00, 0x19, 0x10, 0x00, 0x00, + 0x1D, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x22, 0x10, 0x00, 0x00, 0x46, 0x13, 0x00, 0x00, 0x84, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x10, 0x00, 0x00, + 0x1E, 0x10, 0x00, 0x00, 0x22, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x78, 0x13, 0x00, 0x00, + 0x84, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x28, 0x10, 0x00, 0x00, 0x23, 0x10, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x10, 0x00, 0x00, + 0x64, 0x13, 0x00, 0x00, 0x9F, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2D, 0x10, 0x00, 0x00, 0x28, 0x10, 0x00, 0x00, + 0x2C, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x31, 0x10, 0x00, 0x00, 0x82, 0x13, 0x00, 0x00, 0x9F, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x10, 0x00, 0x00, + 0x2D, 0x10, 0x00, 0x00, 0x31, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x28, 0x13, 0x00, 0x00, + 0x87, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x37, 0x10, 0x00, 0x00, 0x32, 0x10, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x10, 0x00, 0x00, + 0x32, 0x13, 0x00, 0x00, 0xA2, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3C, 0x10, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, + 0x3B, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x40, 0x10, 0x00, 0x00, 0x50, 0x13, 0x00, 0x00, 0xBD, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x10, 0x00, 0x00, + 0x3C, 0x10, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x45, 0x10, 0x00, 0x00, 0x5A, 0x13, 0x00, 0x00, + 0xD8, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x46, 0x10, 0x00, 0x00, 0x41, 0x10, 0x00, 0x00, 0x45, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x10, 0x00, 0x00, + 0x46, 0x10, 0x00, 0x00, 0x54, 0x17, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x62, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x48, 0x10, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3E, 0x19, 0x00, 0x00, 0x03, 0x13, 0x00, 0x00, 0x21, 0x13, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x52, 0x10, 0x00, 0x00, + 0x69, 0x0F, 0x00, 0x00, 0x3E, 0x19, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x56, 0x10, 0x00, 0x00, 0x0D, 0x13, 0x00, 0x00, + 0x72, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x57, 0x10, 0x00, 0x00, 0x52, 0x10, 0x00, 0x00, 0x56, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5B, 0x10, 0x00, 0x00, + 0x3F, 0x13, 0x00, 0x00, 0x72, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5C, 0x10, 0x00, 0x00, 0x57, 0x10, 0x00, 0x00, + 0x5B, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x60, 0x10, 0x00, 0x00, 0x49, 0x13, 0x00, 0x00, 0x84, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x61, 0x10, 0x00, 0x00, + 0x5C, 0x10, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x65, 0x10, 0x00, 0x00, 0x7B, 0x13, 0x00, 0x00, + 0x84, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x66, 0x10, 0x00, 0x00, 0x61, 0x10, 0x00, 0x00, 0x65, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6A, 0x10, 0x00, 0x00, + 0x67, 0x13, 0x00, 0x00, 0x9F, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6B, 0x10, 0x00, 0x00, 0x66, 0x10, 0x00, 0x00, + 0x6A, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6F, 0x10, 0x00, 0x00, 0x85, 0x13, 0x00, 0x00, 0x9F, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x70, 0x10, 0x00, 0x00, + 0x6B, 0x10, 0x00, 0x00, 0x6F, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x74, 0x10, 0x00, 0x00, 0x2B, 0x13, 0x00, 0x00, + 0x87, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x75, 0x10, 0x00, 0x00, 0x70, 0x10, 0x00, 0x00, 0x74, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x79, 0x10, 0x00, 0x00, + 0x35, 0x13, 0x00, 0x00, 0xA2, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7A, 0x10, 0x00, 0x00, 0x75, 0x10, 0x00, 0x00, + 0x79, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7E, 0x10, 0x00, 0x00, 0x53, 0x13, 0x00, 0x00, 0xBD, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7F, 0x10, 0x00, 0x00, + 0x7A, 0x10, 0x00, 0x00, 0x7E, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, 0x5D, 0x13, 0x00, 0x00, + 0xD8, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x84, 0x10, 0x00, 0x00, 0x7F, 0x10, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x86, 0x10, 0x00, 0x00, + 0x84, 0x10, 0x00, 0x00, 0x54, 0x17, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x70, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x86, 0x10, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3F, 0x19, 0x00, 0x00, 0x06, 0x13, 0x00, 0x00, 0x24, 0x13, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x10, 0x00, 0x00, + 0x69, 0x0F, 0x00, 0x00, 0x3F, 0x19, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x94, 0x10, 0x00, 0x00, 0x10, 0x13, 0x00, 0x00, + 0x72, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x95, 0x10, 0x00, 0x00, 0x90, 0x10, 0x00, 0x00, 0x94, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x99, 0x10, 0x00, 0x00, + 0x42, 0x13, 0x00, 0x00, 0x72, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9A, 0x10, 0x00, 0x00, 0x95, 0x10, 0x00, 0x00, + 0x99, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9E, 0x10, 0x00, 0x00, 0x4C, 0x13, 0x00, 0x00, 0x84, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9F, 0x10, 0x00, 0x00, + 0x9A, 0x10, 0x00, 0x00, 0x9E, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA3, 0x10, 0x00, 0x00, 0x7E, 0x13, 0x00, 0x00, + 0x84, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA4, 0x10, 0x00, 0x00, 0x9F, 0x10, 0x00, 0x00, 0xA3, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, + 0x6A, 0x13, 0x00, 0x00, 0x9F, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA9, 0x10, 0x00, 0x00, 0xA4, 0x10, 0x00, 0x00, + 0xA8, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xAD, 0x10, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0x9F, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAE, 0x10, 0x00, 0x00, + 0xA9, 0x10, 0x00, 0x00, 0xAD, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB2, 0x10, 0x00, 0x00, 0x2E, 0x13, 0x00, 0x00, + 0x87, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB3, 0x10, 0x00, 0x00, 0xAE, 0x10, 0x00, 0x00, 0xB2, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB7, 0x10, 0x00, 0x00, + 0x38, 0x13, 0x00, 0x00, 0xA2, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB8, 0x10, 0x00, 0x00, 0xB3, 0x10, 0x00, 0x00, + 0xB7, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xBC, 0x10, 0x00, 0x00, 0x56, 0x13, 0x00, 0x00, 0xBD, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBD, 0x10, 0x00, 0x00, + 0xB8, 0x10, 0x00, 0x00, 0xBC, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC1, 0x10, 0x00, 0x00, 0x60, 0x13, 0x00, 0x00, + 0xD8, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC2, 0x10, 0x00, 0x00, 0xBD, 0x10, 0x00, 0x00, 0xC1, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC4, 0x10, 0x00, 0x00, + 0xC2, 0x10, 0x00, 0x00, 0x54, 0x17, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7E, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0xC4, 0x10, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xD7, 0x08, 0x00, 0x00, + 0xD8, 0x08, 0x00, 0x00, 0xC9, 0x08, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0xD8, 0x08, 0x00, 0x00, 0x62, 0x17, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0xD7, 0x08, 0x00, 0x00, 0xDA, 0x08, 0x00, 0x00, + 0xC9, 0x08, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0xDA, 0x08, 0x00, 0x00, 0x70, 0x17, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xD7, 0x08, 0x00, 0x00, 0xDC, 0x08, 0x00, 0x00, 0xC9, 0x08, 0x00, 0x00, + 0xEA, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xDC, 0x08, 0x00, 0x00, + 0x7E, 0x17, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xDD, 0x08, 0x00, 0x00, 0xC9, 0x08, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0xDE, 0x08, 0x00, 0x00, 0xDD, 0x08, 0x00, 0x00, + 0xDD, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0xDF, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0xDE, 0x08, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xE0, 0x08, 0x00, 0x00, 0xC9, 0x08, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xE1, 0x08, 0x00, 0x00, 0xE0, 0x08, 0x00, 0x00, + 0xDF, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0xC9, 0x08, 0x00, 0x00, 0xE1, 0x08, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xD7, 0x08, 0x00, 0x00, 0xE3, 0x08, 0x00, 0x00, 0xC9, 0x08, 0x00, 0x00, + 0xE2, 0x08, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE3, 0x08, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..515bcb2abc9ee88bd7d93d6d1fdc7b0e7190b2f7 GIT binary patch literal 8892 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 literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.txt new file mode 100644 index 000000000..b2368939f --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.txt @@ -0,0 +1,445 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 6464 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %xe_frag_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 420 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %xe_texture "xe_texture" + OpName %gl_FragCoord "gl_FragCoord" + OpName %XeCasResampleConstants "XeCasResampleConstants" + OpMemberName %XeCasResampleConstants 0 "xe_cas_output_offset" + OpMemberName %XeCasResampleConstants 1 "xe_cas_input_output_size_ratio" + OpMemberName %XeCasResampleConstants 2 "xe_cas_sharpness_post_setup" + OpName %_ "" + OpName %xe_frag_color "xe_frag_color" + OpDecorate %xe_texture DescriptorSet 0 + OpDecorate %xe_texture Binding 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpMemberDecorate %XeCasResampleConstants 0 Offset 16 + OpMemberDecorate %XeCasResampleConstants 1 Offset 24 + OpMemberDecorate %XeCasResampleConstants 2 Offset 32 + OpDecorate %XeCasResampleConstants Block + OpDecorate %xe_frag_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %v3float = OpTypeVector %float 3 + %v2uint = OpTypeVector %uint 2 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 +%uint_532432441 = OpConstant %uint 532432441 +%uint_2129690299 = OpConstant %uint 2129690299 +%uint_2129764351 = OpConstant %uint 2129764351 + %float_2 = OpConstant %float 2 + %143 = OpTypeImage %float 2D 0 0 0 1 Unknown + %144 = OpTypeSampledImage %143 +%_ptr_UniformConstant_144 = OpTypePointer UniformConstant %144 + %xe_texture = OpVariable %_ptr_UniformConstant_144 UniformConstant + %int_0 = OpConstant %int 0 + %v4float = OpTypeVector %float 4 + %int_n1 = OpConstant %int -1 + %181 = OpConstantComposite %v2int %int_0 %int_n1 + %int_1 = OpConstant %int 1 + %188 = OpConstantComposite %v2int %int_1 %int_n1 + %194 = OpConstantComposite %v2int %int_n1 %int_0 + %204 = OpConstantComposite %v2int %int_1 %int_0 + %210 = OpConstantComposite %v2int %int_n1 %int_1 + %216 = OpConstantComposite %v2int %int_0 %int_1 + %222 = OpConstantComposite %v2int %int_1 %int_1 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %v2float = OpTypeVector %float 2 + %int_2 = OpConstant %int 2 + %712 = OpConstantComposite %v2int %int_2 %int_0 + %734 = OpConstantComposite %v2int %int_0 %int_2 + %745 = OpConstantComposite %v2int %int_2 %int_1 + %751 = OpConstantComposite %v2int %int_1 %int_2 +%float_0_03125 = OpConstant %float 0.03125 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%XeCasResampleConstants = OpTypeStruct %v2int %v2float %float +%_ptr_PushConstant_XeCasResampleConstants = OpTypePointer PushConstant %XeCasResampleConstants + %_ = OpVariable %_ptr_PushConstant_XeCasResampleConstants PushConstant +%_ptr_PushConstant_v2int = OpTypePointer PushConstant %v2int +%_ptr_PushConstant_v2float = OpTypePointer PushConstant %v2float + %float_0_5 = OpConstant %float 0.5 +%_ptr_PushConstant_float = OpTypePointer PushConstant %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%xe_frag_color = OpVariable %_ptr_Output_v4float Output +%_ptr_Output_float = OpTypePointer Output %float + %uint_3 = OpConstant %uint 3 + %6457 = OpConstantComposite %v2float %float_0_5 %float_0_5 + %main = OpFunction %void None %3 + %5 = OpLabel + %2205 = OpLoad %v4float %gl_FragCoord + %2206 = OpVectorShuffle %v2float %2205 %2205 0 1 + %2207 = OpConvertFToS %v2int %2206 + %2212 = OpAccessChain %_ptr_PushConstant_v2int %_ %int_0 + %2213 = OpLoad %v2int %2212 + %2214 = OpISub %v2int %2207 %2213 + %2215 = OpBitcast %v2uint %2214 + %2218 = OpAccessChain %_ptr_PushConstant_v2float %_ %int_1 + %2219 = OpLoad %v2float %2218 + %2220 = OpBitcast %v2uint %2219 + %2224 = OpVectorTimesScalar %v2float %2219 %float_0_5 + %2226 = OpFSub %v2float %2224 %6457 + %2227 = OpBitcast %v2uint %2226 + %2235 = OpAccessChain %_ptr_PushConstant_float %_ %int_2 + %2236 = OpLoad %float %2235 + %2237 = OpBitcast %uint %2236 + OpBranch %2781 + %2781 = OpLabel + %3132 = OpConvertUToF %v2float %2215 + %3138 = OpBitcast %v2float %2220 + %3139 = OpFMul %v2float %3132 %3138 + %3145 = OpBitcast %v2float %2227 + %3146 = OpFAdd %v2float %3139 %3145 + %3148 = OpExtInst %v2float %1 Floor %3146 + %3151 = OpFSub %v2float %3146 %3148 + %3153 = OpConvertFToS %v2int %3148 + %3158 = OpIAdd %v2int %3153 %181 + %4748 = OpLoad %144 %xe_texture + %4750 = OpImage %143 %4748 + %4751 = OpImageFetch %v4float %4750 %3158 Lod %int_0 + %3161 = OpIAdd %v2int %3153 %194 + %4757 = OpImage %143 %4748 + %4758 = OpImageFetch %v4float %4757 %3161 Lod %int_0 + %4764 = OpImage %143 %4748 + %4765 = OpImageFetch %v4float %4764 %3153 Lod %int_0 + %3166 = OpIAdd %v2int %3153 %188 + %4771 = OpImage %143 %4748 + %4772 = OpImageFetch %v4float %4771 %3166 Lod %int_0 + %3172 = OpIAdd %v2int %3153 %204 + %4785 = OpImage %143 %4748 + %4786 = OpImageFetch %v4float %4785 %3172 Lod %int_0 + %3175 = OpIAdd %v2int %3153 %712 + %4792 = OpImage %143 %4748 + %4793 = OpImageFetch %v4float %4792 %3175 Lod %int_0 + %3178 = OpIAdd %v2int %3153 %210 + %4799 = OpImage %143 %4748 + %4800 = OpImageFetch %v4float %4799 %3178 Lod %int_0 + %3181 = OpIAdd %v2int %3153 %216 + %4806 = OpImage %143 %4748 + %4807 = OpImageFetch %v4float %4806 %3181 Lod %int_0 + %3187 = OpIAdd %v2int %3153 %734 + %4820 = OpImage %143 %4748 + %4821 = OpImageFetch %v4float %4820 %3187 Lod %int_0 + %3190 = OpIAdd %v2int %3153 %222 + %4827 = OpImage %143 %4748 + %4828 = OpImageFetch %v4float %4827 %3190 Lod %int_0 + %3193 = OpIAdd %v2int %3153 %745 + %4834 = OpImage %143 %4748 + %4835 = OpImageFetch %v4float %4834 %3193 Lod %int_0 + %3196 = OpIAdd %v2int %3153 %751 + %4841 = OpImage %143 %4748 + %4842 = OpImageFetch %v4float %4841 %3196 Lod %int_0 + %3215 = OpCompositeExtract %float %4751 0 + %3217 = OpCompositeExtract %float %4751 1 + %3219 = OpCompositeExtract %float %4751 2 + %4864 = OpFMul %float %3215 %3215 + %4867 = OpFMul %float %3217 %3217 + %4870 = OpFMul %float %3219 %3219 + %3228 = OpCompositeExtract %float %4772 0 + %3230 = OpCompositeExtract %float %4772 1 + %3232 = OpCompositeExtract %float %4772 2 + %4874 = OpFMul %float %3228 %3228 + %4877 = OpFMul %float %3230 %3230 + %4880 = OpFMul %float %3232 %3232 + %3254 = OpCompositeExtract %float %4758 0 + %3256 = OpCompositeExtract %float %4758 1 + %3258 = OpCompositeExtract %float %4758 2 + %4894 = OpFMul %float %3254 %3254 + %4897 = OpFMul %float %3256 %3256 + %4900 = OpFMul %float %3258 %3258 + %3267 = OpCompositeExtract %float %4765 0 + %3269 = OpCompositeExtract %float %4765 1 + %3271 = OpCompositeExtract %float %4765 2 + %4904 = OpFMul %float %3267 %3267 + %4907 = OpFMul %float %3269 %3269 + %4910 = OpFMul %float %3271 %3271 + %3280 = OpCompositeExtract %float %4786 0 + %3282 = OpCompositeExtract %float %4786 1 + %3284 = OpCompositeExtract %float %4786 2 + %4914 = OpFMul %float %3280 %3280 + %4917 = OpFMul %float %3282 %3282 + %4920 = OpFMul %float %3284 %3284 + %3293 = OpCompositeExtract %float %4793 0 + %3295 = OpCompositeExtract %float %4793 1 + %3297 = OpCompositeExtract %float %4793 2 + %4924 = OpFMul %float %3293 %3293 + %4927 = OpFMul %float %3295 %3295 + %4930 = OpFMul %float %3297 %3297 + %3306 = OpCompositeExtract %float %4800 0 + %3308 = OpCompositeExtract %float %4800 1 + %3310 = OpCompositeExtract %float %4800 2 + %4934 = OpFMul %float %3306 %3306 + %4937 = OpFMul %float %3308 %3308 + %4940 = OpFMul %float %3310 %3310 + %3319 = OpCompositeExtract %float %4807 0 + %3321 = OpCompositeExtract %float %4807 1 + %3323 = OpCompositeExtract %float %4807 2 + %4944 = OpFMul %float %3319 %3319 + %4947 = OpFMul %float %3321 %3321 + %4950 = OpFMul %float %3323 %3323 + %3332 = OpCompositeExtract %float %4828 0 + %3334 = OpCompositeExtract %float %4828 1 + %3336 = OpCompositeExtract %float %4828 2 + %4954 = OpFMul %float %3332 %3332 + %4957 = OpFMul %float %3334 %3334 + %4960 = OpFMul %float %3336 %3336 + %3345 = OpCompositeExtract %float %4835 0 + %3347 = OpCompositeExtract %float %4835 1 + %3349 = OpCompositeExtract %float %4835 2 + %4964 = OpFMul %float %3345 %3345 + %4967 = OpFMul %float %3347 %3347 + %4970 = OpFMul %float %3349 %3349 + %3371 = OpCompositeExtract %float %4821 0 + %3373 = OpCompositeExtract %float %4821 1 + %3375 = OpCompositeExtract %float %4821 2 + %4984 = OpFMul %float %3371 %3371 + %4987 = OpFMul %float %3373 %3373 + %4990 = OpFMul %float %3375 %3375 + %3384 = OpCompositeExtract %float %4842 0 + %3386 = OpCompositeExtract %float %4842 1 + %3388 = OpCompositeExtract %float %4842 2 + %4994 = OpFMul %float %3384 %3384 + %4997 = OpFMul %float %3386 %3386 + %5000 = OpFMul %float %3388 %3388 + %5030 = OpExtInst %float %1 FMin %4897 %4907 + %5031 = OpExtInst %float %1 FMin %4867 %5030 + %5037 = OpExtInst %float %1 FMin %4917 %4947 + %5038 = OpExtInst %float %1 FMin %5031 %5037 + %5072 = OpExtInst %float %1 FMax %4897 %4907 + %5073 = OpExtInst %float %1 FMax %4867 %5072 + %5079 = OpExtInst %float %1 FMax %4917 %4947 + %5080 = OpExtInst %float %1 FMax %5073 %5079 + %5114 = OpExtInst %float %1 FMin %4907 %4917 + %5115 = OpExtInst %float %1 FMin %4877 %5114 + %5121 = OpExtInst %float %1 FMin %4927 %4957 + %5122 = OpExtInst %float %1 FMin %5115 %5121 + %5156 = OpExtInst %float %1 FMax %4907 %4917 + %5157 = OpExtInst %float %1 FMax %4877 %5156 + %5163 = OpExtInst %float %1 FMax %4927 %4957 + %5164 = OpExtInst %float %1 FMax %5157 %5163 + %5198 = OpExtInst %float %1 FMin %4937 %4947 + %5199 = OpExtInst %float %1 FMin %4907 %5198 + %5205 = OpExtInst %float %1 FMin %4957 %4987 + %5206 = OpExtInst %float %1 FMin %5199 %5205 + %5240 = OpExtInst %float %1 FMax %4937 %4947 + %5241 = OpExtInst %float %1 FMax %4907 %5240 + %5247 = OpExtInst %float %1 FMax %4957 %4987 + %5248 = OpExtInst %float %1 FMax %5241 %5247 + %5282 = OpExtInst %float %1 FMin %4947 %4957 + %5283 = OpExtInst %float %1 FMin %4917 %5282 + %5289 = OpExtInst %float %1 FMin %4967 %4997 + %5290 = OpExtInst %float %1 FMin %5283 %5289 + %5324 = OpExtInst %float %1 FMax %4947 %4957 + %5325 = OpExtInst %float %1 FMax %4917 %5324 + %5331 = OpExtInst %float %1 FMax %4967 %4997 + %5332 = OpExtInst %float %1 FMax %5325 %5331 + %5363 = OpBitcast %uint %5080 + %5364 = OpISub %uint %uint_2129690299 %5363 + %5365 = OpBitcast %float %5364 + %5396 = OpBitcast %uint %5164 + %5397 = OpISub %uint %uint_2129690299 %5396 + %5398 = OpBitcast %float %5397 + %5429 = OpBitcast %uint %5248 + %5430 = OpISub %uint %uint_2129690299 %5429 + %5431 = OpBitcast %float %5430 + %5462 = OpBitcast %uint %5332 + %5463 = OpISub %uint %uint_2129690299 %5462 + %5464 = OpBitcast %float %5463 + %3732 = OpFSub %float %float_1 %5080 + %3733 = OpExtInst %float %1 FMin %5038 %3732 + %3735 = OpFMul %float %3733 %5365 + %5506 = OpExtInst %float %1 FClamp %3735 %float_0 %float_1 + %3756 = OpFSub %float %float_1 %5164 + %3757 = OpExtInst %float %1 FMin %5122 %3756 + %3759 = OpFMul %float %3757 %5398 + %5557 = OpExtInst %float %1 FClamp %3759 %float_0 %float_1 + %3780 = OpFSub %float %float_1 %5248 + %3781 = OpExtInst %float %1 FMin %5206 %3780 + %3783 = OpFMul %float %3781 %5431 + %5608 = OpExtInst %float %1 FClamp %3783 %float_0 %float_1 + %3804 = OpFSub %float %float_1 %5332 + %3805 = OpExtInst %float %1 FMin %5290 %3804 + %3807 = OpFMul %float %3805 %5464 + %5659 = OpExtInst %float %1 FClamp %3807 %float_0 %float_1 + %5705 = OpBitcast %uint %5506 + %5707 = OpShiftRightLogical %uint %5705 %uint_1 + %5709 = OpIAdd %uint %5707 %uint_532432441 + %5710 = OpBitcast %float %5709 + %5756 = OpBitcast %uint %5557 + %5758 = OpShiftRightLogical %uint %5756 %uint_1 + %5760 = OpIAdd %uint %5758 %uint_532432441 + %5761 = OpBitcast %float %5760 + %5807 = OpBitcast %uint %5608 + %5809 = OpShiftRightLogical %uint %5807 %uint_1 + %5811 = OpIAdd %uint %5809 %uint_532432441 + %5812 = OpBitcast %float %5811 + %5858 = OpBitcast %uint %5659 + %5860 = OpShiftRightLogical %uint %5858 %uint_1 + %5862 = OpIAdd %uint %5860 %uint_532432441 + %5863 = OpBitcast %float %5862 + %3843 = OpBitcast %float %2237 + %3849 = OpFMul %float %5710 %3843 + %3858 = OpFMul %float %5761 %3843 + %3867 = OpFMul %float %5812 %3843 + %3876 = OpFMul %float %5863 %3843 + %3882 = OpCompositeExtract %float %3151 0 + %3883 = OpFSub %float %float_1 %3882 + %3886 = OpCompositeExtract %float %3151 1 + %3887 = OpFSub %float %float_1 %3886 + %3888 = OpFMul %float %3883 %3887 + %3895 = OpFMul %float %3882 %3887 + %3902 = OpFMul %float %3883 %3886 + %3907 = OpFMul %float %3882 %3886 + %3911 = OpFSub %float %5080 %5038 + %3912 = OpFAdd %float %float_0_03125 %3911 + %5904 = OpBitcast %uint %3912 + %5905 = OpISub %uint %uint_2129690299 %5904 + %5906 = OpBitcast %float %5905 + %3915 = OpFMul %float %3888 %5906 + %3919 = OpFSub %float %5164 %5122 + %3920 = OpFAdd %float %float_0_03125 %3919 + %5915 = OpBitcast %uint %3920 + %5916 = OpISub %uint %uint_2129690299 %5915 + %5917 = OpBitcast %float %5916 + %3923 = OpFMul %float %3895 %5917 + %3927 = OpFSub %float %5248 %5206 + %3928 = OpFAdd %float %float_0_03125 %3927 + %5926 = OpBitcast %uint %3928 + %5927 = OpISub %uint %uint_2129690299 %5926 + %5928 = OpBitcast %float %5927 + %3931 = OpFMul %float %3902 %5928 + %3935 = OpFSub %float %5332 %5290 + %3936 = OpFAdd %float %float_0_03125 %3935 + %5937 = OpBitcast %uint %3936 + %5938 = OpISub %uint %uint_2129690299 %5937 + %5939 = OpBitcast %float %5938 + %3939 = OpFMul %float %3907 %5939 + %3945 = OpFMul %float %3849 %3915 + %3954 = OpFMul %float %3858 %3923 + %3972 = OpFMul %float %3867 %3931 + %3973 = OpFAdd %float %3954 %3972 + %3975 = OpFAdd %float %3973 %3915 + %3999 = OpFMul %float %3876 %3939 + %4000 = OpFAdd %float %3945 %3999 + %4002 = OpFAdd %float %4000 %3923 + %4029 = OpFAdd %float %4000 %3931 + %4056 = OpFAdd %float %3973 %3939 + %6458 = OpFAdd %float %3945 %3954 + %6459 = OpFAdd %float %6458 %3972 + %6460 = OpFAdd %float %6459 %3999 + %4098 = OpFMul %float %float_2 %6460 + %4100 = OpFAdd %float %4098 %3975 + %4102 = OpFAdd %float %4100 %4002 + %4104 = OpFAdd %float %4102 %4029 + %4106 = OpFAdd %float %4104 %4056 + %5962 = OpBitcast %uint %4106 + %5963 = OpISub %uint %uint_2129764351 %5962 + %5964 = OpBitcast %float %5963 + %5967 = OpFNegate %float %5964 + %5969 = OpFMul %float %5967 %4106 + %5971 = OpFAdd %float %5969 %float_2 + %5972 = OpFMul %float %5964 %5971 + %6461 = OpFAdd %float %4864 %4894 + %4116 = OpFMul %float %3945 %6461 + %4120 = OpFMul %float %4874 %3954 + %4121 = OpFAdd %float %4116 %4120 + %4125 = OpFMul %float %4924 %3954 + %4126 = OpFAdd %float %4121 %4125 + %4130 = OpFMul %float %4934 %3972 + %4131 = OpFAdd %float %4126 %4130 + %4135 = OpFMul %float %4984 %3972 + %4136 = OpFAdd %float %4131 %4135 + %4140 = OpFMul %float %4964 %3999 + %4141 = OpFAdd %float %4136 %4140 + %4145 = OpFMul %float %4994 %3999 + %4146 = OpFAdd %float %4141 %4145 + %4150 = OpFMul %float %4904 %3975 + %4151 = OpFAdd %float %4146 %4150 + %4155 = OpFMul %float %4914 %4002 + %4156 = OpFAdd %float %4151 %4155 + %4160 = OpFMul %float %4944 %4029 + %4161 = OpFAdd %float %4156 %4160 + %4165 = OpFMul %float %4954 %4056 + %4166 = OpFAdd %float %4161 %4165 + %4168 = OpFMul %float %4166 %5972 + %5986 = OpExtInst %float %1 FClamp %4168 %float_0 %float_1 + %6462 = OpFAdd %float %4867 %4897 + %4178 = OpFMul %float %3945 %6462 + %4182 = OpFMul %float %4877 %3954 + %4183 = OpFAdd %float %4178 %4182 + %4187 = OpFMul %float %4927 %3954 + %4188 = OpFAdd %float %4183 %4187 + %4192 = OpFMul %float %4937 %3972 + %4193 = OpFAdd %float %4188 %4192 + %4197 = OpFMul %float %4987 %3972 + %4198 = OpFAdd %float %4193 %4197 + %4202 = OpFMul %float %4967 %3999 + %4203 = OpFAdd %float %4198 %4202 + %4207 = OpFMul %float %4997 %3999 + %4208 = OpFAdd %float %4203 %4207 + %4212 = OpFMul %float %4907 %3975 + %4213 = OpFAdd %float %4208 %4212 + %4217 = OpFMul %float %4917 %4002 + %4218 = OpFAdd %float %4213 %4217 + %4222 = OpFMul %float %4947 %4029 + %4223 = OpFAdd %float %4218 %4222 + %4227 = OpFMul %float %4957 %4056 + %4228 = OpFAdd %float %4223 %4227 + %4230 = OpFMul %float %4228 %5972 + %6000 = OpExtInst %float %1 FClamp %4230 %float_0 %float_1 + %6463 = OpFAdd %float %4870 %4900 + %4240 = OpFMul %float %3945 %6463 + %4244 = OpFMul %float %4880 %3954 + %4245 = OpFAdd %float %4240 %4244 + %4249 = OpFMul %float %4930 %3954 + %4250 = OpFAdd %float %4245 %4249 + %4254 = OpFMul %float %4940 %3972 + %4255 = OpFAdd %float %4250 %4254 + %4259 = OpFMul %float %4990 %3972 + %4260 = OpFAdd %float %4255 %4259 + %4264 = OpFMul %float %4970 %3999 + %4265 = OpFAdd %float %4260 %4264 + %4269 = OpFMul %float %5000 %3999 + %4270 = OpFAdd %float %4265 %4269 + %4274 = OpFMul %float %4910 %3975 + %4275 = OpFAdd %float %4270 %4274 + %4279 = OpFMul %float %4920 %4002 + %4280 = OpFAdd %float %4275 %4279 + %4284 = OpFMul %float %4950 %4029 + %4285 = OpFAdd %float %4280 %4284 + %4289 = OpFMul %float %4960 %4056 + %4290 = OpFAdd %float %4285 %4289 + %4292 = OpFMul %float %4290 %5972 + %6014 = OpExtInst %float %1 FClamp %4292 %float_0 %float_1 + %2264 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_0 + OpStore %2264 %5986 + %2266 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_1 + OpStore %2266 %6000 + %2268 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_2 + OpStore %2268 %6014 + %2269 = OpLoad %v4float %xe_frag_color + %2270 = OpVectorShuffle %v3float %2269 %2269 0 1 2 + %2271 = OpExtInst %v3float %1 Sqrt %2270 + %2272 = OpLoad %v4float %xe_frag_color + %2273 = OpVectorShuffle %v4float %2272 %2271 4 5 6 3 + OpStore %xe_frag_color %2273 + %2275 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_3 + OpStore %2275 %float_1 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.h new file mode 100644 index 000000000..9e01621fa --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.h @@ -0,0 +1,1212 @@ +// generated from `xb genspirv` +// source: guest_output_ffx_cas_sharpen_dither.frag +const uint8_t guest_output_ffx_cas_sharpen_dither_frag[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x6C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0xB4, 0x09, 0x00, 0x00, 0xD0, 0x09, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0A, 0x00, + 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, + 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, + 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, + 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0xB4, 0x09, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, + 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, + 0xB8, 0x09, 0x00, 0x00, 0x58, 0x65, 0x43, 0x61, 0x73, 0x53, 0x68, 0x61, + 0x72, 0x70, 0x65, 0x6E, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, 0xB8, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x6F, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, 0xB8, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x73, + 0x68, 0x61, 0x72, 0x70, 0x6E, 0x65, 0x73, 0x73, 0x5F, 0x70, 0x6F, 0x73, + 0x74, 0x5F, 0x73, 0x65, 0x74, 0x75, 0x70, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xBA, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0xD0, 0x09, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x66, 0x72, 0x61, 0x67, 0x5F, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xAB, 0x01, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xB4, 0x09, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0xB8, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xB8, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xB8, 0x09, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xD0, 0x09, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x1C, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x00, 0x00, 0x84, 0x83, 0x83, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xE7, 0xE6, 0x66, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0xDE, 0xDD, 0xDD, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0xCC, 0xCB, 0xCB, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0xA8, 0xA7, 0xA7, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x9F, 0x9E, 0x1E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x8C, 0x8B, 0x8B, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xB6, 0xB5, 0xB5, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0xB2, 0xB1, 0xB1, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5A, 0x00, 0x00, 0x00, 0x87, 0x86, 0x06, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xA0, 0x9F, 0x9F, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0xD2, 0xD1, 0xD1, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5D, 0x00, 0x00, 0x00, 0x9B, 0x9A, 0x1A, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x95, 0x94, 0x94, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x92, 0x91, 0x91, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xBA, 0xB9, 0xB9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0xBE, 0xBD, 0xBD, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x8F, 0x8E, 0x0E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x89, 0x88, 0x08, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x9E, 0x9D, 0x9D, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0xE5, 0xE4, 0xE4, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x8A, 0x89, 0x89, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0xA3, 0xA2, 0x22, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0xFD, 0xFC, 0xFC, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x69, 0x00, 0x00, 0x00, 0xF6, 0xF5, 0xF5, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x90, 0x8F, 0x8F, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, + 0xD1, 0xD0, 0xD0, 0xB8, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6C, 0x00, 0x00, 0x00, 0x8B, 0x8A, 0x0A, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0xBC, 0xBB, 0xBB, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, + 0xFE, 0xFD, 0xFD, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0xB7, 0xB6, 0x36, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xA1, 0xA0, 0x20, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, + 0xBB, 0xBA, 0x3A, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0xAC, 0xAB, 0xAB, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x90, 0x8F, 0x8F, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x9D, 0x9C, 0x9C, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x75, 0x00, 0x00, 0x00, 0xE6, 0xE5, 0xE5, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0xDC, 0xDB, 0xDB, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0xC2, 0xC1, 0xC1, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0xA1, 0xA0, 0x20, 0xB8, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0xCF, 0xCE, 0x4E, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, + 0xF0, 0xEF, 0xEF, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7B, 0x00, 0x00, 0x00, 0x9D, 0x9C, 0x9C, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x82, 0x81, 0x81, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, + 0x9A, 0x99, 0x99, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0xB9, 0xB8, 0x38, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xD8, 0xD7, 0xD7, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xD0, 0xCF, 0xCF, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0xB5, 0xB4, 0xB4, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0xF4, 0xF3, 0xF3, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, + 0xE8, 0xE7, 0xE7, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0xE3, 0xE2, 0x62, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0xEB, 0xEA, 0x6A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, + 0xA9, 0xA8, 0x28, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x00, 0x00, 0xF3, 0xF2, 0x72, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xC8, 0xC7, 0xC7, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0xAB, 0xAA, 0x2A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8A, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xA9, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0xD3, 0xD2, 0x52, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, + 0xC0, 0xBF, 0xBF, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8D, 0x00, 0x00, 0x00, 0xEC, 0xEB, 0xEB, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0xDF, 0xDE, 0x5E, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0xDF, 0xDE, 0x5E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0xED, 0xEC, 0xEC, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x8C, 0x8B, 0x8B, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x99, 0x98, 0x18, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0xF9, 0xF8, 0x78, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0xBA, 0xB9, 0xB9, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x9B, 0x9A, 0x1A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x96, 0x00, 0x00, 0x00, 0xC9, 0xC8, 0x48, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0xD2, 0xD1, 0xD1, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x86, 0x85, 0x85, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x00, 0x00, 0xD5, 0xD4, 0xD4, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0xD9, 0xD8, 0x58, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, + 0xD0, 0xCF, 0xCF, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9C, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0x2A, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x91, 0x90, 0x90, 0xB8, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, + 0xDD, 0xDC, 0xDC, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x9F, 0x00, 0x00, 0x00, 0xD8, 0xD7, 0xD7, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0xB4, 0xB3, 0xB3, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, + 0xF3, 0xF2, 0x72, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA2, 0x00, 0x00, 0x00, 0xAF, 0xAE, 0x2E, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x9C, 0x9B, 0x9B, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, + 0xBE, 0xBD, 0xBD, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA5, 0x00, 0x00, 0x00, 0x97, 0x96, 0x16, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, 0xF8, 0xF7, 0xF7, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, + 0xC7, 0xC6, 0x46, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA8, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xA9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xFC, 0xFB, 0xFB, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, + 0xDA, 0xD9, 0xD9, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAB, 0x00, 0x00, 0x00, 0x88, 0x87, 0x87, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x83, 0x82, 0x02, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, + 0xF4, 0xF3, 0xF3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAE, 0x00, 0x00, 0x00, 0x9E, 0x9D, 0x9D, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xAD, 0xAC, 0xAC, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, + 0xB4, 0xB3, 0xB3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0xD6, 0xD5, 0xD5, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xF1, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, + 0xBF, 0xBE, 0x3E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB4, 0x00, 0x00, 0x00, 0xEF, 0xEE, 0x6E, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0xEE, 0xED, 0xED, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, + 0x96, 0x95, 0x95, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB7, 0x00, 0x00, 0x00, 0xC5, 0xC4, 0xC4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0xD1, 0xD0, 0xD0, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, + 0xC3, 0xC2, 0x42, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBA, 0x00, 0x00, 0x00, 0x9A, 0x99, 0x99, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x91, 0x90, 0x90, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0xC4, 0xC3, 0xC3, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBD, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x7E, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, 0xFB, 0xFA, 0x7A, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, + 0xFE, 0xFD, 0xFD, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x8B, 0x8A, 0x0A, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xE1, 0xE0, 0x60, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, + 0xA4, 0xA3, 0xA3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x00, 0x00, 0x8D, 0x8C, 0x8C, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0xCA, 0xC9, 0xC9, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, + 0x81, 0x80, 0x00, 0xB7, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC6, 0x00, 0x00, 0x00, 0xA5, 0xA4, 0xA4, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0xB0, 0xAF, 0xAF, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, + 0xE6, 0xE5, 0xE5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC9, 0x00, 0x00, 0x00, 0x83, 0x82, 0x02, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCA, 0x00, 0x00, 0x00, 0x9C, 0x9B, 0x9B, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, + 0xCB, 0xCA, 0x4A, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCC, 0x00, 0x00, 0x00, 0xB0, 0xAF, 0xAF, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x8D, 0x8C, 0x8C, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, + 0xC2, 0xC1, 0xC1, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCF, 0x00, 0x00, 0x00, 0x89, 0x88, 0x08, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0xCB, 0xCA, 0x4A, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, + 0xAC, 0xAB, 0xAB, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD2, 0x00, 0x00, 0x00, 0xCD, 0xCC, 0xCC, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0xFB, 0xFA, 0x7A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, + 0xF5, 0xF4, 0xF4, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD5, 0x00, 0x00, 0x00, 0xCA, 0xC9, 0xC9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x88, 0x87, 0x87, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, + 0xB3, 0xB2, 0x32, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD8, 0x00, 0x00, 0x00, 0xDC, 0xDB, 0xDB, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x00, 0x00, 0x84, 0x83, 0x83, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, + 0xDD, 0xDC, 0xDC, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDB, 0x00, 0x00, 0x00, 0xEA, 0xE9, 0xE9, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0xF9, 0xF8, 0x78, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, + 0xA3, 0xA2, 0x22, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0x00, 0xDA, 0xD9, 0xD9, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0xDB, 0xDA, 0x5A, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, + 0xE2, 0xE1, 0xE1, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE1, 0x00, 0x00, 0x00, 0x8E, 0x8D, 0x8D, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xE8, 0xE7, 0xE7, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0xCF, 0xCE, 0x4E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE4, 0x00, 0x00, 0x00, 0xEA, 0xE9, 0xE9, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xB3, 0xB2, 0x32, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE6, 0x00, 0x00, 0x00, + 0x82, 0x81, 0x81, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE7, 0x00, 0x00, 0x00, 0xAE, 0xAD, 0xAD, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xB9, 0xB8, 0x38, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, + 0xC6, 0xC5, 0xC5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xEA, 0x00, 0x00, 0x00, 0xAD, 0xAC, 0xAC, 0x39, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0xAF, 0xAE, 0x2E, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xED, 0x00, 0x00, 0x00, 0xD4, 0xD3, 0xD3, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0x98, 0x97, 0x97, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, + 0xED, 0xEC, 0xEC, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0x00, 0xE5, 0xE4, 0xE4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xC9, 0xC8, 0x48, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, + 0xC4, 0xC3, 0xC3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF3, 0x00, 0x00, 0x00, 0x96, 0x95, 0x95, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0xB1, 0xB0, 0xB0, 0x38, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, + 0xF8, 0xF7, 0xF7, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF6, 0x00, 0x00, 0x00, 0xB8, 0xB7, 0xB7, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0x93, 0x92, 0x12, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, + 0xF1, 0xF0, 0xF0, 0x38, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF9, 0x00, 0x00, 0x00, 0xA6, 0xA5, 0xA5, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x97, 0x96, 0x16, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, + 0xFF, 0xFE, 0x7E, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0xAE, 0xAD, 0xAD, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xE9, 0xE8, 0x68, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0xC7, 0xC6, 0x46, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0xB2, 0xB1, 0xB1, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xC0, 0xBF, 0xBF, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0xE3, 0xE2, 0x62, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x00, 0x00, 0xBD, 0xBC, 0xBC, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x8F, 0x8E, 0x0E, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x94, 0x93, 0x93, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x05, 0x01, 0x00, 0x00, 0x85, 0x84, 0x84, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x93, 0x92, 0x12, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, + 0xEE, 0xED, 0xED, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x08, 0x01, 0x00, 0x00, 0xE2, 0xE1, 0xE1, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x98, 0x97, 0x97, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, + 0xC3, 0xC2, 0x42, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0B, 0x01, 0x00, 0x00, 0xCE, 0xCD, 0xCD, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0xF1, 0xF0, 0xF0, 0xB8, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, + 0xA0, 0x9F, 0x9F, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0E, 0x01, 0x00, 0x00, 0xF0, 0xEF, 0xEF, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0xC1, 0xC0, 0xC0, 0x37, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, + 0xFC, 0xFB, 0xFB, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x11, 0x01, 0x00, 0x00, 0xE4, 0xE3, 0xE3, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0xBB, 0xBA, 0x3A, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, + 0xCC, 0xCB, 0xCB, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0xE0, 0xDF, 0xDF, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0xA2, 0xA1, 0xA1, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, + 0xD9, 0xD8, 0x58, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x17, 0x01, 0x00, 0x00, 0x8A, 0x89, 0x89, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0xBF, 0xBE, 0x3E, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, + 0xD6, 0xD5, 0xD5, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1A, 0x01, 0x00, 0x00, 0xA5, 0xA4, 0xA4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xFD, 0xFC, 0xFC, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, + 0xEC, 0xEB, 0xEB, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1D, 0x01, 0x00, 0x00, 0xF7, 0xF6, 0x76, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0xB5, 0xB4, 0xB4, 0xB9, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, + 0xB7, 0xB6, 0x36, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x00, 0x00, 0x86, 0x85, 0x85, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0xCE, 0xCD, 0xCD, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, + 0xC1, 0xC0, 0xC0, 0xB7, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x23, 0x01, 0x00, 0x00, 0xA8, 0xA7, 0xA7, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0xD3, 0xD2, 0x52, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, + 0xEB, 0xEA, 0x6A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x26, 0x01, 0x00, 0x00, 0xBC, 0xBB, 0xBB, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, 0x9F, 0x9E, 0x1E, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, + 0xB1, 0xB0, 0xB0, 0xB8, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x00, 0xEF, 0xEE, 0x6E, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, + 0xB8, 0xB7, 0xB7, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0xA7, 0xA6, 0x26, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, 0xC6, 0xC5, 0xC5, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x00, + 0xD7, 0xD6, 0x56, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2F, 0x01, 0x00, 0x00, 0xA4, 0xA3, 0xA3, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x85, 0x84, 0x84, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, + 0xC8, 0xC7, 0xC7, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x32, 0x01, 0x00, 0x00, 0x94, 0x93, 0x93, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, 0xBD, 0xBC, 0xBC, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, + 0xCD, 0xCC, 0xCC, 0xB9, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x35, 0x01, 0x00, 0x00, 0xFA, 0xF9, 0xF9, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0xB6, 0xB5, 0xB5, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, + 0x92, 0x91, 0x91, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x38, 0x01, 0x00, 0x00, 0xD4, 0xD3, 0xD3, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0xE7, 0xE6, 0x66, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, + 0x99, 0x98, 0x18, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3B, 0x01, 0x00, 0x00, 0x8E, 0x8D, 0x8D, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0xE1, 0xE0, 0x60, 0xB8, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3D, 0x01, 0x00, 0x00, + 0x95, 0x94, 0x94, 0x39, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3E, 0x01, 0x00, 0x00, 0xDE, 0xDD, 0xDD, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3F, 0x01, 0x00, 0x00, 0xA6, 0xA5, 0xA5, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, + 0x87, 0x86, 0x06, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x41, 0x01, 0x00, 0x00, 0xA9, 0xA8, 0x28, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x42, 0x01, 0x00, 0x00, 0xF6, 0xF5, 0xF5, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, + 0xDB, 0xDA, 0x5A, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x44, 0x01, 0x00, 0x00, 0xA7, 0xA6, 0x26, 0x3A, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x81, 0x80, 0x00, 0x37, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x46, 0x01, 0x00, 0x00, + 0xFA, 0xF9, 0xF9, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x47, 0x01, 0x00, 0x00, 0xE9, 0xE8, 0x68, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0xC5, 0xC4, 0xC4, 0x39, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, + 0xE4, 0xE3, 0xE3, 0xBA, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4A, 0x01, 0x00, 0x00, 0xD5, 0xD4, 0xD4, 0xB9, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4B, 0x01, 0x00, 0x00, 0xA2, 0xA1, 0xA1, 0xBA, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00, + 0xE0, 0xDF, 0xDF, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4D, 0x01, 0x00, 0x00, 0xF7, 0xF6, 0x76, 0xBA, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4E, 0x01, 0x00, 0x00, 0xD7, 0xD6, 0x56, 0x3A, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, + 0xF2, 0xF1, 0xF1, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x50, 0x01, 0x00, 0x00, 0xF5, 0xF4, 0xF4, 0x39, 0x2C, 0x00, 0x03, 0x01, + 0x50, 0x00, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x55, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, + 0x5B, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, + 0x5E, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, + 0x6A, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, + 0x6D, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, + 0x76, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, + 0x8B, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, + 0x8E, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x91, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, + 0x9A, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, + 0x9D, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, + 0xA0, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00, + 0xA3, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, + 0xA6, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, + 0xA9, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, + 0xAF, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, + 0xB2, 0x00, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, + 0xB5, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, + 0xB8, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, + 0xBE, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, + 0xC4, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0xC7, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0xC9, 0x00, 0x00, 0x00, + 0xCA, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, + 0xCD, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, + 0xD0, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, + 0xD3, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, + 0xD6, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, + 0xD9, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, + 0xDC, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, + 0xDF, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, + 0xE2, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, + 0xE5, 0x00, 0x00, 0x00, 0xE6, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, + 0xE8, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, + 0xEB, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, + 0xEE, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0xF1, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, + 0xF4, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, + 0xF7, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, + 0xFA, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, + 0x06, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, + 0x09, 0x01, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, + 0x0C, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, + 0x0F, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, + 0x12, 0x01, 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, + 0x15, 0x01, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, + 0x18, 0x01, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x1A, 0x01, 0x00, 0x00, + 0x1B, 0x01, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x1D, 0x01, 0x00, 0x00, + 0x1E, 0x01, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, 0x23, 0x01, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x00, 0x25, 0x01, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, + 0x27, 0x01, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, + 0x2A, 0x01, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, + 0x2D, 0x01, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x00, 0x2F, 0x01, 0x00, 0x00, + 0x30, 0x01, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, + 0x33, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, + 0x36, 0x01, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, + 0x39, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, + 0x3C, 0x01, 0x00, 0x00, 0x3D, 0x01, 0x00, 0x00, 0x3E, 0x01, 0x00, 0x00, + 0x3F, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, + 0x42, 0x01, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x45, 0x01, 0x00, 0x00, 0x46, 0x01, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, + 0x48, 0x01, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x4A, 0x01, 0x00, 0x00, + 0x4B, 0x01, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00, 0x4D, 0x01, 0x00, 0x00, + 0x4E, 0x01, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x55, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x5B, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, + 0x39, 0x46, 0xBC, 0x1F, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x8B, 0x01, 0x00, 0x00, 0xBB, 0x7E, 0xF0, 0x7E, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0xFF, 0x9F, 0xF1, 0x7E, + 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA1, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x19, 0x00, 0x09, 0x00, 0xA8, 0x01, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0xA9, 0x01, 0x00, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xAA, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xA9, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0xAA, 0x01, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xCE, 0x01, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xD5, 0x01, 0x00, 0x00, + 0xD4, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0xDB, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xE5, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xEB, 0x01, 0x00, 0x00, + 0xC7, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0xF1, 0x01, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0xD4, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xF7, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x9E, 0x03, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0xD4, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, + 0xD4, 0x03, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0xF6, 0x03, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0xD4, 0x03, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0xD4, 0x03, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x04, 0x00, 0x00, + 0xD4, 0x01, 0x00, 0x00, 0xD4, 0x03, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, + 0x20, 0x00, 0x04, 0x00, 0xB3, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xB3, 0x09, 0x00, 0x00, + 0xB4, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, + 0xB8, 0x09, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xB9, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB8, 0x09, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xB9, 0x09, 0x00, 0x00, + 0xBA, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xBB, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xC1, 0x09, 0x00, 0x00, + 0x79, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xC4, 0x09, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xCF, 0x09, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0xCF, 0x09, 0x00, 0x00, 0xD0, 0x09, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xDE, 0x09, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, + 0x32, 0x00, 0x00, 0x00, 0xF0, 0x09, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00, + 0x32, 0x00, 0x00, 0x00, 0xF1, 0x09, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x79, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF5, 0x09, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6B, 0x1A, 0x00, 0x00, + 0x4B, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x5B, 0x01, 0x00, 0x00, 0x98, 0x18, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xB5, 0x09, 0x00, 0x00, 0xB4, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0xB6, 0x09, 0x00, 0x00, 0xB5, 0x09, 0x00, 0x00, + 0xB5, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x6E, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, 0xB7, 0x09, 0x00, 0x00, + 0xB6, 0x09, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xBB, 0x09, 0x00, 0x00, + 0xBC, 0x09, 0x00, 0x00, 0xBA, 0x09, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, 0xBD, 0x09, 0x00, 0x00, + 0xBC, 0x09, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xBE, 0x09, 0x00, 0x00, 0xB7, 0x09, 0x00, 0x00, 0xBD, 0x09, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xBF, 0x09, 0x00, 0x00, + 0xBE, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x3D, 0x00, 0x00, 0x00, + 0xC2, 0x09, 0x00, 0x00, 0xC1, 0x09, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xC4, 0x09, 0x00, 0x00, 0xC5, 0x09, 0x00, 0x00, 0xBA, 0x09, 0x00, 0x00, + 0xD4, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC6, 0x09, 0x00, 0x00, 0xC5, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC7, 0x09, 0x00, 0x00, 0xC6, 0x09, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0xF0, 0x0B, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0xF0, 0x0B, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, 0x9E, 0x03, 0x00, 0x00, + 0x4F, 0x0D, 0x00, 0x00, 0xBF, 0x09, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x52, 0x0D, 0x00, 0x00, 0xC2, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x53, 0x0D, 0x00, 0x00, 0xC2, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x54, 0x0D, 0x00, 0x00, + 0x52, 0x0D, 0x00, 0x00, 0x53, 0x0D, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x55, 0x0D, 0x00, 0x00, 0x54, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x9E, 0x03, 0x00, 0x00, 0x56, 0x0D, 0x00, 0x00, + 0x4F, 0x0D, 0x00, 0x00, 0x55, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x59, 0x0D, 0x00, 0x00, 0xC2, 0x09, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5A, 0x0D, 0x00, 0x00, 0xC2, 0x09, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5B, 0x0D, 0x00, 0x00, + 0x59, 0x0D, 0x00, 0x00, 0x5A, 0x0D, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x5C, 0x0D, 0x00, 0x00, 0x5B, 0x0D, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x9E, 0x03, 0x00, 0x00, 0x5D, 0x0D, 0x00, 0x00, + 0x56, 0x0D, 0x00, 0x00, 0x5C, 0x0D, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x5F, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x5D, 0x0D, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x9E, 0x03, 0x00, 0x00, 0x62, 0x0D, 0x00, 0x00, 0x5D, 0x0D, 0x00, 0x00, + 0x5F, 0x0D, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x64, 0x0D, 0x00, 0x00, 0x5F, 0x0D, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x69, 0x0D, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, + 0xCE, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xA9, 0x01, 0x00, 0x00, + 0x9F, 0x13, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xA1, 0x13, 0x00, 0x00, 0x9F, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xA2, 0x13, 0x00, 0x00, + 0xA1, 0x13, 0x00, 0x00, 0x69, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x6C, 0x0D, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, 0xDB, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, 0xA8, 0x13, 0x00, 0x00, + 0x9F, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xA9, 0x13, 0x00, 0x00, 0xA8, 0x13, 0x00, 0x00, 0x6C, 0x0D, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xAF, 0x13, 0x00, 0x00, 0x9F, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, + 0xAF, 0x13, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x71, 0x0D, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, 0xD5, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, 0xB6, 0x13, 0x00, 0x00, + 0x9F, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xB7, 0x13, 0x00, 0x00, 0xB6, 0x13, 0x00, 0x00, 0x71, 0x0D, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x77, 0x0D, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, + 0xE5, 0x01, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, + 0xC4, 0x13, 0x00, 0x00, 0x9F, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0xC5, 0x13, 0x00, 0x00, 0xC4, 0x13, 0x00, 0x00, + 0x77, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x7A, 0x0D, 0x00, 0x00, + 0x64, 0x0D, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xCB, 0x13, 0x00, 0x00, 0x9F, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xCC, 0x13, 0x00, 0x00, + 0xCB, 0x13, 0x00, 0x00, 0x7A, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x7D, 0x0D, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, 0xEB, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, 0xD2, 0x13, 0x00, 0x00, + 0x9F, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xD3, 0x13, 0x00, 0x00, 0xD2, 0x13, 0x00, 0x00, 0x7D, 0x0D, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x80, 0x0D, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, + 0xF1, 0x01, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, + 0xD9, 0x13, 0x00, 0x00, 0x9F, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0xDA, 0x13, 0x00, 0x00, 0xD9, 0x13, 0x00, 0x00, + 0x80, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x86, 0x0D, 0x00, 0x00, + 0x64, 0x0D, 0x00, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xE7, 0x13, 0x00, 0x00, 0x9F, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE8, 0x13, 0x00, 0x00, + 0xE7, 0x13, 0x00, 0x00, 0x86, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x89, 0x0D, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, 0xF7, 0x01, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, 0xEE, 0x13, 0x00, 0x00, + 0x9F, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xEF, 0x13, 0x00, 0x00, 0xEE, 0x13, 0x00, 0x00, 0x89, 0x0D, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x8C, 0x0D, 0x00, 0x00, 0x64, 0x0D, 0x00, 0x00, + 0x01, 0x04, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0xA8, 0x01, 0x00, 0x00, + 0xF5, 0x13, 0x00, 0x00, 0x9F, 0x13, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0xB0, 0x01, 0x00, 0x00, 0xF6, 0x13, 0x00, 0x00, 0xF5, 0x13, 0x00, 0x00, + 0x8C, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x8F, 0x0D, 0x00, 0x00, + 0x64, 0x0D, 0x00, 0x00, 0x07, 0x04, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0xA8, 0x01, 0x00, 0x00, 0xFC, 0x13, 0x00, 0x00, 0x9F, 0x13, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xFD, 0x13, 0x00, 0x00, + 0xFC, 0x13, 0x00, 0x00, 0x8F, 0x0D, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xAE, 0x01, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA2, 0x0D, 0x00, 0x00, 0xA2, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA4, 0x0D, 0x00, 0x00, + 0xA2, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA6, 0x0D, 0x00, 0x00, 0xA2, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x13, 0x14, 0x00, 0x00, 0xA2, 0x0D, 0x00, 0x00, 0xA2, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x16, 0x14, 0x00, 0x00, + 0xA4, 0x0D, 0x00, 0x00, 0xA4, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x19, 0x14, 0x00, 0x00, 0xA6, 0x0D, 0x00, 0x00, + 0xA6, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAF, 0x0D, 0x00, 0x00, 0xB7, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB1, 0x0D, 0x00, 0x00, + 0xB7, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB3, 0x0D, 0x00, 0x00, 0xB7, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1D, 0x14, 0x00, 0x00, 0xAF, 0x0D, 0x00, 0x00, 0xAF, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x14, 0x00, 0x00, + 0xB1, 0x0D, 0x00, 0x00, 0xB1, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x23, 0x14, 0x00, 0x00, 0xB3, 0x0D, 0x00, 0x00, + 0xB3, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC9, 0x0D, 0x00, 0x00, 0xA9, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCB, 0x0D, 0x00, 0x00, + 0xA9, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCD, 0x0D, 0x00, 0x00, 0xA9, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x31, 0x14, 0x00, 0x00, 0xC9, 0x0D, 0x00, 0x00, 0xC9, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x34, 0x14, 0x00, 0x00, + 0xCB, 0x0D, 0x00, 0x00, 0xCB, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x37, 0x14, 0x00, 0x00, 0xCD, 0x0D, 0x00, 0x00, + 0xCD, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD6, 0x0D, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD8, 0x0D, 0x00, 0x00, + 0xB0, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDA, 0x0D, 0x00, 0x00, 0xB0, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3B, 0x14, 0x00, 0x00, 0xD6, 0x0D, 0x00, 0x00, 0xD6, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3E, 0x14, 0x00, 0x00, + 0xD8, 0x0D, 0x00, 0x00, 0xD8, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x41, 0x14, 0x00, 0x00, 0xDA, 0x0D, 0x00, 0x00, + 0xDA, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE3, 0x0D, 0x00, 0x00, 0xC5, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE5, 0x0D, 0x00, 0x00, + 0xC5, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE7, 0x0D, 0x00, 0x00, 0xC5, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x45, 0x14, 0x00, 0x00, 0xE3, 0x0D, 0x00, 0x00, 0xE3, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, + 0xE5, 0x0D, 0x00, 0x00, 0xE5, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4B, 0x14, 0x00, 0x00, 0xE7, 0x0D, 0x00, 0x00, + 0xE7, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF0, 0x0D, 0x00, 0x00, 0xCC, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF2, 0x0D, 0x00, 0x00, + 0xCC, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xF4, 0x0D, 0x00, 0x00, 0xCC, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4F, 0x14, 0x00, 0x00, 0xF0, 0x0D, 0x00, 0x00, 0xF0, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x52, 0x14, 0x00, 0x00, + 0xF2, 0x0D, 0x00, 0x00, 0xF2, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x55, 0x14, 0x00, 0x00, 0xF4, 0x0D, 0x00, 0x00, + 0xF4, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFD, 0x0D, 0x00, 0x00, 0xD3, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xFF, 0x0D, 0x00, 0x00, + 0xD3, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, 0xD3, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x59, 0x14, 0x00, 0x00, 0xFD, 0x0D, 0x00, 0x00, 0xFD, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5C, 0x14, 0x00, 0x00, + 0xFF, 0x0D, 0x00, 0x00, 0xFF, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5F, 0x14, 0x00, 0x00, 0x01, 0x0E, 0x00, 0x00, + 0x01, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0A, 0x0E, 0x00, 0x00, 0xDA, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0C, 0x0E, 0x00, 0x00, + 0xDA, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0xDA, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x63, 0x14, 0x00, 0x00, 0x0A, 0x0E, 0x00, 0x00, 0x0A, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x66, 0x14, 0x00, 0x00, + 0x0C, 0x0E, 0x00, 0x00, 0x0C, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x69, 0x14, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x00, + 0x0E, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x17, 0x0E, 0x00, 0x00, 0xEF, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x19, 0x0E, 0x00, 0x00, + 0xEF, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1B, 0x0E, 0x00, 0x00, 0xEF, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6D, 0x14, 0x00, 0x00, 0x17, 0x0E, 0x00, 0x00, 0x17, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, + 0x19, 0x0E, 0x00, 0x00, 0x19, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x73, 0x14, 0x00, 0x00, 0x1B, 0x0E, 0x00, 0x00, + 0x1B, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x24, 0x0E, 0x00, 0x00, 0xF6, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x26, 0x0E, 0x00, 0x00, + 0xF6, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x28, 0x0E, 0x00, 0x00, 0xF6, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x77, 0x14, 0x00, 0x00, 0x24, 0x0E, 0x00, 0x00, 0x24, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7A, 0x14, 0x00, 0x00, + 0x26, 0x0E, 0x00, 0x00, 0x26, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7D, 0x14, 0x00, 0x00, 0x28, 0x0E, 0x00, 0x00, + 0x28, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3E, 0x0E, 0x00, 0x00, 0xE8, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x40, 0x0E, 0x00, 0x00, + 0xE8, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x42, 0x0E, 0x00, 0x00, 0xE8, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8B, 0x14, 0x00, 0x00, 0x3E, 0x0E, 0x00, 0x00, 0x3E, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8E, 0x14, 0x00, 0x00, + 0x40, 0x0E, 0x00, 0x00, 0x40, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x91, 0x14, 0x00, 0x00, 0x42, 0x0E, 0x00, 0x00, + 0x42, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4B, 0x0E, 0x00, 0x00, 0xFD, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4D, 0x0E, 0x00, 0x00, + 0xFD, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4F, 0x0E, 0x00, 0x00, 0xFD, 0x13, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x95, 0x14, 0x00, 0x00, 0x4B, 0x0E, 0x00, 0x00, 0x4B, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x98, 0x14, 0x00, 0x00, + 0x4D, 0x0E, 0x00, 0x00, 0x4D, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x9B, 0x14, 0x00, 0x00, 0x4F, 0x0E, 0x00, 0x00, + 0x4F, 0x0E, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB9, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x34, 0x14, 0x00, 0x00, 0x3E, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBA, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x16, 0x14, 0x00, 0x00, 0xB9, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC0, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, + 0x66, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC1, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xBA, 0x14, 0x00, 0x00, 0xC0, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE3, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x34, 0x14, 0x00, 0x00, 0x3E, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE4, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x16, 0x14, 0x00, 0x00, + 0xE3, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xEA, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x48, 0x14, 0x00, 0x00, 0x66, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xEB, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0xE4, 0x14, 0x00, 0x00, 0xEA, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3E, 0x14, 0x00, 0x00, + 0x48, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0E, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x20, 0x14, 0x00, 0x00, 0x0D, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x52, 0x14, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x15, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x0E, 0x15, 0x00, 0x00, + 0x14, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x37, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x3E, 0x14, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x38, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x20, 0x14, 0x00, 0x00, 0x37, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3E, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x52, 0x14, 0x00, 0x00, + 0x70, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3F, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x38, 0x15, 0x00, 0x00, 0x3E, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x61, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x5C, 0x14, 0x00, 0x00, 0x66, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3E, 0x14, 0x00, 0x00, + 0x61, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x68, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x70, 0x14, 0x00, 0x00, 0x8E, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x69, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x62, 0x15, 0x00, 0x00, 0x68, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x8B, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x5C, 0x14, 0x00, 0x00, + 0x66, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8C, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x3E, 0x14, 0x00, 0x00, 0x8B, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x92, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0x8E, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x93, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x8C, 0x15, 0x00, 0x00, + 0x92, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB5, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x66, 0x14, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB6, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, 0xB5, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xBC, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x7A, 0x14, 0x00, 0x00, + 0x98, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBD, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xB6, 0x15, 0x00, 0x00, 0xBC, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xDF, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x66, 0x14, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xE0, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, + 0xDF, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xE6, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x7A, 0x14, 0x00, 0x00, 0x98, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xE7, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0xE0, 0x15, 0x00, 0x00, 0xE6, 0x15, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x16, 0x00, 0x00, + 0xEB, 0x14, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x16, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, 0x06, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x16, 0x00, 0x00, + 0x07, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x27, 0x16, 0x00, 0x00, 0x3F, 0x15, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x28, 0x16, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, + 0x27, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x29, 0x16, 0x00, 0x00, 0x28, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x48, 0x16, 0x00, 0x00, 0x93, 0x15, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x16, 0x00, 0x00, + 0x8B, 0x01, 0x00, 0x00, 0x48, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4A, 0x16, 0x00, 0x00, 0x49, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x16, 0x00, 0x00, + 0xE7, 0x15, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6A, 0x16, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, 0x69, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6B, 0x16, 0x00, 0x00, + 0x6A, 0x16, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA7, 0x0F, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0xEB, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA8, 0x0F, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xC1, 0x14, 0x00, 0x00, + 0xA7, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAA, 0x0F, 0x00, 0x00, 0xA8, 0x0F, 0x00, 0x00, 0x08, 0x16, 0x00, 0x00, + 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x95, 0x16, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xAA, 0x0F, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBF, 0x0F, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x3F, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC0, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x15, 0x15, 0x00, 0x00, 0xBF, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC2, 0x0F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, + 0x29, 0x16, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC8, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xC2, 0x0F, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD7, 0x0F, 0x00, 0x00, + 0x79, 0x01, 0x00, 0x00, 0x93, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD8, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x69, 0x15, 0x00, 0x00, 0xD7, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xDA, 0x0F, 0x00, 0x00, + 0xD8, 0x0F, 0x00, 0x00, 0x4A, 0x16, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xFB, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0xDA, 0x0F, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, + 0x79, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xEF, 0x0F, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0xE7, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xBD, 0x15, 0x00, 0x00, + 0xEF, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xF2, 0x0F, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0x6B, 0x16, 0x00, 0x00, + 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2E, 0x17, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xF2, 0x0F, 0x00, 0x00, + 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5C, 0x17, 0x00, 0x00, 0x95, 0x16, 0x00, 0x00, + 0xC2, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5E, 0x17, 0x00, 0x00, + 0x5C, 0x17, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x60, 0x17, 0x00, 0x00, 0x5E, 0x17, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x61, 0x17, 0x00, 0x00, 0x60, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8F, 0x17, 0x00, 0x00, 0xC8, 0x16, 0x00, 0x00, + 0xC2, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x91, 0x17, 0x00, 0x00, + 0x8F, 0x17, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x93, 0x17, 0x00, 0x00, 0x91, 0x17, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x94, 0x17, 0x00, 0x00, 0x93, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC2, 0x17, 0x00, 0x00, 0xFB, 0x16, 0x00, 0x00, + 0xC2, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC4, 0x17, 0x00, 0x00, + 0xC2, 0x17, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC6, 0x17, 0x00, 0x00, 0xC4, 0x17, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC7, 0x17, 0x00, 0x00, 0xC6, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF5, 0x17, 0x00, 0x00, 0x2E, 0x17, 0x00, 0x00, + 0xC2, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF7, 0x17, 0x00, 0x00, + 0xF5, 0x17, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF9, 0x17, 0x00, 0x00, 0xF7, 0x17, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xFA, 0x17, 0x00, 0x00, 0xF9, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0xC7, 0x09, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1C, 0x10, 0x00, 0x00, + 0x61, 0x17, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x25, 0x10, 0x00, 0x00, 0x94, 0x17, 0x00, 0x00, + 0x16, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2E, 0x10, 0x00, 0x00, 0xC7, 0x17, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, + 0xFA, 0x17, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3D, 0x10, 0x00, 0x00, 0x62, 0x0D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3E, 0x10, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x3D, 0x10, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x41, 0x10, 0x00, 0x00, + 0x62, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x42, 0x10, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x41, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x43, 0x10, 0x00, 0x00, 0x3E, 0x10, 0x00, 0x00, 0x42, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4A, 0x10, 0x00, 0x00, + 0x3D, 0x10, 0x00, 0x00, 0x42, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x51, 0x10, 0x00, 0x00, 0x3E, 0x10, 0x00, 0x00, + 0x41, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x56, 0x10, 0x00, 0x00, 0x3D, 0x10, 0x00, 0x00, 0x41, 0x10, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5A, 0x10, 0x00, 0x00, + 0xEB, 0x14, 0x00, 0x00, 0xC1, 0x14, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5B, 0x10, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x00, + 0x5A, 0x10, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x23, 0x18, 0x00, 0x00, 0x5B, 0x10, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x24, 0x18, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, + 0x23, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x25, 0x18, 0x00, 0x00, 0x24, 0x18, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5E, 0x10, 0x00, 0x00, 0x43, 0x10, 0x00, 0x00, + 0x25, 0x18, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x62, 0x10, 0x00, 0x00, 0x3F, 0x15, 0x00, 0x00, 0x15, 0x15, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x10, 0x00, 0x00, + 0x0A, 0x08, 0x00, 0x00, 0x62, 0x10, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2E, 0x18, 0x00, 0x00, 0x63, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x18, 0x00, 0x00, + 0x8B, 0x01, 0x00, 0x00, 0x2E, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x2F, 0x18, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x66, 0x10, 0x00, 0x00, + 0x4A, 0x10, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6A, 0x10, 0x00, 0x00, 0x93, 0x15, 0x00, 0x00, + 0x69, 0x15, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6B, 0x10, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x00, 0x6A, 0x10, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x18, 0x00, 0x00, + 0x6B, 0x10, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3A, 0x18, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, 0x39, 0x18, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3B, 0x18, 0x00, 0x00, + 0x3A, 0x18, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6E, 0x10, 0x00, 0x00, 0x51, 0x10, 0x00, 0x00, 0x3B, 0x18, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x72, 0x10, 0x00, 0x00, + 0xE7, 0x15, 0x00, 0x00, 0xBD, 0x15, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x73, 0x10, 0x00, 0x00, 0x0A, 0x08, 0x00, 0x00, + 0x72, 0x10, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x44, 0x18, 0x00, 0x00, 0x73, 0x10, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x45, 0x18, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, + 0x44, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x46, 0x18, 0x00, 0x00, 0x45, 0x18, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x76, 0x10, 0x00, 0x00, 0x56, 0x10, 0x00, 0x00, + 0x46, 0x18, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7C, 0x10, 0x00, 0x00, 0x1C, 0x10, 0x00, 0x00, 0x5E, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x85, 0x10, 0x00, 0x00, + 0x25, 0x10, 0x00, 0x00, 0x66, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, 0x2E, 0x10, 0x00, 0x00, + 0x6E, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x98, 0x10, 0x00, 0x00, 0x85, 0x10, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9A, 0x10, 0x00, 0x00, + 0x98, 0x10, 0x00, 0x00, 0x5E, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB2, 0x10, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, + 0x76, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB3, 0x10, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x00, 0xB2, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB5, 0x10, 0x00, 0x00, + 0xB3, 0x10, 0x00, 0x00, 0x66, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x00, 0xB3, 0x10, 0x00, 0x00, + 0x6E, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xEB, 0x10, 0x00, 0x00, 0x98, 0x10, 0x00, 0x00, 0x76, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5C, 0x1A, 0x00, 0x00, + 0x7C, 0x10, 0x00, 0x00, 0x85, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5D, 0x1A, 0x00, 0x00, 0x5C, 0x1A, 0x00, 0x00, + 0x97, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5E, 0x1A, 0x00, 0x00, 0x5D, 0x1A, 0x00, 0x00, 0xB2, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x15, 0x11, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x5E, 0x1A, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0x15, 0x11, 0x00, 0x00, + 0x9A, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x19, 0x11, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0xB5, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1B, 0x11, 0x00, 0x00, + 0x19, 0x11, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1D, 0x11, 0x00, 0x00, 0x1B, 0x11, 0x00, 0x00, + 0xEB, 0x10, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5D, 0x18, 0x00, 0x00, 0x1D, 0x11, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5E, 0x18, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, + 0x5D, 0x18, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x5F, 0x18, 0x00, 0x00, 0x5E, 0x18, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x62, 0x18, 0x00, 0x00, 0x5F, 0x18, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x64, 0x18, 0x00, 0x00, + 0x62, 0x18, 0x00, 0x00, 0x1D, 0x11, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x66, 0x18, 0x00, 0x00, 0x64, 0x18, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x67, 0x18, 0x00, 0x00, 0x5F, 0x18, 0x00, 0x00, 0x66, 0x18, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5F, 0x1A, 0x00, 0x00, + 0x13, 0x14, 0x00, 0x00, 0x31, 0x14, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x27, 0x11, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x00, + 0x5F, 0x1A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2B, 0x11, 0x00, 0x00, 0x1D, 0x14, 0x00, 0x00, 0x85, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2C, 0x11, 0x00, 0x00, + 0x27, 0x11, 0x00, 0x00, 0x2B, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x30, 0x11, 0x00, 0x00, 0x4F, 0x14, 0x00, 0x00, + 0x85, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x31, 0x11, 0x00, 0x00, 0x2C, 0x11, 0x00, 0x00, 0x30, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x35, 0x11, 0x00, 0x00, + 0x59, 0x14, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x36, 0x11, 0x00, 0x00, 0x31, 0x11, 0x00, 0x00, + 0x35, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3A, 0x11, 0x00, 0x00, 0x8B, 0x14, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3B, 0x11, 0x00, 0x00, + 0x36, 0x11, 0x00, 0x00, 0x3A, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3F, 0x11, 0x00, 0x00, 0x77, 0x14, 0x00, 0x00, + 0xB2, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x40, 0x11, 0x00, 0x00, 0x3B, 0x11, 0x00, 0x00, 0x3F, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x44, 0x11, 0x00, 0x00, + 0x95, 0x14, 0x00, 0x00, 0xB2, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x45, 0x11, 0x00, 0x00, 0x40, 0x11, 0x00, 0x00, + 0x44, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x49, 0x11, 0x00, 0x00, 0x3B, 0x14, 0x00, 0x00, 0x9A, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4A, 0x11, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x49, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x4E, 0x11, 0x00, 0x00, 0x45, 0x14, 0x00, 0x00, + 0xB5, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x4F, 0x11, 0x00, 0x00, 0x4A, 0x11, 0x00, 0x00, 0x4E, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x53, 0x11, 0x00, 0x00, + 0x63, 0x14, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x54, 0x11, 0x00, 0x00, 0x4F, 0x11, 0x00, 0x00, + 0x53, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x58, 0x11, 0x00, 0x00, 0x6D, 0x14, 0x00, 0x00, 0xEB, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x59, 0x11, 0x00, 0x00, + 0x54, 0x11, 0x00, 0x00, 0x58, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x5B, 0x11, 0x00, 0x00, 0x59, 0x11, 0x00, 0x00, + 0x67, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x75, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x5B, 0x11, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x60, 0x1A, 0x00, 0x00, + 0x16, 0x14, 0x00, 0x00, 0x34, 0x14, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x65, 0x11, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x00, + 0x60, 0x1A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x69, 0x11, 0x00, 0x00, 0x20, 0x14, 0x00, 0x00, 0x85, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6A, 0x11, 0x00, 0x00, + 0x65, 0x11, 0x00, 0x00, 0x69, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x6E, 0x11, 0x00, 0x00, 0x52, 0x14, 0x00, 0x00, + 0x85, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x6F, 0x11, 0x00, 0x00, 0x6A, 0x11, 0x00, 0x00, 0x6E, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x73, 0x11, 0x00, 0x00, + 0x5C, 0x14, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x74, 0x11, 0x00, 0x00, 0x6F, 0x11, 0x00, 0x00, + 0x73, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x78, 0x11, 0x00, 0x00, 0x8E, 0x14, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x79, 0x11, 0x00, 0x00, + 0x74, 0x11, 0x00, 0x00, 0x78, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x7D, 0x11, 0x00, 0x00, 0x7A, 0x14, 0x00, 0x00, + 0xB2, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x7E, 0x11, 0x00, 0x00, 0x79, 0x11, 0x00, 0x00, 0x7D, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x82, 0x11, 0x00, 0x00, + 0x98, 0x14, 0x00, 0x00, 0xB2, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x83, 0x11, 0x00, 0x00, 0x7E, 0x11, 0x00, 0x00, + 0x82, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x87, 0x11, 0x00, 0x00, 0x3E, 0x14, 0x00, 0x00, 0x9A, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, + 0x83, 0x11, 0x00, 0x00, 0x87, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x8C, 0x11, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, + 0xB5, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x8D, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x8C, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x91, 0x11, 0x00, 0x00, + 0x66, 0x14, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x92, 0x11, 0x00, 0x00, 0x8D, 0x11, 0x00, 0x00, + 0x91, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x96, 0x11, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0xEB, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x97, 0x11, 0x00, 0x00, + 0x92, 0x11, 0x00, 0x00, 0x96, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x99, 0x11, 0x00, 0x00, 0x97, 0x11, 0x00, 0x00, + 0x67, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x83, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x99, 0x11, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x61, 0x1A, 0x00, 0x00, + 0x19, 0x14, 0x00, 0x00, 0x37, 0x14, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA3, 0x11, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x00, + 0x61, 0x1A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA7, 0x11, 0x00, 0x00, 0x23, 0x14, 0x00, 0x00, 0x85, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA8, 0x11, 0x00, 0x00, + 0xA3, 0x11, 0x00, 0x00, 0xA7, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xAC, 0x11, 0x00, 0x00, 0x55, 0x14, 0x00, 0x00, + 0x85, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xAD, 0x11, 0x00, 0x00, 0xA8, 0x11, 0x00, 0x00, 0xAC, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB1, 0x11, 0x00, 0x00, + 0x5F, 0x14, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xB2, 0x11, 0x00, 0x00, 0xAD, 0x11, 0x00, 0x00, + 0xB1, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xB6, 0x11, 0x00, 0x00, 0x91, 0x14, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xB7, 0x11, 0x00, 0x00, + 0xB2, 0x11, 0x00, 0x00, 0xB6, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBB, 0x11, 0x00, 0x00, 0x7D, 0x14, 0x00, 0x00, + 0xB2, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xBC, 0x11, 0x00, 0x00, 0xB7, 0x11, 0x00, 0x00, 0xBB, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x00, + 0x9B, 0x14, 0x00, 0x00, 0xB2, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xC1, 0x11, 0x00, 0x00, 0xBC, 0x11, 0x00, 0x00, + 0xC0, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xC5, 0x11, 0x00, 0x00, 0x41, 0x14, 0x00, 0x00, 0x9A, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xC6, 0x11, 0x00, 0x00, + 0xC1, 0x11, 0x00, 0x00, 0xC5, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xCA, 0x11, 0x00, 0x00, 0x4B, 0x14, 0x00, 0x00, + 0xB5, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xCB, 0x11, 0x00, 0x00, 0xC6, 0x11, 0x00, 0x00, 0xCA, 0x11, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xCF, 0x11, 0x00, 0x00, + 0x69, 0x14, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD0, 0x11, 0x00, 0x00, 0xCB, 0x11, 0x00, 0x00, + 0xCF, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xD4, 0x11, 0x00, 0x00, 0x73, 0x14, 0x00, 0x00, 0xEB, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0xD5, 0x11, 0x00, 0x00, + 0xD0, 0x11, 0x00, 0x00, 0xD4, 0x11, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xD7, 0x11, 0x00, 0x00, 0xD5, 0x11, 0x00, 0x00, + 0x67, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x91, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xD7, 0x11, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0xDE, 0x09, 0x00, 0x00, 0xDF, 0x09, 0x00, 0x00, + 0xD0, 0x09, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0xDF, 0x09, 0x00, 0x00, 0x75, 0x18, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xDE, 0x09, 0x00, 0x00, 0xE1, 0x09, 0x00, 0x00, 0xD0, 0x09, 0x00, 0x00, + 0x52, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE1, 0x09, 0x00, 0x00, + 0x83, 0x18, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xDE, 0x09, 0x00, 0x00, + 0xE3, 0x09, 0x00, 0x00, 0xD0, 0x09, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0xE3, 0x09, 0x00, 0x00, 0x91, 0x18, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE4, 0x09, 0x00, 0x00, + 0xD0, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, + 0xE5, 0x09, 0x00, 0x00, 0xE4, 0x09, 0x00, 0x00, 0xE4, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x32, 0x00, 0x00, 0x00, 0xE6, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xE5, 0x09, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xE7, 0x09, 0x00, 0x00, + 0xD0, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xE8, 0x09, 0x00, 0x00, 0xE7, 0x09, 0x00, 0x00, 0xE6, 0x09, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD0, 0x09, 0x00, 0x00, + 0xE8, 0x09, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xE9, 0x09, 0x00, 0x00, 0xD0, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, + 0x32, 0x00, 0x00, 0x00, 0xEA, 0x09, 0x00, 0x00, 0xE9, 0x09, 0x00, 0x00, + 0xE9, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x9D, 0x18, 0x00, 0x00, 0xBF, 0x09, 0x00, 0x00, 0x6B, 0x1A, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9F, 0x18, 0x00, 0x00, + 0x9D, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA0, 0x18, 0x00, 0x00, 0x9F, 0x18, 0x00, 0x00, + 0x55, 0x01, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA2, 0x18, 0x00, 0x00, 0x9D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA3, 0x18, 0x00, 0x00, + 0xA0, 0x18, 0x00, 0x00, 0xA2, 0x18, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x98, 0x18, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0xA4, 0x18, 0x00, 0x00, 0x98, 0x18, 0x00, 0x00, + 0xA3, 0x18, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xA5, 0x18, 0x00, 0x00, 0xA4, 0x18, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x32, 0x00, 0x00, 0x00, 0xEE, 0x09, 0x00, 0x00, 0xA5, 0x18, 0x00, 0x00, + 0xA5, 0x18, 0x00, 0x00, 0xA5, 0x18, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x32, 0x00, 0x00, 0x00, 0xEF, 0x09, 0x00, 0x00, 0xEA, 0x09, 0x00, 0x00, + 0xEE, 0x09, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, + 0xF2, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xEF, 0x09, 0x00, 0x00, 0xF0, 0x09, 0x00, 0x00, 0xF1, 0x09, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xF3, 0x09, 0x00, 0x00, + 0xD0, 0x09, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0xB0, 0x01, 0x00, 0x00, + 0xF4, 0x09, 0x00, 0x00, 0xF3, 0x09, 0x00, 0x00, 0xF2, 0x09, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD0, 0x09, 0x00, 0x00, + 0xF4, 0x09, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xDE, 0x09, 0x00, 0x00, + 0xF6, 0x09, 0x00, 0x00, 0xD0, 0x09, 0x00, 0x00, 0xF5, 0x09, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0xF6, 0x09, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..02044065f36008117d1f3fea04602b37e783d5a8 GIT binary patch literal 14492 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 literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.txt new file mode 100644 index 000000000..487844679 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.txt @@ -0,0 +1,726 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 6764 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %xe_frag_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 420 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %xe_texture "xe_texture" + OpName %gl_FragCoord "gl_FragCoord" + OpName %XeCasSharpenConstants "XeCasSharpenConstants" + OpMemberName %XeCasSharpenConstants 0 "xe_cas_output_offset" + OpMemberName %XeCasSharpenConstants 1 "xe_cas_sharpness_post_setup" + OpName %_ "" + OpName %xe_frag_color "xe_frag_color" + OpDecorate %xe_texture DescriptorSet 0 + OpDecorate %xe_texture Binding 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpMemberDecorate %XeCasSharpenConstants 0 Offset 16 + OpMemberDecorate %XeCasSharpenConstants 1 Offset 24 + OpDecorate %XeCasSharpenConstants Block + OpDecorate %xe_frag_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %v3float = OpTypeVector %float 3 + %v4uint = OpTypeVector %uint 4 + %uint_15 = OpConstant %uint 15 + %uint_256 = OpConstant %uint 256 +%_arr_float_uint_256 = OpTypeArray %float %uint_256 +%float_n0_00100337015 = OpConstant %float -0.00100337015 +%float_0_000880821084 = OpConstant %float 0.000880821084 +%float_0_00169270835 = OpConstant %float 0.00169270835 +%float_n0_00155484071 = OpConstant %float -0.00155484071 +%float_0_00127910543 = OpConstant %float 0.00127910543 +%float_n0_000605085806 = OpConstant %float -0.000605085806 +%float_0_00106464466 = OpConstant %float 0.00106464466 +%float_n0_00138633582 = OpConstant %float -0.00138633582 +%float_0_00135569857 = OpConstant %float 0.00135569857 +%float_0_000513174047 = OpConstant %float 0.000513174047 +%float_0_00121783093 = OpConstant %float 0.00121783093 +%float_n0_00160079659 = OpConstant %float -0.00160079659 +%float_0_00058976718 = OpConstant %float 0.00058976718 +%float_n0_00028339462 = OpConstant %float -0.00028339462 +%float_0_00111060054 = OpConstant %float 0.00111060054 +%float_n0_00141697307 = OpConstant %float -0.00141697307 +%float_0_00144761032 = OpConstant %float 0.00144761032 +%float_n0_0005438113 = OpConstant %float -0.0005438113 +%float_0_00013020834 = OpConstant %float 0.00013020834 +%float_n0_0012025123 = OpConstant %float -0.0012025123 +%float_0_000436580885 = OpConstant %float 0.000436580885 +%float_n0_00104932603 = OpConstant %float -0.00104932603 +%float_0_000620404433 = OpConstant %float 0.000620404433 +%float_n0_000482536765 = OpConstant %float -0.000482536765 +%float_0_00187653187 = OpConstant %float 0.00187653187 +%float_n0_00109528191 = OpConstant %float -0.00109528191 +%float_n9_95710798en05 = OpConstant %float -9.95710798e-05 +%float_n0_000528492674 = OpConstant %float -0.000528492674 +%float_0_0014322917 = OpConstant %float 0.0014322917 +%float_n0_00193780637 = OpConstant %float -0.00193780637 +%float_n0_000696997566 = OpConstant %float -0.000696997566 +%float_3_829657en05 = OpConstant %float 3.829657e-05 +%float_0_000712316192 = OpConstant %float 0.000712316192 +%float_n0_00130974269 = OpConstant %float -0.00130974269 +%float_0_00109528191 = OpConstant %float 0.00109528191 +%float_n0_000298713247 = OpConstant %float -0.000298713247 +%float_0_00175398286 = OpConstant %float 0.00175398286 +%float_n0_00167738972 = OpConstant %float -0.00167738972 +%float_0_00147824758 = OpConstant %float 0.00147824758 +%float_n3_829657en05 = OpConstant %float -3.829657e-05 +%float_0_000788909325 = OpConstant %float 0.000788909325 +%float_n0_00183057599 = OpConstant %float -0.00183057599 +%float_0_000298713247 = OpConstant %float 0.000298713247 +%float_0_000988051528 = OpConstant %float 0.000988051528 +%float_n0_00117187505 = OpConstant %float -0.00117187505 +%float_0_00017616422 = OpConstant %float 0.00017616422 +%float_0_00164675247 = OpConstant %float 0.00164675247 +%float_n0_00158547796 = OpConstant %float -0.00158547796 +%float_0_000344669126 = OpConstant %float 0.000344669126 +%float_0_00186121324 = OpConstant %float 0.00186121324 +%float_n0_00176930148 = OpConstant %float -0.00176930148 +%float_n0_000865502458 = OpConstant %float -0.000865502458 +%float_0_000896139711 = OpConstant %float 0.000896139711 +%float_0_000160845593 = OpConstant %float 0.000160845593 +%float_n0_000926776964 = OpConstant %float -0.000926776964 +%float_n0_00152420346 = OpConstant %float -0.00152420346 +%float_n0_000651041686 = OpConstant %float -0.000651041686 +%float_0_00129442406 = OpConstant %float 0.00129442406 +%float_n0_000804227951 = OpConstant %float -0.000804227951 +%float_n0_00146292895 = OpConstant %float -0.00146292895 +%float_0_00179993873 = OpConstant %float 0.00179993873 +%float_n0_000850183831 = OpConstant %float -0.000850183831 +%float_0_000850183831 = OpConstant %float 0.000850183831 +%float_n0_000451899512 = OpConstant %float -0.000451899512 +%float_n0_00106464466 = OpConstant %float -0.00106464466 +%float_n0_000145526967 = OpConstant %float -0.000145526967 +%float_0_000237438726 = OpConstant %float 0.000237438726 +%float_0_00141697307 = OpConstant %float 0.00141697307 +%float_n0_00058976718 = OpConstant %float -0.00058976718 +%float_n0_000191482846 = OpConstant %float -0.000191482846 +%float_0_00160079659 = OpConstant %float 0.00160079659 +%float_0_00101868878 = OpConstant %float 0.00101868878 +%float_0_000405943632 = OpConstant %float 0.000405943632 +%float_n0_000206801473 = OpConstant %float -0.000206801473 +%float_0_00158547796 = OpConstant %float 0.00158547796 +%float_0_000651041686 = OpConstant %float 0.000651041686 +%float_n6_89338267en05 = OpConstant %float -6.89338267e-05 +%float_0_000421262259 = OpConstant %float 0.000421262259 +%float_n0_00164675247 = OpConstant %float -0.00164675247 +%float_0_00137101719 = OpConstant %float 0.00137101719 +%float_0_000926776964 = OpConstant %float 0.000926776964 +%float_n0_000666360313 = OpConstant %float -0.000666360313 +%float_0_00118719367 = OpConstant %float 0.00118719367 +%float_n0_00144761032 = OpConstant %float -0.00144761032 +%float_0_000574448553 = OpConstant %float 0.000574448553 +%float_n0_00189185049 = OpConstant %float -0.00189185049 +%float_0_000758272072 = OpConstant %float 0.000758272072 +%float_n0_00129442406 = OpConstant %float -0.00129442406 +%float_0_00192248775 = OpConstant %float 0.00192248775 +%float_n0_0016620711 = OpConstant %float -0.0016620711 +%float_n0_00103400741 = OpConstant %float -0.00103400741 +%float_n0_000497855421 = OpConstant %float -0.000497855421 +%float_n0_00186121324 = OpConstant %float -0.00186121324 +%float_0_0012025123 = OpConstant %float 0.0012025123 +%float_n0_0003293505 = OpConstant %float -0.0003293505 +%float_n0_00137101719 = OpConstant %float -0.00137101719 +%float_0_00163143384 = OpConstant %float 0.00163143384 +%float_n0_00184589461 = OpConstant %float -0.00184589461 +%float_0_000727634819 = OpConstant %float 0.000727634819 +%float_n0_000911458337 = OpConstant %float -0.000911458337 +%float_0_00181525736 = OpConstant %float 0.00181525736 +%float_n0_00114123779 = OpConstant %float -0.00114123779 +%float_n0_000375306379 = OpConstant %float -0.000375306379 +%float_9_95710798en05 = OpConstant %float 9.95710798e-05 +%float_n0_000742953445 = OpConstant %float -0.000742953445 +%float_0_00117187505 = OpConstant %float 0.00117187505 +%float_6_89338267en05 = OpConstant %float 6.89338267e-05 +%float_0_0014935662 = OpConstant %float 0.0014935662 +%float_0_000972732843 = OpConstant %float 0.000972732843 +%float_n0_000957414217 = OpConstant %float -0.000957414217 +%float_0_00193780637 = OpConstant %float 0.00193780637 +%float_0_000528492674 = OpConstant %float 0.000528492674 +%float_5_36151965en05 = OpConstant %float 5.36151965e-05 +%float_n0_00124846818 = OpConstant %float -0.00124846818 +%float_n0_000268075994 = OpConstant %float -0.000268075994 +%float_0_00153952208 = OpConstant %float 0.00153952208 +%float_n7_65931418en06 = OpConstant %float -7.65931418e-06 +%float_0_000314031873 = OpConstant %float 0.000314031873 +%float_0_00134037994 = OpConstant %float 0.00134037994 +%float_n0_00175398286 = OpConstant %float -0.00175398286 +%float_0_000497855421 = OpConstant %float 0.000497855421 +%float_n0_00118719367 = OpConstant %float -0.00118719367 +%float_0_000773590698 = OpConstant %float 0.000773590698 +%float_n0_00134037994 = OpConstant %float -0.00134037994 +%float_0_000268075994 = OpConstant %float 0.000268075994 +%float_n0_00147824758 = OpConstant %float -0.00147824758 +%float_n0_00013020834 = OpConstant %float -0.00013020834 +%float_n0_000773590698 = OpConstant %float -0.000773590698 +%float_0_00130974269 = OpConstant %float 0.00130974269 +%float_0_000390625006 = OpConstant %float 0.000390625006 +%float_0_000957414217 = OpConstant %float 0.000957414217 +%float_n0_000467218139 = OpConstant %float -0.000467218139 +%float_n0_00153952208 = OpConstant %float -0.00153952208 +%float_0_00103400741 = OpConstant %float 0.00103400741 +%float_n0_000681678939 = OpConstant %float -0.000681678939 +%float_0_00167738972 = OpConstant %float 0.00167738972 +%float_0_00100337015 = OpConstant %float 0.00100337015 +%float_n0_000421262259 = OpConstant %float -0.000421262259 +%float_0_00178462011 = OpConstant %float 0.00178462011 +%float_n0_000237438726 = OpConstant %float -0.000237438726 +%float_n0_000620404433 = OpConstant %float -0.000620404433 +%float_0_0016620711 = OpConstant %float 0.0016620711 +%float_0_000834865205 = OpConstant %float 0.000834865205 +%float_n0_0017233456 = OpConstant %float -0.0017233456 +%float_n0_00107996329 = OpConstant %float -0.00107996329 +%float_0_00176930148 = OpConstant %float 0.00176930148 +%float_n0_000788909325 = OpConstant %float -0.000788909325 +%float_n0_00178462011 = OpConstant %float -0.00178462011 +%float_0_000681678939 = OpConstant %float 0.000681678939 +%float_n0_000988051528 = OpConstant %float -0.000988051528 +%float_n0_00132506131 = OpConstant %float -0.00132506131 +%float_n0_00017616422 = OpConstant %float -0.00017616422 +%float_n0_00150888483 = OpConstant %float -0.00150888483 +%float_0_0003293505 = OpConstant %float 0.0003293505 +%float_n0_001953125 = OpConstant %float -0.001953125 +%float_0_000666360313 = OpConstant %float 0.000666360313 +%float_n0_00161611522 = OpConstant %float -0.00161611522 +%float_0_00115655642 = OpConstant %float 0.00115655642 +%float_0_000451899512 = OpConstant %float 0.000451899512 +%float_n0_000436580885 = OpConstant %float -0.000436580885 +%float_0_000191482846 = OpConstant %float 0.000191482846 +%float_n0_0014935662 = OpConstant %float -0.0014935662 +%float_0_00114123779 = OpConstant %float 0.00114123779 +%float_8_42524532en05 = OpConstant %float 8.42524532e-05 +%float_0_00189185049 = OpConstant %float 0.00189185049 +%float_0_00140165444 = OpConstant %float 0.00140165444 +%float_0_000559129927 = OpConstant %float 0.000559129927 +%float_0_000114889706 = OpConstant %float 0.000114889706 +%float_0_00126378681 = OpConstant %float 0.00126378681 +%float_n0_000574448553 = OpConstant %float -0.000574448553 +%float_n0_000972732843 = OpConstant %float -0.000972732843 +%float_0_00132506131 = OpConstant %float 0.00132506131 +%float_0_000222120099 = OpConstant %float 0.000222120099 +%float_n0_000758272072 = OpConstant %float -0.000758272072 +%float_n0_00135569857 = OpConstant %float -0.00135569857 +%float_0_00146292895 = OpConstant %float 0.00146292895 +%float_0_000865502458 = OpConstant %float 0.000865502458 +%float_n0_000359987753 = OpConstant %float -0.000359987753 +%float_0_0005438113 = OpConstant %float 0.0005438113 +%float_n0_00112591917 = OpConstant %float -0.00112591917 +%float_n0_000252757367 = OpConstant %float -0.000252757367 +%float_n0_000559129927 = OpConstant %float -0.000559129927 +%float_n0_00181525736 = OpConstant %float -0.00181525736 +%float_0_0017233456 = OpConstant %float 0.0017233456 +%float_n0_00115655642 = OpConstant %float -0.00115655642 +%float_0_000742953445 = OpConstant %float 0.000742953445 +%float_0_00157015934 = OpConstant %float 0.00157015934 +%float_n0_000114889706 = OpConstant %float -0.000114889706 +%float_n0_00121783093 = OpConstant %float -0.00121783093 +%float_0_00183057599 = OpConstant %float 0.00183057599 +%float_2_29779416en05 = OpConstant %float 2.29779416e-05 +%float_n0_00192248775 = OpConstant %float -0.00192248775 +%float_0_00173866423 = OpConstant %float 0.00173866423 +%float_n0_000712316192 = OpConstant %float -0.000712316192 +%float_0_00155484071 = OpConstant %float 0.00155484071 +%float_n0_00170802698 = OpConstant %float -0.00170802698 +%float_0_00123314955 = OpConstant %float 0.00123314955 +%float_0_000206801473 = OpConstant %float 0.000206801473 +%float_0_00104932603 = OpConstant %float 0.00104932603 +%float_n0_000727634819 = OpConstant %float -0.000727634819 +%float_n0_00163143384 = OpConstant %float -0.00163143384 +%float_n0_000314031873 = OpConstant %float -0.000314031873 +%float_0_000482536765 = OpConstant %float 0.000482536765 +%float_n0_00179993873 = OpConstant %float -0.00179993873 +%float_0_00094209559 = OpConstant %float 0.00094209559 +%float_n0_000344669126 = OpConstant %float -0.000344669126 +%float_0_000696997566 = OpConstant %float 0.000696997566 +%float_n0_00101868878 = OpConstant %float -0.00101868878 +%float_n0_00157015934 = OpConstant %float -0.00157015934 +%float_n2_29779416en05 = OpConstant %float -2.29779416e-05 +%float_n0_00127910543 = OpConstant %float -0.00127910543 +%float_0_000804227951 = OpConstant %float 0.000804227951 +%float_n0_000896139711 = OpConstant %float -0.000896139711 +%float_n0_0014322917 = OpConstant %float -0.0014322917 +%float_0_000605085806 = OpConstant %float 0.000605085806 +%float_n8_42524532en05 = OpConstant %float -8.42524532e-05 +%float_0_000911458337 = OpConstant %float 0.000911458337 +%float_0_001953125 = OpConstant %float 0.001953125 +%float_n0_00140165444 = OpConstant %float -0.00140165444 +%float_n0_00063572306 = OpConstant %float -0.00063572306 +%float_0_00150888483 = OpConstant %float 0.00150888483 +%float_n0_000819546578 = OpConstant %float -0.000819546578 +%float_0_00124846818 = OpConstant %float 0.00124846818 +%float_0_000252757367 = OpConstant %float 0.000252757367 +%float_0_00152420346 = OpConstant %float 0.00152420346 +%float_0_00112591917 = OpConstant %float 0.00112591917 +%float_0_000359987753 = OpConstant %float 0.000359987753 +%float_n0_000390625006 = OpConstant %float -0.000390625006 +%float_0_00190716912 = OpConstant %float 0.00190716912 +%float_0_00138633582 = OpConstant %float 0.00138633582 +%float_n0_00111060054 = OpConstant %float -0.00111060054 +%float_0_00161611522 = OpConstant %float 0.00161611522 +%float_n0_000880821084 = OpConstant %float -0.000880821084 +%float_0_000145526967 = OpConstant %float 0.000145526967 +%float_0_00107996329 = OpConstant %float 0.00107996329 +%float_n5_36151965en05 = OpConstant %float -5.36151965e-05 +%float_0_00028339462 = OpConstant %float 0.00028339462 +%float_n0_00169270835 = OpConstant %float -0.00169270835 +%float_n0_00126378681 = OpConstant %float -0.00126378681 +%float_n0_000513174047 = OpConstant %float -0.000513174047 +%float_n0_000160845593 = OpConstant %float -0.000160845593 +%float_n0_00187653187 = OpConstant %float -0.00187653187 +%float_n0_000834865205 = OpConstant %float -0.000834865205 +%float_0_00063572306 = OpConstant %float 0.00063572306 +%float_7_65931418en06 = OpConstant %float 7.65931418e-06 +%float_n0_00190716912 = OpConstant %float -0.00190716912 +%float_n0_000222120099 = OpConstant %float -0.000222120099 +%float_0_000375306379 = OpConstant %float 0.000375306379 +%float_n0_00173866423 = OpConstant %float -0.00173866423 +%float_n0_000405943632 = OpConstant %float -0.000405943632 +%float_n0_00123314955 = OpConstant %float -0.00123314955 +%float_0_00170802698 = OpConstant %float 0.00170802698 +%float_n0_00094209559 = OpConstant %float -0.00094209559 +%float_0_000819546578 = OpConstant %float 0.000819546578 +%float_0_00184589461 = OpConstant %float 0.00184589461 +%float_0_000467218139 = OpConstant %float 0.000467218139 + %337 = OpConstantComposite %_arr_float_uint_256 %float_n0_00100337015 %float_0_000880821084 %float_0_00169270835 %float_n0_00155484071 %float_0_00127910543 %float_n0_000605085806 %float_0_00106464466 %float_n0_00138633582 %float_0_00135569857 %float_0_000513174047 %float_0_00121783093 %float_n0_00160079659 %float_0_00058976718 %float_n0_00028339462 %float_0_00111060054 %float_n0_00141697307 %float_0_00144761032 %float_n0_0005438113 %float_0_00013020834 %float_n0_0012025123 %float_0_000436580885 %float_n0_00104932603 %float_0_000620404433 %float_n0_000482536765 %float_0_00187653187 %float_n0_00109528191 %float_n9_95710798en05 %float_n0_000528492674 %float_0_0014322917 %float_n0_00193780637 %float_n0_000696997566 %float_3_829657en05 %float_0_000712316192 %float_n0_00130974269 %float_0_00109528191 %float_n0_000298713247 %float_0_00175398286 %float_n0_00167738972 %float_0_00147824758 %float_n3_829657en05 %float_0_000788909325 %float_n0_00183057599 %float_0_000298713247 %float_0_000988051528 %float_n0_00117187505 %float_0_00017616422 %float_0_00164675247 %float_n0_00158547796 %float_0_000344669126 %float_0_00186121324 %float_n0_00176930148 %float_n0_000865502458 %float_0_000896139711 %float_0_000160845593 %float_n0_000926776964 %float_n0_00152420346 %float_n0_000651041686 %float_0_00129442406 %float_n0_000804227951 %float_n0_00146292895 %float_0_00179993873 %float_n0_000850183831 %float_0_000850183831 %float_n0_000451899512 %float_n0_00106464466 %float_n0_000145526967 %float_0_000237438726 %float_0_00141697307 %float_n0_00058976718 %float_n0_000191482846 %float_0_00160079659 %float_0_00101868878 %float_0_000405943632 %float_n0_000206801473 %float_0_00158547796 %float_0_000651041686 %float_n6_89338267en05 %float_0_000421262259 %float_n0_00164675247 %float_0_00137101719 %float_0_000926776964 %float_n0_000666360313 %float_0_00118719367 %float_n0_00144761032 %float_0_000574448553 %float_n0_00189185049 %float_0_000758272072 %float_n0_00129442406 %float_0_00192248775 %float_n0_0016620711 %float_n0_00103400741 %float_n0_000497855421 %float_n0_00186121324 %float_0_0012025123 %float_n0_0003293505 %float_n0_00137101719 %float_0_00163143384 %float_n0_00184589461 %float_0_000727634819 %float_n0_000911458337 %float_0_00181525736 %float_n0_00114123779 %float_n0_000375306379 %float_9_95710798en05 %float_n0_000742953445 %float_0_00117187505 %float_6_89338267en05 %float_0_0014935662 %float_0_000972732843 %float_n0_000957414217 %float_0_00193780637 %float_0_000528492674 %float_5_36151965en05 %float_n0_00124846818 %float_n0_000268075994 %float_0_00153952208 %float_n7_65931418en06 %float_0_000314031873 %float_0_00134037994 %float_n0_00175398286 %float_0_000497855421 %float_n0_00118719367 %float_0_000773590698 %float_n0_00134037994 %float_0_000268075994 %float_n0_00147824758 %float_n0_00013020834 %float_n0_000773590698 %float_0_00130974269 %float_0_000390625006 %float_0_000957414217 %float_n0_000467218139 %float_n0_00153952208 %float_0_00103400741 %float_n0_000681678939 %float_0_00167738972 %float_0_00100337015 %float_n0_000421262259 %float_0_00178462011 %float_n0_000237438726 %float_n0_000620404433 %float_0_0016620711 %float_0_000834865205 %float_n0_0017233456 %float_n0_00107996329 %float_0_00176930148 %float_n0_000788909325 %float_n0_00178462011 %float_0_000681678939 %float_n0_000988051528 %float_n0_00132506131 %float_n0_00017616422 %float_n0_00150888483 %float_0_0003293505 %float_n0_001953125 %float_0_000666360313 %float_n0_00161611522 %float_0_00115655642 %float_0_000451899512 %float_n0_000436580885 %float_0_000191482846 %float_n0_0014935662 %float_0_00114123779 %float_8_42524532en05 %float_0_00189185049 %float_0_00140165444 %float_0_000559129927 %float_0_000114889706 %float_0_00126378681 %float_n0_000574448553 %float_n0_000972732843 %float_0_00132506131 %float_0_000222120099 %float_n0_000758272072 %float_n0_00135569857 %float_0_00146292895 %float_0_000865502458 %float_n0_000359987753 %float_0_0005438113 %float_n0_00112591917 %float_n0_000252757367 %float_n0_000559129927 %float_n0_00181525736 %float_0_0017233456 %float_n0_00115655642 %float_0_000742953445 %float_0_00157015934 %float_n0_000114889706 %float_n0_00121783093 %float_0_00183057599 %float_2_29779416en05 %float_n0_00192248775 %float_0_00173866423 %float_n0_000712316192 %float_0_00155484071 %float_n0_00170802698 %float_0_00123314955 %float_0_000206801473 %float_0_00104932603 %float_n0_000727634819 %float_n0_00163143384 %float_n0_000314031873 %float_0_000482536765 %float_n0_00179993873 %float_0_00094209559 %float_n0_000344669126 %float_0_000696997566 %float_n0_00101868878 %float_n0_00157015934 %float_n2_29779416en05 %float_n0_00127910543 %float_0_000804227951 %float_n0_000896139711 %float_n0_0014322917 %float_0_000605085806 %float_n8_42524532en05 %float_0_000911458337 %float_0_001953125 %float_n0_00140165444 %float_n0_00063572306 %float_0_00150888483 %float_n0_000819546578 %float_0_00124846818 %float_0_000252757367 %float_0_00152420346 %float_0_00112591917 %float_0_000359987753 %float_n0_000390625006 %float_0_00190716912 %float_0_00138633582 %float_n0_00111060054 %float_0_00161611522 %float_n0_000880821084 %float_0_000145526967 %float_0_00107996329 %float_n5_36151965en05 %float_0_00028339462 %float_n0_00169270835 %float_n0_00126378681 %float_n0_000513174047 %float_n0_000160845593 %float_n0_00187653187 %float_n0_000834865205 %float_0_00063572306 %float_7_65931418en06 %float_n0_00190716912 %float_n0_000222120099 %float_0_000375306379 %float_n0_00173866423 %float_n0_000405943632 %float_n0_00123314955 %float_0_00170802698 %float_n0_00094209559 %float_0_000819546578 %float_0_00184589461 %float_0_000467218139 + %uint_1 = OpConstant %uint 1 + %uint_16 = OpConstant %uint 16 + %uint_0 = OpConstant %uint 0 +%_ptr_Function__arr_float_uint_256 = OpTypePointer Function %_arr_float_uint_256 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 +%uint_532432441 = OpConstant %uint 532432441 +%uint_2129690299 = OpConstant %uint 2129690299 +%uint_2129764351 = OpConstant %uint 2129764351 + %float_2 = OpConstant %float 2 + %424 = OpTypeImage %float 2D 0 0 0 1 Unknown + %425 = OpTypeSampledImage %424 +%_ptr_UniformConstant_425 = OpTypePointer UniformConstant %425 + %xe_texture = OpVariable %_ptr_UniformConstant_425 UniformConstant + %int_0 = OpConstant %int 0 + %v4float = OpTypeVector %float 4 + %int_n1 = OpConstant %int -1 + %462 = OpConstantComposite %v2int %int_0 %int_n1 + %int_1 = OpConstant %int 1 + %469 = OpConstantComposite %v2int %int_1 %int_n1 + %475 = OpConstantComposite %v2int %int_n1 %int_0 + %485 = OpConstantComposite %v2int %int_1 %int_0 + %491 = OpConstantComposite %v2int %int_n1 %int_1 + %497 = OpConstantComposite %v2int %int_0 %int_1 + %503 = OpConstantComposite %v2int %int_1 %int_1 + %uint_2 = OpConstant %uint 2 + %v2float = OpTypeVector %float 2 + %int_2 = OpConstant %int 2 + %992 = OpConstantComposite %v2int %int_2 %int_0 + %1014 = OpConstantComposite %v2int %int_0 %int_2 + %1025 = OpConstantComposite %v2int %int_2 %int_1 + %1031 = OpConstantComposite %v2int %int_1 %int_2 +%float_0_03125 = OpConstant %float 0.03125 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%XeCasSharpenConstants = OpTypeStruct %v2int %float +%_ptr_PushConstant_XeCasSharpenConstants = OpTypePointer PushConstant %XeCasSharpenConstants + %_ = OpVariable %_ptr_PushConstant_XeCasSharpenConstants PushConstant +%_ptr_PushConstant_v2int = OpTypePointer PushConstant %v2int + %2497 = OpConstantComposite %v4float %float_1 %float_1 %float_0 %float_0 +%_ptr_PushConstant_float = OpTypePointer PushConstant %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%xe_frag_color = OpVariable %_ptr_Output_v4float Output +%_ptr_Output_float = OpTypePointer Output %float + %2544 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %2545 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %uint_3 = OpConstant %uint 3 + %6763 = OpConstantComposite %v2uint %uint_15 %uint_15 + %main = OpFunction %void None %3 + %5 = OpLabel + %6296 = OpVariable %_ptr_Function__arr_float_uint_256 Function + %2485 = OpLoad %v4float %gl_FragCoord + %2486 = OpVectorShuffle %v2float %2485 %2485 0 1 + %2487 = OpConvertFToS %v2int %2486 + %2492 = OpAccessChain %_ptr_PushConstant_v2int %_ %int_0 + %2493 = OpLoad %v2int %2492 + %2494 = OpISub %v2int %2487 %2493 + %2495 = OpBitcast %v2uint %2494 + %2498 = OpBitcast %v4uint %2497 + %2501 = OpAccessChain %_ptr_PushConstant_float %_ %int_1 + %2502 = OpLoad %float %2501 + %2503 = OpBitcast %uint %2502 + OpBranch %3056 + %3056 = OpLabel + %3407 = OpConvertUToF %v2float %2495 + %3410 = OpCompositeExtract %uint %2498 0 + %3411 = OpCompositeExtract %uint %2498 1 + %3412 = OpCompositeConstruct %v2uint %3410 %3411 + %3413 = OpBitcast %v2float %3412 + %3414 = OpFMul %v2float %3407 %3413 + %3417 = OpCompositeExtract %uint %2498 2 + %3418 = OpCompositeExtract %uint %2498 3 + %3419 = OpCompositeConstruct %v2uint %3417 %3418 + %3420 = OpBitcast %v2float %3419 + %3421 = OpFAdd %v2float %3414 %3420 + %3423 = OpExtInst %v2float %1 Floor %3421 + %3426 = OpFSub %v2float %3421 %3423 + %3428 = OpConvertFToS %v2int %3423 + %3433 = OpIAdd %v2int %3428 %462 + %5023 = OpLoad %425 %xe_texture + %5025 = OpImage %424 %5023 + %5026 = OpImageFetch %v4float %5025 %3433 Lod %int_0 + %3436 = OpIAdd %v2int %3428 %475 + %5032 = OpImage %424 %5023 + %5033 = OpImageFetch %v4float %5032 %3436 Lod %int_0 + %5039 = OpImage %424 %5023 + %5040 = OpImageFetch %v4float %5039 %3428 Lod %int_0 + %3441 = OpIAdd %v2int %3428 %469 + %5046 = OpImage %424 %5023 + %5047 = OpImageFetch %v4float %5046 %3441 Lod %int_0 + %3447 = OpIAdd %v2int %3428 %485 + %5060 = OpImage %424 %5023 + %5061 = OpImageFetch %v4float %5060 %3447 Lod %int_0 + %3450 = OpIAdd %v2int %3428 %992 + %5067 = OpImage %424 %5023 + %5068 = OpImageFetch %v4float %5067 %3450 Lod %int_0 + %3453 = OpIAdd %v2int %3428 %491 + %5074 = OpImage %424 %5023 + %5075 = OpImageFetch %v4float %5074 %3453 Lod %int_0 + %3456 = OpIAdd %v2int %3428 %497 + %5081 = OpImage %424 %5023 + %5082 = OpImageFetch %v4float %5081 %3456 Lod %int_0 + %3462 = OpIAdd %v2int %3428 %1014 + %5095 = OpImage %424 %5023 + %5096 = OpImageFetch %v4float %5095 %3462 Lod %int_0 + %3465 = OpIAdd %v2int %3428 %503 + %5102 = OpImage %424 %5023 + %5103 = OpImageFetch %v4float %5102 %3465 Lod %int_0 + %3468 = OpIAdd %v2int %3428 %1025 + %5109 = OpImage %424 %5023 + %5110 = OpImageFetch %v4float %5109 %3468 Lod %int_0 + %3471 = OpIAdd %v2int %3428 %1031 + %5116 = OpImage %424 %5023 + %5117 = OpImageFetch %v4float %5116 %3471 Lod %int_0 + %3490 = OpCompositeExtract %float %5026 0 + %3492 = OpCompositeExtract %float %5026 1 + %3494 = OpCompositeExtract %float %5026 2 + %5139 = OpFMul %float %3490 %3490 + %5142 = OpFMul %float %3492 %3492 + %5145 = OpFMul %float %3494 %3494 + %3503 = OpCompositeExtract %float %5047 0 + %3505 = OpCompositeExtract %float %5047 1 + %3507 = OpCompositeExtract %float %5047 2 + %5149 = OpFMul %float %3503 %3503 + %5152 = OpFMul %float %3505 %3505 + %5155 = OpFMul %float %3507 %3507 + %3529 = OpCompositeExtract %float %5033 0 + %3531 = OpCompositeExtract %float %5033 1 + %3533 = OpCompositeExtract %float %5033 2 + %5169 = OpFMul %float %3529 %3529 + %5172 = OpFMul %float %3531 %3531 + %5175 = OpFMul %float %3533 %3533 + %3542 = OpCompositeExtract %float %5040 0 + %3544 = OpCompositeExtract %float %5040 1 + %3546 = OpCompositeExtract %float %5040 2 + %5179 = OpFMul %float %3542 %3542 + %5182 = OpFMul %float %3544 %3544 + %5185 = OpFMul %float %3546 %3546 + %3555 = OpCompositeExtract %float %5061 0 + %3557 = OpCompositeExtract %float %5061 1 + %3559 = OpCompositeExtract %float %5061 2 + %5189 = OpFMul %float %3555 %3555 + %5192 = OpFMul %float %3557 %3557 + %5195 = OpFMul %float %3559 %3559 + %3568 = OpCompositeExtract %float %5068 0 + %3570 = OpCompositeExtract %float %5068 1 + %3572 = OpCompositeExtract %float %5068 2 + %5199 = OpFMul %float %3568 %3568 + %5202 = OpFMul %float %3570 %3570 + %5205 = OpFMul %float %3572 %3572 + %3581 = OpCompositeExtract %float %5075 0 + %3583 = OpCompositeExtract %float %5075 1 + %3585 = OpCompositeExtract %float %5075 2 + %5209 = OpFMul %float %3581 %3581 + %5212 = OpFMul %float %3583 %3583 + %5215 = OpFMul %float %3585 %3585 + %3594 = OpCompositeExtract %float %5082 0 + %3596 = OpCompositeExtract %float %5082 1 + %3598 = OpCompositeExtract %float %5082 2 + %5219 = OpFMul %float %3594 %3594 + %5222 = OpFMul %float %3596 %3596 + %5225 = OpFMul %float %3598 %3598 + %3607 = OpCompositeExtract %float %5103 0 + %3609 = OpCompositeExtract %float %5103 1 + %3611 = OpCompositeExtract %float %5103 2 + %5229 = OpFMul %float %3607 %3607 + %5232 = OpFMul %float %3609 %3609 + %5235 = OpFMul %float %3611 %3611 + %3620 = OpCompositeExtract %float %5110 0 + %3622 = OpCompositeExtract %float %5110 1 + %3624 = OpCompositeExtract %float %5110 2 + %5239 = OpFMul %float %3620 %3620 + %5242 = OpFMul %float %3622 %3622 + %5245 = OpFMul %float %3624 %3624 + %3646 = OpCompositeExtract %float %5096 0 + %3648 = OpCompositeExtract %float %5096 1 + %3650 = OpCompositeExtract %float %5096 2 + %5259 = OpFMul %float %3646 %3646 + %5262 = OpFMul %float %3648 %3648 + %5265 = OpFMul %float %3650 %3650 + %3659 = OpCompositeExtract %float %5117 0 + %3661 = OpCompositeExtract %float %5117 1 + %3663 = OpCompositeExtract %float %5117 2 + %5269 = OpFMul %float %3659 %3659 + %5272 = OpFMul %float %3661 %3661 + %5275 = OpFMul %float %3663 %3663 + %5305 = OpExtInst %float %1 FMin %5172 %5182 + %5306 = OpExtInst %float %1 FMin %5142 %5305 + %5312 = OpExtInst %float %1 FMin %5192 %5222 + %5313 = OpExtInst %float %1 FMin %5306 %5312 + %5347 = OpExtInst %float %1 FMax %5172 %5182 + %5348 = OpExtInst %float %1 FMax %5142 %5347 + %5354 = OpExtInst %float %1 FMax %5192 %5222 + %5355 = OpExtInst %float %1 FMax %5348 %5354 + %5389 = OpExtInst %float %1 FMin %5182 %5192 + %5390 = OpExtInst %float %1 FMin %5152 %5389 + %5396 = OpExtInst %float %1 FMin %5202 %5232 + %5397 = OpExtInst %float %1 FMin %5390 %5396 + %5431 = OpExtInst %float %1 FMax %5182 %5192 + %5432 = OpExtInst %float %1 FMax %5152 %5431 + %5438 = OpExtInst %float %1 FMax %5202 %5232 + %5439 = OpExtInst %float %1 FMax %5432 %5438 + %5473 = OpExtInst %float %1 FMin %5212 %5222 + %5474 = OpExtInst %float %1 FMin %5182 %5473 + %5480 = OpExtInst %float %1 FMin %5232 %5262 + %5481 = OpExtInst %float %1 FMin %5474 %5480 + %5515 = OpExtInst %float %1 FMax %5212 %5222 + %5516 = OpExtInst %float %1 FMax %5182 %5515 + %5522 = OpExtInst %float %1 FMax %5232 %5262 + %5523 = OpExtInst %float %1 FMax %5516 %5522 + %5557 = OpExtInst %float %1 FMin %5222 %5232 + %5558 = OpExtInst %float %1 FMin %5192 %5557 + %5564 = OpExtInst %float %1 FMin %5242 %5272 + %5565 = OpExtInst %float %1 FMin %5558 %5564 + %5599 = OpExtInst %float %1 FMax %5222 %5232 + %5600 = OpExtInst %float %1 FMax %5192 %5599 + %5606 = OpExtInst %float %1 FMax %5242 %5272 + %5607 = OpExtInst %float %1 FMax %5600 %5606 + %5638 = OpBitcast %uint %5355 + %5639 = OpISub %uint %uint_2129690299 %5638 + %5640 = OpBitcast %float %5639 + %5671 = OpBitcast %uint %5439 + %5672 = OpISub %uint %uint_2129690299 %5671 + %5673 = OpBitcast %float %5672 + %5704 = OpBitcast %uint %5523 + %5705 = OpISub %uint %uint_2129690299 %5704 + %5706 = OpBitcast %float %5705 + %5737 = OpBitcast %uint %5607 + %5738 = OpISub %uint %uint_2129690299 %5737 + %5739 = OpBitcast %float %5738 + %4007 = OpFSub %float %float_1 %5355 + %4008 = OpExtInst %float %1 FMin %5313 %4007 + %4010 = OpFMul %float %4008 %5640 + %5781 = OpExtInst %float %1 FClamp %4010 %float_0 %float_1 + %4031 = OpFSub %float %float_1 %5439 + %4032 = OpExtInst %float %1 FMin %5397 %4031 + %4034 = OpFMul %float %4032 %5673 + %5832 = OpExtInst %float %1 FClamp %4034 %float_0 %float_1 + %4055 = OpFSub %float %float_1 %5523 + %4056 = OpExtInst %float %1 FMin %5481 %4055 + %4058 = OpFMul %float %4056 %5706 + %5883 = OpExtInst %float %1 FClamp %4058 %float_0 %float_1 + %4079 = OpFSub %float %float_1 %5607 + %4080 = OpExtInst %float %1 FMin %5565 %4079 + %4082 = OpFMul %float %4080 %5739 + %5934 = OpExtInst %float %1 FClamp %4082 %float_0 %float_1 + %5980 = OpBitcast %uint %5781 + %5982 = OpShiftRightLogical %uint %5980 %uint_1 + %5984 = OpIAdd %uint %5982 %uint_532432441 + %5985 = OpBitcast %float %5984 + %6031 = OpBitcast %uint %5832 + %6033 = OpShiftRightLogical %uint %6031 %uint_1 + %6035 = OpIAdd %uint %6033 %uint_532432441 + %6036 = OpBitcast %float %6035 + %6082 = OpBitcast %uint %5883 + %6084 = OpShiftRightLogical %uint %6082 %uint_1 + %6086 = OpIAdd %uint %6084 %uint_532432441 + %6087 = OpBitcast %float %6086 + %6133 = OpBitcast %uint %5934 + %6135 = OpShiftRightLogical %uint %6133 %uint_1 + %6137 = OpIAdd %uint %6135 %uint_532432441 + %6138 = OpBitcast %float %6137 + %4118 = OpBitcast %float %2503 + %4124 = OpFMul %float %5985 %4118 + %4133 = OpFMul %float %6036 %4118 + %4142 = OpFMul %float %6087 %4118 + %4151 = OpFMul %float %6138 %4118 + %4157 = OpCompositeExtract %float %3426 0 + %4158 = OpFSub %float %float_1 %4157 + %4161 = OpCompositeExtract %float %3426 1 + %4162 = OpFSub %float %float_1 %4161 + %4163 = OpFMul %float %4158 %4162 + %4170 = OpFMul %float %4157 %4162 + %4177 = OpFMul %float %4158 %4161 + %4182 = OpFMul %float %4157 %4161 + %4186 = OpFSub %float %5355 %5313 + %4187 = OpFAdd %float %float_0_03125 %4186 + %6179 = OpBitcast %uint %4187 + %6180 = OpISub %uint %uint_2129690299 %6179 + %6181 = OpBitcast %float %6180 + %4190 = OpFMul %float %4163 %6181 + %4194 = OpFSub %float %5439 %5397 + %4195 = OpFAdd %float %float_0_03125 %4194 + %6190 = OpBitcast %uint %4195 + %6191 = OpISub %uint %uint_2129690299 %6190 + %6192 = OpBitcast %float %6191 + %4198 = OpFMul %float %4170 %6192 + %4202 = OpFSub %float %5523 %5481 + %4203 = OpFAdd %float %float_0_03125 %4202 + %6201 = OpBitcast %uint %4203 + %6202 = OpISub %uint %uint_2129690299 %6201 + %6203 = OpBitcast %float %6202 + %4206 = OpFMul %float %4177 %6203 + %4210 = OpFSub %float %5607 %5565 + %4211 = OpFAdd %float %float_0_03125 %4210 + %6212 = OpBitcast %uint %4211 + %6213 = OpISub %uint %uint_2129690299 %6212 + %6214 = OpBitcast %float %6213 + %4214 = OpFMul %float %4182 %6214 + %4220 = OpFMul %float %4124 %4190 + %4229 = OpFMul %float %4133 %4198 + %4247 = OpFMul %float %4142 %4206 + %4248 = OpFAdd %float %4229 %4247 + %4250 = OpFAdd %float %4248 %4190 + %4274 = OpFMul %float %4151 %4214 + %4275 = OpFAdd %float %4220 %4274 + %4277 = OpFAdd %float %4275 %4198 + %4304 = OpFAdd %float %4275 %4206 + %4331 = OpFAdd %float %4248 %4214 + %6748 = OpFAdd %float %4220 %4229 + %6749 = OpFAdd %float %6748 %4247 + %6750 = OpFAdd %float %6749 %4274 + %4373 = OpFMul %float %float_2 %6750 + %4375 = OpFAdd %float %4373 %4250 + %4377 = OpFAdd %float %4375 %4277 + %4379 = OpFAdd %float %4377 %4304 + %4381 = OpFAdd %float %4379 %4331 + %6237 = OpBitcast %uint %4381 + %6238 = OpISub %uint %uint_2129764351 %6237 + %6239 = OpBitcast %float %6238 + %6242 = OpFNegate %float %6239 + %6244 = OpFMul %float %6242 %4381 + %6246 = OpFAdd %float %6244 %float_2 + %6247 = OpFMul %float %6239 %6246 + %6751 = OpFAdd %float %5139 %5169 + %4391 = OpFMul %float %4220 %6751 + %4395 = OpFMul %float %5149 %4229 + %4396 = OpFAdd %float %4391 %4395 + %4400 = OpFMul %float %5199 %4229 + %4401 = OpFAdd %float %4396 %4400 + %4405 = OpFMul %float %5209 %4247 + %4406 = OpFAdd %float %4401 %4405 + %4410 = OpFMul %float %5259 %4247 + %4411 = OpFAdd %float %4406 %4410 + %4415 = OpFMul %float %5239 %4274 + %4416 = OpFAdd %float %4411 %4415 + %4420 = OpFMul %float %5269 %4274 + %4421 = OpFAdd %float %4416 %4420 + %4425 = OpFMul %float %5179 %4250 + %4426 = OpFAdd %float %4421 %4425 + %4430 = OpFMul %float %5189 %4277 + %4431 = OpFAdd %float %4426 %4430 + %4435 = OpFMul %float %5219 %4304 + %4436 = OpFAdd %float %4431 %4435 + %4440 = OpFMul %float %5229 %4331 + %4441 = OpFAdd %float %4436 %4440 + %4443 = OpFMul %float %4441 %6247 + %6261 = OpExtInst %float %1 FClamp %4443 %float_0 %float_1 + %6752 = OpFAdd %float %5142 %5172 + %4453 = OpFMul %float %4220 %6752 + %4457 = OpFMul %float %5152 %4229 + %4458 = OpFAdd %float %4453 %4457 + %4462 = OpFMul %float %5202 %4229 + %4463 = OpFAdd %float %4458 %4462 + %4467 = OpFMul %float %5212 %4247 + %4468 = OpFAdd %float %4463 %4467 + %4472 = OpFMul %float %5262 %4247 + %4473 = OpFAdd %float %4468 %4472 + %4477 = OpFMul %float %5242 %4274 + %4478 = OpFAdd %float %4473 %4477 + %4482 = OpFMul %float %5272 %4274 + %4483 = OpFAdd %float %4478 %4482 + %4487 = OpFMul %float %5182 %4250 + %4488 = OpFAdd %float %4483 %4487 + %4492 = OpFMul %float %5192 %4277 + %4493 = OpFAdd %float %4488 %4492 + %4497 = OpFMul %float %5222 %4304 + %4498 = OpFAdd %float %4493 %4497 + %4502 = OpFMul %float %5232 %4331 + %4503 = OpFAdd %float %4498 %4502 + %4505 = OpFMul %float %4503 %6247 + %6275 = OpExtInst %float %1 FClamp %4505 %float_0 %float_1 + %6753 = OpFAdd %float %5145 %5175 + %4515 = OpFMul %float %4220 %6753 + %4519 = OpFMul %float %5155 %4229 + %4520 = OpFAdd %float %4515 %4519 + %4524 = OpFMul %float %5205 %4229 + %4525 = OpFAdd %float %4520 %4524 + %4529 = OpFMul %float %5215 %4247 + %4530 = OpFAdd %float %4525 %4529 + %4534 = OpFMul %float %5265 %4247 + %4535 = OpFAdd %float %4530 %4534 + %4539 = OpFMul %float %5245 %4274 + %4540 = OpFAdd %float %4535 %4539 + %4544 = OpFMul %float %5275 %4274 + %4545 = OpFAdd %float %4540 %4544 + %4549 = OpFMul %float %5185 %4250 + %4550 = OpFAdd %float %4545 %4549 + %4554 = OpFMul %float %5195 %4277 + %4555 = OpFAdd %float %4550 %4554 + %4559 = OpFMul %float %5225 %4304 + %4560 = OpFAdd %float %4555 %4559 + %4564 = OpFMul %float %5235 %4331 + %4565 = OpFAdd %float %4560 %4564 + %4567 = OpFMul %float %4565 %6247 + %6289 = OpExtInst %float %1 FClamp %4567 %float_0 %float_1 + %2527 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_0 + OpStore %2527 %6261 + %2529 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_1 + OpStore %2529 %6275 + %2531 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_2 + OpStore %2531 %6289 + %2532 = OpLoad %v4float %xe_frag_color + %2533 = OpVectorShuffle %v3float %2532 %2532 0 1 2 + %2534 = OpExtInst %v3float %1 Sqrt %2533 + %2535 = OpLoad %v4float %xe_frag_color + %2536 = OpVectorShuffle %v4float %2535 %2534 4 5 6 3 + OpStore %xe_frag_color %2536 + %2537 = OpLoad %v4float %xe_frag_color + %2538 = OpVectorShuffle %v3float %2537 %2537 0 1 2 + %6301 = OpBitwiseAnd %v2uint %2495 %6763 + %6303 = OpCompositeExtract %uint %6301 1 + %6304 = OpIMul %uint %6303 %uint_16 + %6306 = OpCompositeExtract %uint %6301 0 + %6307 = OpIAdd %uint %6304 %6306 + OpStore %6296 %337 + %6308 = OpAccessChain %_ptr_Function_float %6296 %6307 + %6309 = OpLoad %float %6308 + %2542 = OpCompositeConstruct %v3float %6309 %6309 %6309 + %2543 = OpFAdd %v3float %2538 %2542 + %2546 = OpExtInst %v3float %1 FClamp %2543 %2544 %2545 + %2547 = OpLoad %v4float %xe_frag_color + %2548 = OpVectorShuffle %v4float %2547 %2546 4 5 6 3 + OpStore %xe_frag_color %2548 + %2550 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_3 + OpStore %2550 %float_1 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.h new file mode 100644 index 000000000..225d5e0e3 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.h @@ -0,0 +1,741 @@ +// generated from `xb genspirv` +// source: guest_output_ffx_cas_sharpen.frag +const uint8_t guest_output_ffx_cas_sharpen_frag[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x2E, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x9C, 0x08, 0x00, 0x00, 0xB8, 0x08, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0A, 0x00, + 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, + 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, + 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, + 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x92, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x9C, 0x08, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, + 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, + 0xA0, 0x08, 0x00, 0x00, 0x58, 0x65, 0x43, 0x61, 0x73, 0x53, 0x68, 0x61, + 0x72, 0x70, 0x65, 0x6E, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, 0xA0, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x6F, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, 0xA0, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x61, 0x73, 0x5F, 0x73, + 0x68, 0x61, 0x72, 0x70, 0x6E, 0x65, 0x73, 0x73, 0x5F, 0x70, 0x6F, 0x73, + 0x74, 0x5F, 0x73, 0x65, 0x74, 0x75, 0x70, 0x00, 0x05, 0x00, 0x03, 0x00, + 0xA2, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0xB8, 0x08, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x66, 0x72, 0x61, 0x67, 0x5F, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x92, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x92, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x9C, 0x08, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0xA0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xA0, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0xA0, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0xB8, 0x08, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x2B, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, + 0x39, 0x46, 0xBC, 0x1F, 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0xBB, 0x7E, 0xF0, 0x7E, 0x2B, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xFF, 0x9F, 0xF1, 0x7E, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x19, 0x00, 0x09, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x91, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x91, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x29, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xCC, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, + 0xAE, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xEA, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x86, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xC8, 0x02, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0xE9, 0x02, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, + 0xBB, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xEF, 0x02, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3D, 0x20, 0x00, 0x04, 0x00, 0x9B, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x9B, 0x08, 0x00, 0x00, 0x9C, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x04, 0x00, 0xA0, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xA1, 0x08, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xA0, 0x08, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0xA1, 0x08, 0x00, 0x00, 0xA2, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xA3, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xA9, 0x08, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xAC, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xB7, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xB7, 0x08, 0x00, 0x00, + 0xB8, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xC6, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD1, 0x08, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x9D, 0x08, 0x00, 0x00, 0x9C, 0x08, 0x00, 0x00, + 0x4F, 0x00, 0x07, 0x00, 0x86, 0x02, 0x00, 0x00, 0x9E, 0x08, 0x00, 0x00, + 0x9D, 0x08, 0x00, 0x00, 0x9D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x9F, 0x08, 0x00, 0x00, 0x9E, 0x08, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xA3, 0x08, 0x00, 0x00, 0xA4, 0x08, 0x00, 0x00, 0xA2, 0x08, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0xA5, 0x08, 0x00, 0x00, 0xA4, 0x08, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0xA6, 0x08, 0x00, 0x00, 0x9F, 0x08, 0x00, 0x00, + 0xA5, 0x08, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, + 0xA7, 0x08, 0x00, 0x00, 0xA6, 0x08, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x39, 0x00, 0x00, 0x00, 0xAA, 0x08, 0x00, 0x00, 0xA9, 0x08, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0xAC, 0x08, 0x00, 0x00, 0xAD, 0x08, 0x00, 0x00, + 0xA2, 0x08, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xAE, 0x08, 0x00, 0x00, 0xAD, 0x08, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xAF, 0x08, 0x00, 0x00, + 0xAE, 0x08, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xCC, 0x0A, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xCC, 0x0A, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, + 0x86, 0x02, 0x00, 0x00, 0x2B, 0x0C, 0x00, 0x00, 0xA7, 0x08, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x2E, 0x0C, 0x00, 0x00, + 0xAA, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x2F, 0x0C, 0x00, 0x00, 0xAA, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x30, 0x0C, 0x00, 0x00, 0x2E, 0x0C, 0x00, 0x00, 0x2F, 0x0C, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x86, 0x02, 0x00, 0x00, 0x31, 0x0C, 0x00, 0x00, + 0x30, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, + 0x32, 0x0C, 0x00, 0x00, 0x2B, 0x0C, 0x00, 0x00, 0x31, 0x0C, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x35, 0x0C, 0x00, 0x00, + 0xAA, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x36, 0x0C, 0x00, 0x00, 0xAA, 0x08, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x37, 0x0C, 0x00, 0x00, 0x35, 0x0C, 0x00, 0x00, 0x36, 0x0C, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x86, 0x02, 0x00, 0x00, 0x38, 0x0C, 0x00, 0x00, + 0x37, 0x0C, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, + 0x39, 0x0C, 0x00, 0x00, 0x32, 0x0C, 0x00, 0x00, 0x38, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x86, 0x02, 0x00, 0x00, 0x3B, 0x0C, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x39, 0x0C, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, 0x3E, 0x0C, 0x00, 0x00, + 0x39, 0x0C, 0x00, 0x00, 0x3B, 0x0C, 0x00, 0x00, 0x6E, 0x00, 0x04, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, 0x3B, 0x0C, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x45, 0x0C, 0x00, 0x00, + 0x40, 0x0C, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x7B, 0x12, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x7D, 0x12, 0x00, 0x00, + 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x7E, 0x12, 0x00, 0x00, 0x7D, 0x12, 0x00, 0x00, 0x45, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x48, 0x0C, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, + 0xC2, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0x84, 0x12, 0x00, 0x00, 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x85, 0x12, 0x00, 0x00, 0x84, 0x12, 0x00, 0x00, + 0x48, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x8B, 0x12, 0x00, 0x00, + 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x8C, 0x12, 0x00, 0x00, 0x8B, 0x12, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x4D, 0x0C, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, + 0xBC, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0x92, 0x12, 0x00, 0x00, 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x93, 0x12, 0x00, 0x00, 0x92, 0x12, 0x00, 0x00, + 0x4D, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x0C, 0x00, 0x00, + 0x40, 0x0C, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0xA0, 0x12, 0x00, 0x00, 0x7B, 0x12, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, 0xA1, 0x12, 0x00, 0x00, + 0xA0, 0x12, 0x00, 0x00, 0x53, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x56, 0x0C, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, 0xC8, 0x02, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0xA7, 0x12, 0x00, 0x00, + 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xA8, 0x12, 0x00, 0x00, 0xA7, 0x12, 0x00, 0x00, 0x56, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x59, 0x0C, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, + 0xD2, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0xAE, 0x12, 0x00, 0x00, 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xAF, 0x12, 0x00, 0x00, 0xAE, 0x12, 0x00, 0x00, + 0x59, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x5C, 0x0C, 0x00, 0x00, + 0x40, 0x0C, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0xB5, 0x12, 0x00, 0x00, 0x7B, 0x12, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, 0xB6, 0x12, 0x00, 0x00, + 0xB5, 0x12, 0x00, 0x00, 0x5C, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x62, 0x0C, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0xC3, 0x12, 0x00, 0x00, + 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xC4, 0x12, 0x00, 0x00, 0xC3, 0x12, 0x00, 0x00, 0x62, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x65, 0x0C, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, + 0xCA, 0x12, 0x00, 0x00, 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xCB, 0x12, 0x00, 0x00, 0xCA, 0x12, 0x00, 0x00, + 0x65, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x68, 0x0C, 0x00, 0x00, + 0x40, 0x0C, 0x00, 0x00, 0xE9, 0x02, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, + 0x8F, 0x00, 0x00, 0x00, 0xD1, 0x12, 0x00, 0x00, 0x7B, 0x12, 0x00, 0x00, + 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, 0xD2, 0x12, 0x00, 0x00, + 0xD1, 0x12, 0x00, 0x00, 0x68, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x6B, 0x0C, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, 0xEF, 0x02, 0x00, 0x00, + 0x64, 0x00, 0x04, 0x00, 0x8F, 0x00, 0x00, 0x00, 0xD8, 0x12, 0x00, 0x00, + 0x7B, 0x12, 0x00, 0x00, 0x5F, 0x00, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xD9, 0x12, 0x00, 0x00, 0xD8, 0x12, 0x00, 0x00, 0x6B, 0x0C, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7E, 0x0C, 0x00, 0x00, 0x7E, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x80, 0x0C, 0x00, 0x00, 0x7E, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x82, 0x0C, 0x00, 0x00, + 0x7E, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xEF, 0x12, 0x00, 0x00, 0x7E, 0x0C, 0x00, 0x00, + 0x7E, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF2, 0x12, 0x00, 0x00, 0x80, 0x0C, 0x00, 0x00, 0x80, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF5, 0x12, 0x00, 0x00, + 0x82, 0x0C, 0x00, 0x00, 0x82, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8B, 0x0C, 0x00, 0x00, 0x93, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x8D, 0x0C, 0x00, 0x00, 0x93, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8F, 0x0C, 0x00, 0x00, + 0x93, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF9, 0x12, 0x00, 0x00, 0x8B, 0x0C, 0x00, 0x00, + 0x8B, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xFC, 0x12, 0x00, 0x00, 0x8D, 0x0C, 0x00, 0x00, 0x8D, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0x12, 0x00, 0x00, + 0x8F, 0x0C, 0x00, 0x00, 0x8F, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA5, 0x0C, 0x00, 0x00, 0x85, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA7, 0x0C, 0x00, 0x00, 0x85, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA9, 0x0C, 0x00, 0x00, + 0x85, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0D, 0x13, 0x00, 0x00, 0xA5, 0x0C, 0x00, 0x00, + 0xA5, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x10, 0x13, 0x00, 0x00, 0xA7, 0x0C, 0x00, 0x00, 0xA7, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x13, 0x13, 0x00, 0x00, + 0xA9, 0x0C, 0x00, 0x00, 0xA9, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB2, 0x0C, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB4, 0x0C, 0x00, 0x00, 0x8C, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB6, 0x0C, 0x00, 0x00, + 0x8C, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x17, 0x13, 0x00, 0x00, 0xB2, 0x0C, 0x00, 0x00, + 0xB2, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1A, 0x13, 0x00, 0x00, 0xB4, 0x0C, 0x00, 0x00, 0xB4, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x13, 0x00, 0x00, + 0xB6, 0x0C, 0x00, 0x00, 0xB6, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xBF, 0x0C, 0x00, 0x00, 0xA1, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC1, 0x0C, 0x00, 0x00, 0xA1, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC3, 0x0C, 0x00, 0x00, + 0xA1, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x21, 0x13, 0x00, 0x00, 0xBF, 0x0C, 0x00, 0x00, + 0xBF, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x24, 0x13, 0x00, 0x00, 0xC1, 0x0C, 0x00, 0x00, 0xC1, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x27, 0x13, 0x00, 0x00, + 0xC3, 0x0C, 0x00, 0x00, 0xC3, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCC, 0x0C, 0x00, 0x00, 0xA8, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCE, 0x0C, 0x00, 0x00, 0xA8, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD0, 0x0C, 0x00, 0x00, + 0xA8, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x13, 0x00, 0x00, 0xCC, 0x0C, 0x00, 0x00, + 0xCC, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2E, 0x13, 0x00, 0x00, 0xCE, 0x0C, 0x00, 0x00, 0xCE, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x13, 0x00, 0x00, + 0xD0, 0x0C, 0x00, 0x00, 0xD0, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD9, 0x0C, 0x00, 0x00, 0xAF, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDB, 0x0C, 0x00, 0x00, 0xAF, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDD, 0x0C, 0x00, 0x00, + 0xAF, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x35, 0x13, 0x00, 0x00, 0xD9, 0x0C, 0x00, 0x00, + 0xD9, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x38, 0x13, 0x00, 0x00, 0xDB, 0x0C, 0x00, 0x00, 0xDB, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x13, 0x00, 0x00, + 0xDD, 0x0C, 0x00, 0x00, 0xDD, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xE6, 0x0C, 0x00, 0x00, 0xB6, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE8, 0x0C, 0x00, 0x00, 0xB6, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xEA, 0x0C, 0x00, 0x00, + 0xB6, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3F, 0x13, 0x00, 0x00, 0xE6, 0x0C, 0x00, 0x00, + 0xE6, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x42, 0x13, 0x00, 0x00, 0xE8, 0x0C, 0x00, 0x00, 0xE8, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x45, 0x13, 0x00, 0x00, + 0xEA, 0x0C, 0x00, 0x00, 0xEA, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF3, 0x0C, 0x00, 0x00, 0xCB, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF5, 0x0C, 0x00, 0x00, 0xCB, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, + 0xCB, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x49, 0x13, 0x00, 0x00, 0xF3, 0x0C, 0x00, 0x00, + 0xF3, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4C, 0x13, 0x00, 0x00, 0xF5, 0x0C, 0x00, 0x00, 0xF5, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4F, 0x13, 0x00, 0x00, + 0xF7, 0x0C, 0x00, 0x00, 0xF7, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0xD2, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x0D, 0x00, 0x00, 0xD2, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x0D, 0x00, 0x00, + 0xD2, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x53, 0x13, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, + 0x00, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x56, 0x13, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x59, 0x13, 0x00, 0x00, + 0x04, 0x0D, 0x00, 0x00, 0x04, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1A, 0x0D, 0x00, 0x00, 0xC4, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1C, 0x0D, 0x00, 0x00, 0xC4, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x0D, 0x00, 0x00, + 0xC4, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x67, 0x13, 0x00, 0x00, 0x1A, 0x0D, 0x00, 0x00, + 0x1A, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6A, 0x13, 0x00, 0x00, 0x1C, 0x0D, 0x00, 0x00, 0x1C, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6D, 0x13, 0x00, 0x00, + 0x1E, 0x0D, 0x00, 0x00, 0x1E, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x27, 0x0D, 0x00, 0x00, 0xD9, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x29, 0x0D, 0x00, 0x00, 0xD9, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x0D, 0x00, 0x00, + 0xD9, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x71, 0x13, 0x00, 0x00, 0x27, 0x0D, 0x00, 0x00, + 0x27, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x74, 0x13, 0x00, 0x00, 0x29, 0x0D, 0x00, 0x00, 0x29, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x13, 0x00, 0x00, + 0x2B, 0x0D, 0x00, 0x00, 0x2B, 0x0D, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x95, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x10, 0x13, 0x00, 0x00, 0x1A, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xF2, 0x12, 0x00, 0x00, + 0x95, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9C, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x24, 0x13, 0x00, 0x00, 0x42, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9D, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x96, 0x13, 0x00, 0x00, 0x9C, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBF, 0x13, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x13, 0x00, 0x00, + 0x1A, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC0, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0xF2, 0x12, 0x00, 0x00, 0xBF, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC6, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x24, 0x13, 0x00, 0x00, 0x42, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC7, 0x13, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xC0, 0x13, 0x00, 0x00, + 0xC6, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE9, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x1A, 0x13, 0x00, 0x00, 0x24, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xEA, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0xFC, 0x12, 0x00, 0x00, 0xE9, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF0, 0x13, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x2E, 0x13, 0x00, 0x00, + 0x4C, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF1, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xEA, 0x13, 0x00, 0x00, 0xF0, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x13, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x1A, 0x13, 0x00, 0x00, 0x24, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xFC, 0x12, 0x00, 0x00, + 0x13, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1A, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x2E, 0x13, 0x00, 0x00, 0x4C, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1B, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x14, 0x14, 0x00, 0x00, 0x1A, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3D, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x38, 0x13, 0x00, 0x00, + 0x42, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3E, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x1A, 0x13, 0x00, 0x00, 0x3D, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x44, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x4C, 0x13, 0x00, 0x00, 0x6A, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x45, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3E, 0x14, 0x00, 0x00, + 0x44, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x67, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x38, 0x13, 0x00, 0x00, 0x42, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x68, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x1A, 0x13, 0x00, 0x00, 0x67, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6E, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x4C, 0x13, 0x00, 0x00, + 0x6A, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6F, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x68, 0x14, 0x00, 0x00, 0x6E, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x91, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x42, 0x13, 0x00, 0x00, 0x4C, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x92, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x24, 0x13, 0x00, 0x00, + 0x91, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x98, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x56, 0x13, 0x00, 0x00, 0x74, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x99, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x92, 0x14, 0x00, 0x00, 0x98, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBB, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x42, 0x13, 0x00, 0x00, + 0x4C, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xBC, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x24, 0x13, 0x00, 0x00, 0xBB, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC2, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x56, 0x13, 0x00, 0x00, 0x74, 0x13, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC3, 0x14, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xBC, 0x14, 0x00, 0x00, + 0xC2, 0x14, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xE2, 0x14, 0x00, 0x00, 0xC7, 0x13, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xE3, 0x14, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0xE2, 0x14, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE4, 0x14, 0x00, 0x00, 0xE3, 0x14, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x03, 0x15, 0x00, 0x00, 0x1B, 0x14, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x15, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x03, 0x15, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x04, 0x15, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x24, 0x15, 0x00, 0x00, + 0x6F, 0x14, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x25, 0x15, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x24, 0x15, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x15, 0x00, 0x00, + 0x25, 0x15, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x45, 0x15, 0x00, 0x00, 0xC3, 0x14, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x46, 0x15, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x45, 0x15, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x47, 0x15, 0x00, 0x00, 0x46, 0x15, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x83, 0x0E, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0xC7, 0x13, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x84, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x9D, 0x13, 0x00, 0x00, 0x83, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x86, 0x0E, 0x00, 0x00, 0x84, 0x0E, 0x00, 0x00, + 0xE4, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x71, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x86, 0x0E, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9B, 0x0E, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x1B, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9C, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0xF1, 0x13, 0x00, 0x00, 0x9B, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9E, 0x0E, 0x00, 0x00, + 0x9C, 0x0E, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA4, 0x15, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x9E, 0x0E, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB3, 0x0E, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x6F, 0x14, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB4, 0x0E, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x45, 0x14, 0x00, 0x00, + 0xB3, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB6, 0x0E, 0x00, 0x00, 0xB4, 0x0E, 0x00, 0x00, 0x26, 0x15, 0x00, 0x00, + 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD7, 0x15, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB6, 0x0E, 0x00, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCB, 0x0E, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0xC3, 0x14, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCC, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x99, 0x14, 0x00, 0x00, 0xCB, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCE, 0x0E, 0x00, 0x00, 0xCC, 0x0E, 0x00, 0x00, + 0x47, 0x15, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0A, 0x16, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xCE, 0x0E, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x38, 0x16, 0x00, 0x00, + 0x71, 0x15, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x3A, 0x16, 0x00, 0x00, 0x38, 0x16, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3C, 0x16, 0x00, 0x00, + 0x3A, 0x16, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3D, 0x16, 0x00, 0x00, 0x3C, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x6B, 0x16, 0x00, 0x00, + 0xA4, 0x15, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x6D, 0x16, 0x00, 0x00, 0x6B, 0x16, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x6F, 0x16, 0x00, 0x00, + 0x6D, 0x16, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x70, 0x16, 0x00, 0x00, 0x6F, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x9E, 0x16, 0x00, 0x00, + 0xD7, 0x15, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xA0, 0x16, 0x00, 0x00, 0x9E, 0x16, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xA2, 0x16, 0x00, 0x00, + 0xA0, 0x16, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA3, 0x16, 0x00, 0x00, 0xA2, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD1, 0x16, 0x00, 0x00, + 0x0A, 0x16, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xD3, 0x16, 0x00, 0x00, 0xD1, 0x16, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD5, 0x16, 0x00, 0x00, + 0xD3, 0x16, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD6, 0x16, 0x00, 0x00, 0xD5, 0x16, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF2, 0x0E, 0x00, 0x00, + 0xAF, 0x08, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF8, 0x0E, 0x00, 0x00, 0x3D, 0x16, 0x00, 0x00, 0xF2, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x0F, 0x00, 0x00, + 0x70, 0x16, 0x00, 0x00, 0xF2, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0A, 0x0F, 0x00, 0x00, 0xA3, 0x16, 0x00, 0x00, + 0xF2, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x13, 0x0F, 0x00, 0x00, 0xD6, 0x16, 0x00, 0x00, 0xF2, 0x0E, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x0F, 0x00, 0x00, + 0x3E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1A, 0x0F, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x19, 0x0F, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1D, 0x0F, 0x00, 0x00, 0x3E, 0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x0F, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x1D, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1F, 0x0F, 0x00, 0x00, 0x1A, 0x0F, 0x00, 0x00, + 0x1E, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x26, 0x0F, 0x00, 0x00, 0x19, 0x0F, 0x00, 0x00, 0x1E, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2D, 0x0F, 0x00, 0x00, + 0x1A, 0x0F, 0x00, 0x00, 0x1D, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x0F, 0x00, 0x00, 0x19, 0x0F, 0x00, 0x00, + 0x1D, 0x0F, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x36, 0x0F, 0x00, 0x00, 0xC7, 0x13, 0x00, 0x00, 0x9D, 0x13, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x37, 0x0F, 0x00, 0x00, + 0xF2, 0x06, 0x00, 0x00, 0x36, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xFF, 0x16, 0x00, 0x00, 0x37, 0x0F, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0xFF, 0x16, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3A, 0x0F, 0x00, 0x00, + 0x1F, 0x0F, 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3E, 0x0F, 0x00, 0x00, 0x1B, 0x14, 0x00, 0x00, + 0xF1, 0x13, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3F, 0x0F, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, 0x3E, 0x0F, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0A, 0x17, 0x00, 0x00, + 0x3F, 0x0F, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0B, 0x17, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0A, 0x17, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x17, 0x00, 0x00, + 0x0B, 0x17, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x42, 0x0F, 0x00, 0x00, 0x26, 0x0F, 0x00, 0x00, 0x0C, 0x17, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x0F, 0x00, 0x00, + 0x6F, 0x14, 0x00, 0x00, 0x45, 0x14, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x47, 0x0F, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, + 0x46, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x15, 0x17, 0x00, 0x00, 0x47, 0x0F, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x16, 0x17, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x15, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x17, 0x17, 0x00, 0x00, 0x16, 0x17, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4A, 0x0F, 0x00, 0x00, 0x2D, 0x0F, 0x00, 0x00, + 0x17, 0x17, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4E, 0x0F, 0x00, 0x00, 0xC3, 0x14, 0x00, 0x00, 0x99, 0x14, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x00, 0x00, + 0xF2, 0x06, 0x00, 0x00, 0x4E, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x20, 0x17, 0x00, 0x00, 0x4F, 0x0F, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x21, 0x17, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x20, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x22, 0x17, 0x00, 0x00, 0x21, 0x17, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x52, 0x0F, 0x00, 0x00, + 0x32, 0x0F, 0x00, 0x00, 0x22, 0x17, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x58, 0x0F, 0x00, 0x00, 0xF8, 0x0E, 0x00, 0x00, + 0x3A, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x61, 0x0F, 0x00, 0x00, 0x01, 0x0F, 0x00, 0x00, 0x42, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x73, 0x0F, 0x00, 0x00, + 0x0A, 0x0F, 0x00, 0x00, 0x4A, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x74, 0x0F, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, + 0x73, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x76, 0x0F, 0x00, 0x00, 0x74, 0x0F, 0x00, 0x00, 0x3A, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8E, 0x0F, 0x00, 0x00, + 0x13, 0x0F, 0x00, 0x00, 0x52, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8F, 0x0F, 0x00, 0x00, 0x58, 0x0F, 0x00, 0x00, + 0x8E, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x91, 0x0F, 0x00, 0x00, 0x8F, 0x0F, 0x00, 0x00, 0x42, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAC, 0x0F, 0x00, 0x00, + 0x8F, 0x0F, 0x00, 0x00, 0x4A, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC7, 0x0F, 0x00, 0x00, 0x74, 0x0F, 0x00, 0x00, + 0x52, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x28, 0x19, 0x00, 0x00, 0x58, 0x0F, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x19, 0x00, 0x00, + 0x28, 0x19, 0x00, 0x00, 0x73, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2A, 0x19, 0x00, 0x00, 0x29, 0x19, 0x00, 0x00, + 0x8E, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF1, 0x0F, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x2A, 0x19, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF3, 0x0F, 0x00, 0x00, + 0xF1, 0x0F, 0x00, 0x00, 0x76, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF5, 0x0F, 0x00, 0x00, 0xF3, 0x0F, 0x00, 0x00, + 0x91, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF7, 0x0F, 0x00, 0x00, 0xF5, 0x0F, 0x00, 0x00, 0xAC, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF9, 0x0F, 0x00, 0x00, + 0xF7, 0x0F, 0x00, 0x00, 0xC7, 0x0F, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x39, 0x17, 0x00, 0x00, 0xF9, 0x0F, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3A, 0x17, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x39, 0x17, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3B, 0x17, 0x00, 0x00, 0x3A, 0x17, 0x00, 0x00, + 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3E, 0x17, 0x00, 0x00, + 0x3B, 0x17, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x40, 0x17, 0x00, 0x00, 0x3E, 0x17, 0x00, 0x00, 0xF9, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x42, 0x17, 0x00, 0x00, + 0x40, 0x17, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x43, 0x17, 0x00, 0x00, 0x3B, 0x17, 0x00, 0x00, + 0x42, 0x17, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2B, 0x19, 0x00, 0x00, 0xEF, 0x12, 0x00, 0x00, 0x0D, 0x13, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, + 0x58, 0x0F, 0x00, 0x00, 0x2B, 0x19, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0xF9, 0x12, 0x00, 0x00, + 0x61, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x08, 0x10, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x10, 0x00, 0x00, + 0x2B, 0x13, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0D, 0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, + 0x0C, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x11, 0x10, 0x00, 0x00, 0x35, 0x13, 0x00, 0x00, 0x73, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x10, 0x00, 0x00, + 0x0D, 0x10, 0x00, 0x00, 0x11, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0x67, 0x13, 0x00, 0x00, + 0x73, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x17, 0x10, 0x00, 0x00, 0x12, 0x10, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1B, 0x10, 0x00, 0x00, + 0x53, 0x13, 0x00, 0x00, 0x8E, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1C, 0x10, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, + 0x1B, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x71, 0x13, 0x00, 0x00, 0x8E, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x21, 0x10, 0x00, 0x00, + 0x1C, 0x10, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x25, 0x10, 0x00, 0x00, 0x17, 0x13, 0x00, 0x00, + 0x76, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x26, 0x10, 0x00, 0x00, 0x21, 0x10, 0x00, 0x00, 0x25, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x10, 0x00, 0x00, + 0x21, 0x13, 0x00, 0x00, 0x91, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x10, 0x00, 0x00, 0x26, 0x10, 0x00, 0x00, + 0x2A, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2F, 0x10, 0x00, 0x00, 0x3F, 0x13, 0x00, 0x00, 0xAC, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, + 0x2B, 0x10, 0x00, 0x00, 0x2F, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x10, 0x00, 0x00, 0x49, 0x13, 0x00, 0x00, + 0xC7, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x35, 0x10, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x34, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, + 0x35, 0x10, 0x00, 0x00, 0x43, 0x17, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x51, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x37, 0x10, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2C, 0x19, 0x00, 0x00, 0xF2, 0x12, 0x00, 0x00, 0x10, 0x13, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x10, 0x00, 0x00, + 0x58, 0x0F, 0x00, 0x00, 0x2C, 0x19, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x45, 0x10, 0x00, 0x00, 0xFC, 0x12, 0x00, 0x00, + 0x61, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x46, 0x10, 0x00, 0x00, 0x41, 0x10, 0x00, 0x00, 0x45, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x10, 0x00, 0x00, + 0x2E, 0x13, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4B, 0x10, 0x00, 0x00, 0x46, 0x10, 0x00, 0x00, + 0x4A, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4F, 0x10, 0x00, 0x00, 0x38, 0x13, 0x00, 0x00, 0x73, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x10, 0x00, 0x00, + 0x4B, 0x10, 0x00, 0x00, 0x4F, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x54, 0x10, 0x00, 0x00, 0x6A, 0x13, 0x00, 0x00, + 0x73, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x55, 0x10, 0x00, 0x00, 0x50, 0x10, 0x00, 0x00, 0x54, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x59, 0x10, 0x00, 0x00, + 0x56, 0x13, 0x00, 0x00, 0x8E, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5A, 0x10, 0x00, 0x00, 0x55, 0x10, 0x00, 0x00, + 0x59, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5E, 0x10, 0x00, 0x00, 0x74, 0x13, 0x00, 0x00, 0x8E, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5F, 0x10, 0x00, 0x00, + 0x5A, 0x10, 0x00, 0x00, 0x5E, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x63, 0x10, 0x00, 0x00, 0x1A, 0x13, 0x00, 0x00, + 0x76, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x64, 0x10, 0x00, 0x00, 0x5F, 0x10, 0x00, 0x00, 0x63, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x68, 0x10, 0x00, 0x00, + 0x24, 0x13, 0x00, 0x00, 0x91, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x69, 0x10, 0x00, 0x00, 0x64, 0x10, 0x00, 0x00, + 0x68, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6D, 0x10, 0x00, 0x00, 0x42, 0x13, 0x00, 0x00, 0xAC, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6E, 0x10, 0x00, 0x00, + 0x69, 0x10, 0x00, 0x00, 0x6D, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x72, 0x10, 0x00, 0x00, 0x4C, 0x13, 0x00, 0x00, + 0xC7, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x73, 0x10, 0x00, 0x00, 0x6E, 0x10, 0x00, 0x00, 0x72, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x75, 0x10, 0x00, 0x00, + 0x73, 0x10, 0x00, 0x00, 0x43, 0x17, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5F, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x75, 0x10, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2D, 0x19, 0x00, 0x00, 0xF5, 0x12, 0x00, 0x00, 0x13, 0x13, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7F, 0x10, 0x00, 0x00, + 0x58, 0x0F, 0x00, 0x00, 0x2D, 0x19, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, 0xFF, 0x12, 0x00, 0x00, + 0x61, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x84, 0x10, 0x00, 0x00, 0x7F, 0x10, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x88, 0x10, 0x00, 0x00, + 0x31, 0x13, 0x00, 0x00, 0x61, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x89, 0x10, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00, + 0x88, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x8D, 0x10, 0x00, 0x00, 0x3B, 0x13, 0x00, 0x00, 0x73, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8E, 0x10, 0x00, 0x00, + 0x89, 0x10, 0x00, 0x00, 0x8D, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x92, 0x10, 0x00, 0x00, 0x6D, 0x13, 0x00, 0x00, + 0x73, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x93, 0x10, 0x00, 0x00, 0x8E, 0x10, 0x00, 0x00, 0x92, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x97, 0x10, 0x00, 0x00, + 0x59, 0x13, 0x00, 0x00, 0x8E, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x98, 0x10, 0x00, 0x00, 0x93, 0x10, 0x00, 0x00, + 0x97, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9C, 0x10, 0x00, 0x00, 0x77, 0x13, 0x00, 0x00, 0x8E, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9D, 0x10, 0x00, 0x00, + 0x98, 0x10, 0x00, 0x00, 0x9C, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA1, 0x10, 0x00, 0x00, 0x1D, 0x13, 0x00, 0x00, + 0x76, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA2, 0x10, 0x00, 0x00, 0x9D, 0x10, 0x00, 0x00, 0xA1, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA6, 0x10, 0x00, 0x00, + 0x27, 0x13, 0x00, 0x00, 0x91, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA7, 0x10, 0x00, 0x00, 0xA2, 0x10, 0x00, 0x00, + 0xA6, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xAB, 0x10, 0x00, 0x00, 0x45, 0x13, 0x00, 0x00, 0xAC, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAC, 0x10, 0x00, 0x00, + 0xA7, 0x10, 0x00, 0x00, 0xAB, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB0, 0x10, 0x00, 0x00, 0x4F, 0x13, 0x00, 0x00, + 0xC7, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB1, 0x10, 0x00, 0x00, 0xAC, 0x10, 0x00, 0x00, 0xB0, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB3, 0x10, 0x00, 0x00, + 0xB1, 0x10, 0x00, 0x00, 0x43, 0x17, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6D, 0x17, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0xB3, 0x10, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xC6, 0x08, 0x00, 0x00, + 0xC7, 0x08, 0x00, 0x00, 0xB8, 0x08, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0xC7, 0x08, 0x00, 0x00, 0x51, 0x17, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0xC6, 0x08, 0x00, 0x00, 0xC9, 0x08, 0x00, 0x00, + 0xB8, 0x08, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0xC9, 0x08, 0x00, 0x00, 0x5F, 0x17, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xC6, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, 0xB8, 0x08, 0x00, 0x00, + 0xEA, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xCB, 0x08, 0x00, 0x00, + 0x6D, 0x17, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xCC, 0x08, 0x00, 0x00, 0xB8, 0x08, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0xCD, 0x08, 0x00, 0x00, 0xCC, 0x08, 0x00, 0x00, + 0xCC, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x2C, 0x00, 0x00, 0x00, + 0xCE, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0xCD, 0x08, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xCF, 0x08, 0x00, 0x00, 0xB8, 0x08, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0xCF, 0x08, 0x00, 0x00, + 0xCE, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0xB8, 0x08, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0xC6, 0x08, 0x00, 0x00, 0xD2, 0x08, 0x00, 0x00, 0xB8, 0x08, 0x00, 0x00, + 0xD1, 0x08, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD2, 0x08, 0x00, 0x00, + 0x5F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..1edd8d2aa19384dcfd538d57ff35438a9588c9bc GIT binary patch literal 8844 zcmZvheUO!98OC2&77!Li5eQH|R6tlnaO69XkCru~R-+tgoX%{!hh+nH*F9&=CPD&q za7+mt!$v?rlq|wZp=Mylf<`Jf5}Hb<$v>iLl7AE&MwzLh>v#6K>YjIcXP!Oxecji6 zU(d(;JbT{vRQ0~OUzYXC24wxSsUxz=b#2yLNR|!E`c^*cmn~m5wN!4NdgqL35U$Ru zDm*yXWc{)}`lPLGY;7a3vwxQTTy3y;;1ZBkrK(xBUrBwWR==!J-_TIMtgg`1(NQRs zzrLneSku~8EHt-v7Mseg4;SI}54_g4rZrv7kx(s0)UD2{v+dG&q*y2yA1QZrsz$ZO zKz~}+6qa^2wk&CH?`#%Dc>ipV@CS-Z8cWMpHFkCs+m^JqmCB86<&xrkv#Tm`xT@FG zSSqx4l{>o1h4z&zOT{vHUyXwr%tPXosMJ<0l?om0rE)>Ut`2EcW&6bAqCd=y_$$?) zLR0&i_DPthm&+#&e0^hpiJ=OmwX zt5qECgKAv%3l(p}UHIg-sG(47ByW9M`%sA z$^fHYyzGxs&%dJpys^^XmPlaplFvGEp~u*tt$01--65O$9v|^TYFBhk(V8(|ht

4Q1PWP`ymc0Bpy8taw~FJfmC58S-B6DMNt3d5&)?#{{PvVUH-*Y0r{j1AxY zJ|+j>I=_|!7W?yV^1;n}FY#i3-WP@skNRj`Zw#(hbHdoXcb`)BgG$K4lv_7)iYHo9qd534%O?+foj9|E9gq!<&p!QuZ03WyjQ>Y!)MbsR%h(PjoE-M*tMb7v zYdqZpdd9_PhvJN*RwQ)pQ%(3&^#>g;W49-aG2Sa4eVZl>E-|#IHC;CNW8&c7A)ET( zt~En8^%edvHZx(&yX(&q2A7!Y&z24TtyF)GY&5`YG|>nr{sgd&F8O5`JBVnKa#T<=NQ+?QO&o=RBM-I}nd zds%tp)2#6?VdT?{T@gk;%^EKe&*>gWSk!$bVNrL`5S`P)D>c#_3>HRyrAESr2qT|n z-Ji;4AII^&p0KEUBwDl*6o(fzK^=66Bc#fN?6pbRXy@))|e`ce44T8!pNst_Z``s7g6^w z35&XaO<2^Op?c)gtT9^{`7~qmgpp6P?muL6&P3gRCM@cnPgvBwOZCX7S!1y<@@dBE zgpp6P?uW8DU!v}%ghkzt5*Bqot$O6stg%cO`7~npM*u- z<*G+M%^LR$BcEn$g)s7I)*UIE^O(!n4GD|7qZ1Z&TU3vHnl-*EjC`80wZh1!S!1GX z&e5nlDPd8!HepfsA=M+FW{t8i@@dAtCX9SqjlO4a#y%^I`s39&YuNXWeK*87ci}p= zGsM`l+)s|}3NdQHb?l84yY>I*{UXHBgX_FELoBa%GQ_9>*Lm-y7~^AX93GV|<(?aUB+f z7=3~3yibJ~W8*$`tS-c;0oSpX6k~jOy;UKG9$e?Og;-v%J;bO1*LmAgjPWtHxDMMx zjCFwH8mDCwXU?rLVA%iWinukrhO-rCK98sSYp#4S-nH?J z$36E+`QW^7^SaKL5AHgvWiw~4vlhNutkW*Pf`>XC!oXc;w`}Icb@s>yi*@$N2ao-E zQ9iiqoRQ7gUFQ$7!D5|1$_I~i{v;pVrTwP8xxPUH*a*}5p!S0$R?kdu?e!N6|vjo!;86hBp#SG zX3A!-vM+i6!Ty^ijQ4TGJl|I|F7S=%p3zI+%2@#Jx$Gqj-2Iv>KK%u^-U9jTTlT2+ z?h=L`G3yOb4!HFO%IE9>w_c5GaO*7=pSb|H-ctGOVfL%_>V=_4%z8tW18%)x^3emg z-f-FA*1Jc1<{8|2pOepiX75_BK^S_(tT$FU;MN-_A3bpE-6R{_dY>1cH3T>IfPC&* z=Gs`JZ0=d+!&pf+_bh#Ozsj=V5;OL&Z17m`Y5CmS=$p4yHrm9DZIca-uCap&i@GO; zp%Lrds^6rjM}M3zS-(>mV=z44yWN*>j5p(W`!oV4iu}iYS>8G)eWP{UBW0z%v z(@(G09L+hn*K4l)b(coCi2D!gGDyK zaEY0ByKJ79V;<47mjMWL_IU=s%Qekk3nRkzDo+%>lGs55!Gp|85-|6F8-zy9*G4sA4 zo9Bhd`=T(o#LQbEoA1JL4ZkD|E-~}IESvA=kyj7~mza4?vU#qEYuzjiE-~{~$*w#P zgtciE2A7z5Yi08tKdyC~Fu26b>y*uNM_lWYFu26b>yix~*SZkans|P#Z_|46{*rfk zuJfpPtTi!Xn}qScF|OfeVQ`6=w?+1J)r!0)gux|d-cz!9&lK12X<=}QnYUdw?<*qj z8DVgVnYTkW?^EI$J|_$=G4poH<~>B@?Ggr;n0b3;^S&{z^*&*6iJAAZ?1|#VeSJU} zTw><^P&V(k;#waR2A7z5KbFn=sJPZY5eAo-d9TR^k88a)tTl13^&)*|;&;N6YW#M1 zPWyg=enG_NEO7icDb9CT;`rP@j-N{L^C^yxrhiN9PVojkM@JG);k_`r*-!iM!^mV548z}vR3&;IDD;td8L5bt@efQr| Ho2~XA&SG+5 literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.txt new file mode 100644 index 000000000..71d71da9a --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.txt @@ -0,0 +1,443 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 6446 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %xe_frag_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 420 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %xe_texture "xe_texture" + OpName %gl_FragCoord "gl_FragCoord" + OpName %XeCasSharpenConstants "XeCasSharpenConstants" + OpMemberName %XeCasSharpenConstants 0 "xe_cas_output_offset" + OpMemberName %XeCasSharpenConstants 1 "xe_cas_sharpness_post_setup" + OpName %_ "" + OpName %xe_frag_color "xe_frag_color" + OpDecorate %xe_texture DescriptorSet 0 + OpDecorate %xe_texture Binding 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpMemberDecorate %XeCasSharpenConstants 0 Offset 16 + OpMemberDecorate %XeCasSharpenConstants 1 Offset 24 + OpDecorate %XeCasSharpenConstants Block + OpDecorate %xe_frag_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %v3float = OpTypeVector %float 3 + %v2uint = OpTypeVector %uint 2 + %v4uint = OpTypeVector %uint 4 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 +%uint_532432441 = OpConstant %uint 532432441 +%uint_2129690299 = OpConstant %uint 2129690299 +%uint_2129764351 = OpConstant %uint 2129764351 + %float_2 = OpConstant %float 2 + %143 = OpTypeImage %float 2D 0 0 0 1 Unknown + %144 = OpTypeSampledImage %143 +%_ptr_UniformConstant_144 = OpTypePointer UniformConstant %144 + %xe_texture = OpVariable %_ptr_UniformConstant_144 UniformConstant + %int_0 = OpConstant %int 0 + %v4float = OpTypeVector %float 4 + %int_n1 = OpConstant %int -1 + %181 = OpConstantComposite %v2int %int_0 %int_n1 + %int_1 = OpConstant %int 1 + %188 = OpConstantComposite %v2int %int_1 %int_n1 + %194 = OpConstantComposite %v2int %int_n1 %int_0 + %204 = OpConstantComposite %v2int %int_1 %int_0 + %210 = OpConstantComposite %v2int %int_n1 %int_1 + %216 = OpConstantComposite %v2int %int_0 %int_1 + %222 = OpConstantComposite %v2int %int_1 %int_1 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %v2float = OpTypeVector %float 2 + %int_2 = OpConstant %int 2 + %712 = OpConstantComposite %v2int %int_2 %int_0 + %734 = OpConstantComposite %v2int %int_0 %int_2 + %745 = OpConstantComposite %v2int %int_2 %int_1 + %751 = OpConstantComposite %v2int %int_1 %int_2 +%float_0_03125 = OpConstant %float 0.03125 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%XeCasSharpenConstants = OpTypeStruct %v2int %float +%_ptr_PushConstant_XeCasSharpenConstants = OpTypePointer PushConstant %XeCasSharpenConstants + %_ = OpVariable %_ptr_PushConstant_XeCasSharpenConstants PushConstant +%_ptr_PushConstant_v2int = OpTypePointer PushConstant %v2int + %2217 = OpConstantComposite %v4float %float_1 %float_1 %float_0 %float_0 +%_ptr_PushConstant_float = OpTypePointer PushConstant %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%xe_frag_color = OpVariable %_ptr_Output_v4float Output +%_ptr_Output_float = OpTypePointer Output %float + %uint_3 = OpConstant %uint 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %2205 = OpLoad %v4float %gl_FragCoord + %2206 = OpVectorShuffle %v2float %2205 %2205 0 1 + %2207 = OpConvertFToS %v2int %2206 + %2212 = OpAccessChain %_ptr_PushConstant_v2int %_ %int_0 + %2213 = OpLoad %v2int %2212 + %2214 = OpISub %v2int %2207 %2213 + %2215 = OpBitcast %v2uint %2214 + %2218 = OpBitcast %v4uint %2217 + %2221 = OpAccessChain %_ptr_PushConstant_float %_ %int_1 + %2222 = OpLoad %float %2221 + %2223 = OpBitcast %uint %2222 + OpBranch %2764 + %2764 = OpLabel + %3115 = OpConvertUToF %v2float %2215 + %3118 = OpCompositeExtract %uint %2218 0 + %3119 = OpCompositeExtract %uint %2218 1 + %3120 = OpCompositeConstruct %v2uint %3118 %3119 + %3121 = OpBitcast %v2float %3120 + %3122 = OpFMul %v2float %3115 %3121 + %3125 = OpCompositeExtract %uint %2218 2 + %3126 = OpCompositeExtract %uint %2218 3 + %3127 = OpCompositeConstruct %v2uint %3125 %3126 + %3128 = OpBitcast %v2float %3127 + %3129 = OpFAdd %v2float %3122 %3128 + %3131 = OpExtInst %v2float %1 Floor %3129 + %3134 = OpFSub %v2float %3129 %3131 + %3136 = OpConvertFToS %v2int %3131 + %3141 = OpIAdd %v2int %3136 %181 + %4731 = OpLoad %144 %xe_texture + %4733 = OpImage %143 %4731 + %4734 = OpImageFetch %v4float %4733 %3141 Lod %int_0 + %3144 = OpIAdd %v2int %3136 %194 + %4740 = OpImage %143 %4731 + %4741 = OpImageFetch %v4float %4740 %3144 Lod %int_0 + %4747 = OpImage %143 %4731 + %4748 = OpImageFetch %v4float %4747 %3136 Lod %int_0 + %3149 = OpIAdd %v2int %3136 %188 + %4754 = OpImage %143 %4731 + %4755 = OpImageFetch %v4float %4754 %3149 Lod %int_0 + %3155 = OpIAdd %v2int %3136 %204 + %4768 = OpImage %143 %4731 + %4769 = OpImageFetch %v4float %4768 %3155 Lod %int_0 + %3158 = OpIAdd %v2int %3136 %712 + %4775 = OpImage %143 %4731 + %4776 = OpImageFetch %v4float %4775 %3158 Lod %int_0 + %3161 = OpIAdd %v2int %3136 %210 + %4782 = OpImage %143 %4731 + %4783 = OpImageFetch %v4float %4782 %3161 Lod %int_0 + %3164 = OpIAdd %v2int %3136 %216 + %4789 = OpImage %143 %4731 + %4790 = OpImageFetch %v4float %4789 %3164 Lod %int_0 + %3170 = OpIAdd %v2int %3136 %734 + %4803 = OpImage %143 %4731 + %4804 = OpImageFetch %v4float %4803 %3170 Lod %int_0 + %3173 = OpIAdd %v2int %3136 %222 + %4810 = OpImage %143 %4731 + %4811 = OpImageFetch %v4float %4810 %3173 Lod %int_0 + %3176 = OpIAdd %v2int %3136 %745 + %4817 = OpImage %143 %4731 + %4818 = OpImageFetch %v4float %4817 %3176 Lod %int_0 + %3179 = OpIAdd %v2int %3136 %751 + %4824 = OpImage %143 %4731 + %4825 = OpImageFetch %v4float %4824 %3179 Lod %int_0 + %3198 = OpCompositeExtract %float %4734 0 + %3200 = OpCompositeExtract %float %4734 1 + %3202 = OpCompositeExtract %float %4734 2 + %4847 = OpFMul %float %3198 %3198 + %4850 = OpFMul %float %3200 %3200 + %4853 = OpFMul %float %3202 %3202 + %3211 = OpCompositeExtract %float %4755 0 + %3213 = OpCompositeExtract %float %4755 1 + %3215 = OpCompositeExtract %float %4755 2 + %4857 = OpFMul %float %3211 %3211 + %4860 = OpFMul %float %3213 %3213 + %4863 = OpFMul %float %3215 %3215 + %3237 = OpCompositeExtract %float %4741 0 + %3239 = OpCompositeExtract %float %4741 1 + %3241 = OpCompositeExtract %float %4741 2 + %4877 = OpFMul %float %3237 %3237 + %4880 = OpFMul %float %3239 %3239 + %4883 = OpFMul %float %3241 %3241 + %3250 = OpCompositeExtract %float %4748 0 + %3252 = OpCompositeExtract %float %4748 1 + %3254 = OpCompositeExtract %float %4748 2 + %4887 = OpFMul %float %3250 %3250 + %4890 = OpFMul %float %3252 %3252 + %4893 = OpFMul %float %3254 %3254 + %3263 = OpCompositeExtract %float %4769 0 + %3265 = OpCompositeExtract %float %4769 1 + %3267 = OpCompositeExtract %float %4769 2 + %4897 = OpFMul %float %3263 %3263 + %4900 = OpFMul %float %3265 %3265 + %4903 = OpFMul %float %3267 %3267 + %3276 = OpCompositeExtract %float %4776 0 + %3278 = OpCompositeExtract %float %4776 1 + %3280 = OpCompositeExtract %float %4776 2 + %4907 = OpFMul %float %3276 %3276 + %4910 = OpFMul %float %3278 %3278 + %4913 = OpFMul %float %3280 %3280 + %3289 = OpCompositeExtract %float %4783 0 + %3291 = OpCompositeExtract %float %4783 1 + %3293 = OpCompositeExtract %float %4783 2 + %4917 = OpFMul %float %3289 %3289 + %4920 = OpFMul %float %3291 %3291 + %4923 = OpFMul %float %3293 %3293 + %3302 = OpCompositeExtract %float %4790 0 + %3304 = OpCompositeExtract %float %4790 1 + %3306 = OpCompositeExtract %float %4790 2 + %4927 = OpFMul %float %3302 %3302 + %4930 = OpFMul %float %3304 %3304 + %4933 = OpFMul %float %3306 %3306 + %3315 = OpCompositeExtract %float %4811 0 + %3317 = OpCompositeExtract %float %4811 1 + %3319 = OpCompositeExtract %float %4811 2 + %4937 = OpFMul %float %3315 %3315 + %4940 = OpFMul %float %3317 %3317 + %4943 = OpFMul %float %3319 %3319 + %3328 = OpCompositeExtract %float %4818 0 + %3330 = OpCompositeExtract %float %4818 1 + %3332 = OpCompositeExtract %float %4818 2 + %4947 = OpFMul %float %3328 %3328 + %4950 = OpFMul %float %3330 %3330 + %4953 = OpFMul %float %3332 %3332 + %3354 = OpCompositeExtract %float %4804 0 + %3356 = OpCompositeExtract %float %4804 1 + %3358 = OpCompositeExtract %float %4804 2 + %4967 = OpFMul %float %3354 %3354 + %4970 = OpFMul %float %3356 %3356 + %4973 = OpFMul %float %3358 %3358 + %3367 = OpCompositeExtract %float %4825 0 + %3369 = OpCompositeExtract %float %4825 1 + %3371 = OpCompositeExtract %float %4825 2 + %4977 = OpFMul %float %3367 %3367 + %4980 = OpFMul %float %3369 %3369 + %4983 = OpFMul %float %3371 %3371 + %5013 = OpExtInst %float %1 FMin %4880 %4890 + %5014 = OpExtInst %float %1 FMin %4850 %5013 + %5020 = OpExtInst %float %1 FMin %4900 %4930 + %5021 = OpExtInst %float %1 FMin %5014 %5020 + %5055 = OpExtInst %float %1 FMax %4880 %4890 + %5056 = OpExtInst %float %1 FMax %4850 %5055 + %5062 = OpExtInst %float %1 FMax %4900 %4930 + %5063 = OpExtInst %float %1 FMax %5056 %5062 + %5097 = OpExtInst %float %1 FMin %4890 %4900 + %5098 = OpExtInst %float %1 FMin %4860 %5097 + %5104 = OpExtInst %float %1 FMin %4910 %4940 + %5105 = OpExtInst %float %1 FMin %5098 %5104 + %5139 = OpExtInst %float %1 FMax %4890 %4900 + %5140 = OpExtInst %float %1 FMax %4860 %5139 + %5146 = OpExtInst %float %1 FMax %4910 %4940 + %5147 = OpExtInst %float %1 FMax %5140 %5146 + %5181 = OpExtInst %float %1 FMin %4920 %4930 + %5182 = OpExtInst %float %1 FMin %4890 %5181 + %5188 = OpExtInst %float %1 FMin %4940 %4970 + %5189 = OpExtInst %float %1 FMin %5182 %5188 + %5223 = OpExtInst %float %1 FMax %4920 %4930 + %5224 = OpExtInst %float %1 FMax %4890 %5223 + %5230 = OpExtInst %float %1 FMax %4940 %4970 + %5231 = OpExtInst %float %1 FMax %5224 %5230 + %5265 = OpExtInst %float %1 FMin %4930 %4940 + %5266 = OpExtInst %float %1 FMin %4900 %5265 + %5272 = OpExtInst %float %1 FMin %4950 %4980 + %5273 = OpExtInst %float %1 FMin %5266 %5272 + %5307 = OpExtInst %float %1 FMax %4930 %4940 + %5308 = OpExtInst %float %1 FMax %4900 %5307 + %5314 = OpExtInst %float %1 FMax %4950 %4980 + %5315 = OpExtInst %float %1 FMax %5308 %5314 + %5346 = OpBitcast %uint %5063 + %5347 = OpISub %uint %uint_2129690299 %5346 + %5348 = OpBitcast %float %5347 + %5379 = OpBitcast %uint %5147 + %5380 = OpISub %uint %uint_2129690299 %5379 + %5381 = OpBitcast %float %5380 + %5412 = OpBitcast %uint %5231 + %5413 = OpISub %uint %uint_2129690299 %5412 + %5414 = OpBitcast %float %5413 + %5445 = OpBitcast %uint %5315 + %5446 = OpISub %uint %uint_2129690299 %5445 + %5447 = OpBitcast %float %5446 + %3715 = OpFSub %float %float_1 %5063 + %3716 = OpExtInst %float %1 FMin %5021 %3715 + %3718 = OpFMul %float %3716 %5348 + %5489 = OpExtInst %float %1 FClamp %3718 %float_0 %float_1 + %3739 = OpFSub %float %float_1 %5147 + %3740 = OpExtInst %float %1 FMin %5105 %3739 + %3742 = OpFMul %float %3740 %5381 + %5540 = OpExtInst %float %1 FClamp %3742 %float_0 %float_1 + %3763 = OpFSub %float %float_1 %5231 + %3764 = OpExtInst %float %1 FMin %5189 %3763 + %3766 = OpFMul %float %3764 %5414 + %5591 = OpExtInst %float %1 FClamp %3766 %float_0 %float_1 + %3787 = OpFSub %float %float_1 %5315 + %3788 = OpExtInst %float %1 FMin %5273 %3787 + %3790 = OpFMul %float %3788 %5447 + %5642 = OpExtInst %float %1 FClamp %3790 %float_0 %float_1 + %5688 = OpBitcast %uint %5489 + %5690 = OpShiftRightLogical %uint %5688 %uint_1 + %5692 = OpIAdd %uint %5690 %uint_532432441 + %5693 = OpBitcast %float %5692 + %5739 = OpBitcast %uint %5540 + %5741 = OpShiftRightLogical %uint %5739 %uint_1 + %5743 = OpIAdd %uint %5741 %uint_532432441 + %5744 = OpBitcast %float %5743 + %5790 = OpBitcast %uint %5591 + %5792 = OpShiftRightLogical %uint %5790 %uint_1 + %5794 = OpIAdd %uint %5792 %uint_532432441 + %5795 = OpBitcast %float %5794 + %5841 = OpBitcast %uint %5642 + %5843 = OpShiftRightLogical %uint %5841 %uint_1 + %5845 = OpIAdd %uint %5843 %uint_532432441 + %5846 = OpBitcast %float %5845 + %3826 = OpBitcast %float %2223 + %3832 = OpFMul %float %5693 %3826 + %3841 = OpFMul %float %5744 %3826 + %3850 = OpFMul %float %5795 %3826 + %3859 = OpFMul %float %5846 %3826 + %3865 = OpCompositeExtract %float %3134 0 + %3866 = OpFSub %float %float_1 %3865 + %3869 = OpCompositeExtract %float %3134 1 + %3870 = OpFSub %float %float_1 %3869 + %3871 = OpFMul %float %3866 %3870 + %3878 = OpFMul %float %3865 %3870 + %3885 = OpFMul %float %3866 %3869 + %3890 = OpFMul %float %3865 %3869 + %3894 = OpFSub %float %5063 %5021 + %3895 = OpFAdd %float %float_0_03125 %3894 + %5887 = OpBitcast %uint %3895 + %5888 = OpISub %uint %uint_2129690299 %5887 + %5889 = OpBitcast %float %5888 + %3898 = OpFMul %float %3871 %5889 + %3902 = OpFSub %float %5147 %5105 + %3903 = OpFAdd %float %float_0_03125 %3902 + %5898 = OpBitcast %uint %3903 + %5899 = OpISub %uint %uint_2129690299 %5898 + %5900 = OpBitcast %float %5899 + %3906 = OpFMul %float %3878 %5900 + %3910 = OpFSub %float %5231 %5189 + %3911 = OpFAdd %float %float_0_03125 %3910 + %5909 = OpBitcast %uint %3911 + %5910 = OpISub %uint %uint_2129690299 %5909 + %5911 = OpBitcast %float %5910 + %3914 = OpFMul %float %3885 %5911 + %3918 = OpFSub %float %5315 %5273 + %3919 = OpFAdd %float %float_0_03125 %3918 + %5920 = OpBitcast %uint %3919 + %5921 = OpISub %uint %uint_2129690299 %5920 + %5922 = OpBitcast %float %5921 + %3922 = OpFMul %float %3890 %5922 + %3928 = OpFMul %float %3832 %3898 + %3937 = OpFMul %float %3841 %3906 + %3955 = OpFMul %float %3850 %3914 + %3956 = OpFAdd %float %3937 %3955 + %3958 = OpFAdd %float %3956 %3898 + %3982 = OpFMul %float %3859 %3922 + %3983 = OpFAdd %float %3928 %3982 + %3985 = OpFAdd %float %3983 %3906 + %4012 = OpFAdd %float %3983 %3914 + %4039 = OpFAdd %float %3956 %3922 + %6440 = OpFAdd %float %3928 %3937 + %6441 = OpFAdd %float %6440 %3955 + %6442 = OpFAdd %float %6441 %3982 + %4081 = OpFMul %float %float_2 %6442 + %4083 = OpFAdd %float %4081 %3958 + %4085 = OpFAdd %float %4083 %3985 + %4087 = OpFAdd %float %4085 %4012 + %4089 = OpFAdd %float %4087 %4039 + %5945 = OpBitcast %uint %4089 + %5946 = OpISub %uint %uint_2129764351 %5945 + %5947 = OpBitcast %float %5946 + %5950 = OpFNegate %float %5947 + %5952 = OpFMul %float %5950 %4089 + %5954 = OpFAdd %float %5952 %float_2 + %5955 = OpFMul %float %5947 %5954 + %6443 = OpFAdd %float %4847 %4877 + %4099 = OpFMul %float %3928 %6443 + %4103 = OpFMul %float %4857 %3937 + %4104 = OpFAdd %float %4099 %4103 + %4108 = OpFMul %float %4907 %3937 + %4109 = OpFAdd %float %4104 %4108 + %4113 = OpFMul %float %4917 %3955 + %4114 = OpFAdd %float %4109 %4113 + %4118 = OpFMul %float %4967 %3955 + %4119 = OpFAdd %float %4114 %4118 + %4123 = OpFMul %float %4947 %3982 + %4124 = OpFAdd %float %4119 %4123 + %4128 = OpFMul %float %4977 %3982 + %4129 = OpFAdd %float %4124 %4128 + %4133 = OpFMul %float %4887 %3958 + %4134 = OpFAdd %float %4129 %4133 + %4138 = OpFMul %float %4897 %3985 + %4139 = OpFAdd %float %4134 %4138 + %4143 = OpFMul %float %4927 %4012 + %4144 = OpFAdd %float %4139 %4143 + %4148 = OpFMul %float %4937 %4039 + %4149 = OpFAdd %float %4144 %4148 + %4151 = OpFMul %float %4149 %5955 + %5969 = OpExtInst %float %1 FClamp %4151 %float_0 %float_1 + %6444 = OpFAdd %float %4850 %4880 + %4161 = OpFMul %float %3928 %6444 + %4165 = OpFMul %float %4860 %3937 + %4166 = OpFAdd %float %4161 %4165 + %4170 = OpFMul %float %4910 %3937 + %4171 = OpFAdd %float %4166 %4170 + %4175 = OpFMul %float %4920 %3955 + %4176 = OpFAdd %float %4171 %4175 + %4180 = OpFMul %float %4970 %3955 + %4181 = OpFAdd %float %4176 %4180 + %4185 = OpFMul %float %4950 %3982 + %4186 = OpFAdd %float %4181 %4185 + %4190 = OpFMul %float %4980 %3982 + %4191 = OpFAdd %float %4186 %4190 + %4195 = OpFMul %float %4890 %3958 + %4196 = OpFAdd %float %4191 %4195 + %4200 = OpFMul %float %4900 %3985 + %4201 = OpFAdd %float %4196 %4200 + %4205 = OpFMul %float %4930 %4012 + %4206 = OpFAdd %float %4201 %4205 + %4210 = OpFMul %float %4940 %4039 + %4211 = OpFAdd %float %4206 %4210 + %4213 = OpFMul %float %4211 %5955 + %5983 = OpExtInst %float %1 FClamp %4213 %float_0 %float_1 + %6445 = OpFAdd %float %4853 %4883 + %4223 = OpFMul %float %3928 %6445 + %4227 = OpFMul %float %4863 %3937 + %4228 = OpFAdd %float %4223 %4227 + %4232 = OpFMul %float %4913 %3937 + %4233 = OpFAdd %float %4228 %4232 + %4237 = OpFMul %float %4923 %3955 + %4238 = OpFAdd %float %4233 %4237 + %4242 = OpFMul %float %4973 %3955 + %4243 = OpFAdd %float %4238 %4242 + %4247 = OpFMul %float %4953 %3982 + %4248 = OpFAdd %float %4243 %4247 + %4252 = OpFMul %float %4983 %3982 + %4253 = OpFAdd %float %4248 %4252 + %4257 = OpFMul %float %4893 %3958 + %4258 = OpFAdd %float %4253 %4257 + %4262 = OpFMul %float %4903 %3985 + %4263 = OpFAdd %float %4258 %4262 + %4267 = OpFMul %float %4933 %4012 + %4268 = OpFAdd %float %4263 %4267 + %4272 = OpFMul %float %4943 %4039 + %4273 = OpFAdd %float %4268 %4272 + %4275 = OpFMul %float %4273 %5955 + %5997 = OpExtInst %float %1 FClamp %4275 %float_0 %float_1 + %2247 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_0 + OpStore %2247 %5969 + %2249 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_1 + OpStore %2249 %5983 + %2251 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_2 + OpStore %2251 %5997 + %2252 = OpLoad %v4float %xe_frag_color + %2253 = OpVectorShuffle %v3float %2252 %2252 0 1 2 + %2254 = OpExtInst %v3float %1 Sqrt %2253 + %2255 = OpLoad %v4float %xe_frag_color + %2256 = OpVectorShuffle %v4float %2255 %2254 4 5 6 3 + OpStore %xe_frag_color %2256 + %2258 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_3 + OpStore %2258 %float_1 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.h new file mode 100644 index 000000000..c32ba006b --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.h @@ -0,0 +1,1332 @@ +// generated from `xb genspirv` +// source: guest_output_ffx_fsr_easu.frag +const uint8_t guest_output_ffx_fsr_easu_frag[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x5A, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x05, 0x00, 0x00, 0x0A, 0x05, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0A, 0x00, + 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70, + 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, + 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, + 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, + 0xDE, 0x04, 0x00, 0x00, 0x58, 0x65, 0x46, 0x73, 0x72, 0x45, 0x61, 0x73, + 0x75, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, + 0x06, 0x00, 0x0C, 0x00, 0xDE, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x66, 0x73, 0x72, 0x5F, 0x65, 0x61, 0x73, 0x75, 0x5F, + 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5F, 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x00, + 0x06, 0x00, 0x0A, 0x00, 0xDE, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x66, 0x73, 0x72, 0x5F, 0x65, 0x61, 0x73, 0x75, 0x5F, + 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x69, + 0x6E, 0x76, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xE0, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x05, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0A, 0x05, 0x00, 0x00, + 0x67, 0x6C, 0x5F, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6F, 0x6F, 0x72, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0xBC, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0xDE, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0xDE, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0xDE, 0x04, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x05, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0A, 0x05, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0xBB, 0x7E, 0xF0, 0x7E, 0x2B, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xAC, 0x00, 0x00, 0x00, 0x74, 0x7D, 0x34, 0x5F, 0x2B, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0xB9, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x03, 0x00, 0xBA, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBA, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xBB, 0x00, 0x00, 0x00, + 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0xBF, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0xBF, 0x00, 0x00, 0x00, + 0xC6, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0xBF, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0xCD, 0xCC, 0xCC, 0x3E, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBF, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x3F, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x10, 0xBF, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x61, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xE6, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0xE1, 0x7A, 0x94, 0xBE, + 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x8D, 0x03, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xA8, 0x03, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xC3, 0x03, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xDE, 0x03, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x14, 0x04, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x2F, 0x04, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x4B, 0x04, 0x00, 0x00, 0x4A, 0x04, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x66, 0x04, 0x00, 0x00, + 0x4A, 0x04, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x81, 0x04, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x9C, 0x04, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x4A, 0x04, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xB7, 0x04, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x4A, 0x04, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, + 0xDE, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xDF, 0x04, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0xDE, 0x04, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xDF, 0x04, 0x00, 0x00, + 0xE0, 0x04, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xE1, 0x04, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, 0xF1, 0x04, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x2C, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, + 0xF8, 0x04, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x4A, 0x04, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x4A, 0x04, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, + 0x20, 0x00, 0x04, 0x00, 0x01, 0x05, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x05, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x07, 0x05, 0x00, 0x00, 0x08, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x09, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x09, 0x05, 0x00, 0x00, + 0x0A, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1C, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x35, 0x12, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x2C, 0x00, 0x07, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xE1, 0x04, 0x00, 0x00, + 0xE2, 0x04, 0x00, 0x00, 0xE0, 0x04, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE3, 0x04, 0x00, 0x00, + 0xE2, 0x04, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xE4, 0x04, 0x00, 0x00, 0xE3, 0x04, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xE7, 0x04, 0x00, 0x00, 0xE3, 0x04, 0x00, 0x00, + 0x21, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xE9, 0x04, 0x00, 0x00, 0xE7, 0x04, 0x00, 0x00, 0x35, 0x12, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x60, 0x00, 0x00, 0x00, 0xEA, 0x04, 0x00, 0x00, + 0xE9, 0x04, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0xE1, 0x04, 0x00, 0x00, + 0xF2, 0x04, 0x00, 0x00, 0xE0, 0x04, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xF3, 0x04, 0x00, 0x00, + 0xF2, 0x04, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, 0x16, 0x00, 0x00, 0x00, + 0xF4, 0x04, 0x00, 0x00, 0xF3, 0x04, 0x00, 0x00, 0xF3, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0xF5, 0x04, 0x00, 0x00, 0xF1, 0x04, 0x00, 0x00, 0xF4, 0x04, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0xF6, 0x04, 0x00, 0x00, + 0xF5, 0x04, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0xFC, 0x04, 0x00, 0x00, 0xF8, 0x04, 0x00, 0x00, 0xF4, 0x04, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0xFD, 0x04, 0x00, 0x00, + 0xFC, 0x04, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xFF, 0x04, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, + 0x01, 0x05, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0xE0, 0x04, 0x00, 0x00, + 0xC6, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x0B, 0x05, 0x00, 0x00, + 0x0A, 0x05, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0C, 0x05, 0x00, 0x00, 0x0B, 0x05, 0x00, 0x00, 0x0B, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x04, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x0D, 0x05, 0x00, 0x00, 0x0C, 0x05, 0x00, 0x00, + 0x70, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0F, 0x06, 0x00, 0x00, + 0x0D, 0x05, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x15, 0x06, 0x00, 0x00, 0xE4, 0x04, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x16, 0x06, 0x00, 0x00, 0x0F, 0x06, 0x00, 0x00, + 0x15, 0x06, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x1C, 0x06, 0x00, 0x00, 0xEA, 0x04, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x1D, 0x06, 0x00, 0x00, 0x16, 0x06, 0x00, 0x00, + 0x1C, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x1F, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x1D, 0x06, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x22, 0x06, 0x00, 0x00, 0x1D, 0x06, 0x00, 0x00, 0x1F, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x26, 0x06, 0x00, 0x00, + 0xF6, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x27, 0x06, 0x00, 0x00, 0xF6, 0x04, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x28, 0x06, 0x00, 0x00, 0x26, 0x06, 0x00, 0x00, 0x27, 0x06, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x29, 0x06, 0x00, 0x00, + 0x28, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x2A, 0x06, 0x00, 0x00, 0x1F, 0x06, 0x00, 0x00, 0x29, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x2D, 0x06, 0x00, 0x00, + 0xF6, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x2E, 0x06, 0x00, 0x00, 0xF6, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x2F, 0x06, 0x00, 0x00, 0x2D, 0x06, 0x00, 0x00, 0x2E, 0x06, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, + 0x2F, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x31, 0x06, 0x00, 0x00, 0x2A, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x35, 0x06, 0x00, 0x00, + 0xFD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0xFD, 0x04, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x37, 0x06, 0x00, 0x00, 0x35, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x38, 0x06, 0x00, 0x00, + 0x37, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x39, 0x06, 0x00, 0x00, 0x31, 0x06, 0x00, 0x00, 0x38, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x06, 0x00, 0x00, + 0xFD, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x3E, 0x06, 0x00, 0x00, 0xFD, 0x04, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x3F, 0x06, 0x00, 0x00, 0x3D, 0x06, 0x00, 0x00, 0x3E, 0x06, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, + 0x3F, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x41, 0x06, 0x00, 0x00, 0x31, 0x06, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, + 0x50, 0x00, 0x05, 0x00, 0x60, 0x00, 0x00, 0x00, 0x47, 0x06, 0x00, 0x00, + 0xFF, 0x04, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x48, 0x06, 0x00, 0x00, 0x47, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x49, 0x06, 0x00, 0x00, + 0x31, 0x06, 0x00, 0x00, 0x48, 0x06, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0xBA, 0x00, 0x00, 0x00, 0x3C, 0x08, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3E, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x31, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x31, 0x06, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x31, 0x06, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x4D, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x39, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x52, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x39, 0x06, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x57, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x39, 0x06, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x5C, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x41, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x41, 0x06, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x66, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x41, 0x06, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x6B, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x49, 0x06, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x70, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x49, 0x06, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x75, 0x08, 0x00, 0x00, + 0x3C, 0x08, 0x00, 0x00, 0x49, 0x06, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x64, 0x06, 0x00, 0x00, + 0x48, 0x08, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x67, 0x06, 0x00, 0x00, 0x3E, 0x08, 0x00, 0x00, + 0x36, 0x12, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x69, 0x06, 0x00, 0x00, 0x67, 0x06, 0x00, 0x00, 0x43, 0x08, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x6A, 0x06, 0x00, 0x00, + 0x64, 0x06, 0x00, 0x00, 0x69, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x6D, 0x06, 0x00, 0x00, 0x57, 0x08, 0x00, 0x00, + 0x36, 0x12, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x70, 0x06, 0x00, 0x00, 0x4D, 0x08, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x72, 0x06, 0x00, 0x00, + 0x70, 0x06, 0x00, 0x00, 0x52, 0x08, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x73, 0x06, 0x00, 0x00, 0x6D, 0x06, 0x00, 0x00, + 0x72, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x76, 0x06, 0x00, 0x00, 0x66, 0x08, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x79, 0x06, 0x00, 0x00, + 0x5C, 0x08, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x7B, 0x06, 0x00, 0x00, 0x79, 0x06, 0x00, 0x00, + 0x61, 0x08, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x7C, 0x06, 0x00, 0x00, 0x76, 0x06, 0x00, 0x00, 0x7B, 0x06, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x7F, 0x06, 0x00, 0x00, + 0x75, 0x08, 0x00, 0x00, 0x36, 0x12, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x82, 0x06, 0x00, 0x00, 0x6B, 0x08, 0x00, 0x00, + 0x36, 0x12, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x84, 0x06, 0x00, 0x00, 0x82, 0x06, 0x00, 0x00, 0x70, 0x08, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x85, 0x06, 0x00, 0x00, + 0x7F, 0x06, 0x00, 0x00, 0x84, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x87, 0x06, 0x00, 0x00, 0x6A, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x89, 0x06, 0x00, 0x00, 0x6A, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8B, 0x06, 0x00, 0x00, + 0x73, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8D, 0x06, 0x00, 0x00, 0x73, 0x06, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x8F, 0x06, 0x00, 0x00, 0x73, 0x06, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x91, 0x06, 0x00, 0x00, + 0x73, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x93, 0x06, 0x00, 0x00, 0x7C, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x95, 0x06, 0x00, 0x00, 0x7C, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x97, 0x06, 0x00, 0x00, + 0x7C, 0x06, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x99, 0x06, 0x00, 0x00, 0x7C, 0x06, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9B, 0x06, 0x00, 0x00, 0x85, 0x06, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9D, 0x06, 0x00, 0x00, + 0x85, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCE, 0x08, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCF, 0x08, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0xCE, 0x08, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD2, 0x08, 0x00, 0x00, + 0x22, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD3, 0x08, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0xD2, 0x08, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xD4, 0x08, 0x00, 0x00, 0xCF, 0x08, 0x00, 0x00, 0xD3, 0x08, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF4, 0x08, 0x00, 0x00, + 0x99, 0x06, 0x00, 0x00, 0x8F, 0x06, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF7, 0x08, 0x00, 0x00, 0x8F, 0x06, 0x00, 0x00, + 0x91, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF9, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xF4, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xFB, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xF7, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xFC, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0xF9, 0x08, 0x00, 0x00, 0xFB, 0x08, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x4E, 0x09, 0x00, 0x00, 0xFC, 0x08, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x4F, 0x09, 0x00, 0x00, + 0xA3, 0x00, 0x00, 0x00, 0x4E, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x50, 0x09, 0x00, 0x00, 0x4F, 0x09, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, + 0x99, 0x06, 0x00, 0x00, 0x91, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x09, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, + 0xD4, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0A, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x09, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0C, 0x09, 0x00, 0x00, 0x0A, 0x09, 0x00, 0x00, 0x50, 0x09, 0x00, 0x00, + 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5B, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x0C, 0x09, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x10, 0x09, 0x00, 0x00, 0x5B, 0x09, 0x00, 0x00, + 0x5B, 0x09, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x13, 0x09, 0x00, 0x00, 0x10, 0x09, 0x00, 0x00, 0xD4, 0x08, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x09, 0x00, 0x00, + 0x8D, 0x06, 0x00, 0x00, 0x8F, 0x06, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1B, 0x09, 0x00, 0x00, 0x8F, 0x06, 0x00, 0x00, + 0x87, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1D, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x18, 0x09, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1F, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1B, 0x09, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x1D, 0x09, 0x00, 0x00, 0x1F, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x67, 0x09, 0x00, 0x00, 0x20, 0x09, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x68, 0x09, 0x00, 0x00, + 0xA3, 0x00, 0x00, 0x00, 0x67, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x69, 0x09, 0x00, 0x00, 0x68, 0x09, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x09, 0x00, 0x00, + 0x8D, 0x06, 0x00, 0x00, 0x87, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x28, 0x09, 0x00, 0x00, 0x25, 0x09, 0x00, 0x00, + 0xD4, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2E, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x25, 0x09, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x30, 0x09, 0x00, 0x00, 0x2E, 0x09, 0x00, 0x00, 0x69, 0x09, 0x00, 0x00, + 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x74, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x30, 0x09, 0x00, 0x00, + 0x9B, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x34, 0x09, 0x00, 0x00, 0x74, 0x09, 0x00, 0x00, + 0x74, 0x09, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x37, 0x09, 0x00, 0x00, 0x34, 0x09, 0x00, 0x00, 0xD4, 0x08, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, + 0x13, 0x09, 0x00, 0x00, 0x37, 0x09, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA3, 0x09, 0x00, 0x00, 0xCE, 0x08, 0x00, 0x00, + 0xD3, 0x08, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB9, 0x09, 0x00, 0x00, 0x97, 0x06, 0x00, 0x00, 0x99, 0x06, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBE, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xB9, 0x09, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC1, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xBE, 0x09, 0x00, 0x00, + 0xF9, 0x08, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x13, 0x0A, 0x00, 0x00, 0xC1, 0x09, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x14, 0x0A, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0x13, 0x0A, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x15, 0x0A, 0x00, 0x00, 0x14, 0x0A, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC6, 0x09, 0x00, 0x00, 0x97, 0x06, 0x00, 0x00, + 0x8F, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC9, 0x09, 0x00, 0x00, 0xC6, 0x09, 0x00, 0x00, 0xA3, 0x09, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCC, 0x09, 0x00, 0x00, + 0x04, 0x09, 0x00, 0x00, 0xC9, 0x09, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCF, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xC6, 0x09, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD1, 0x09, 0x00, 0x00, 0xCF, 0x09, 0x00, 0x00, + 0x15, 0x0A, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xD1, 0x09, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD5, 0x09, 0x00, 0x00, + 0x20, 0x0A, 0x00, 0x00, 0x20, 0x0A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD8, 0x09, 0x00, 0x00, 0xD5, 0x09, 0x00, 0x00, + 0xA3, 0x09, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDA, 0x09, 0x00, 0x00, 0x39, 0x09, 0x00, 0x00, 0xD8, 0x09, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDD, 0x09, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x00, 0x99, 0x06, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xE0, 0x09, 0x00, 0x00, 0x99, 0x06, 0x00, 0x00, + 0x89, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE2, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xDD, 0x09, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE4, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xE0, 0x09, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE5, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0xE2, 0x09, 0x00, 0x00, 0xE4, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x2C, 0x0A, 0x00, 0x00, 0xE5, 0x09, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x00, 0x00, + 0xA3, 0x00, 0x00, 0x00, 0x2C, 0x0A, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2E, 0x0A, 0x00, 0x00, 0x2D, 0x0A, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xEA, 0x09, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x00, 0x89, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xED, 0x09, 0x00, 0x00, 0xEA, 0x09, 0x00, 0x00, + 0xA3, 0x09, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF0, 0x09, 0x00, 0x00, 0x28, 0x09, 0x00, 0x00, 0xED, 0x09, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF3, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xEA, 0x09, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF5, 0x09, 0x00, 0x00, + 0xF3, 0x09, 0x00, 0x00, 0x2E, 0x0A, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x39, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0xF5, 0x09, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF9, 0x09, 0x00, 0x00, 0x39, 0x0A, 0x00, 0x00, 0x39, 0x0A, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFC, 0x09, 0x00, 0x00, + 0xF9, 0x09, 0x00, 0x00, 0xA3, 0x09, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xFE, 0x09, 0x00, 0x00, 0xDA, 0x09, 0x00, 0x00, + 0xFC, 0x09, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x0A, 0x00, 0x00, 0xCF, 0x08, 0x00, 0x00, 0xD2, 0x08, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7E, 0x0A, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x00, 0x8D, 0x06, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, 0x8D, 0x06, 0x00, 0x00, + 0x8B, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x83, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x7E, 0x0A, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x85, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x81, 0x0A, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x86, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x83, 0x0A, 0x00, 0x00, 0x85, 0x0A, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0xD8, 0x0A, 0x00, 0x00, 0x86, 0x0A, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xD9, 0x0A, 0x00, 0x00, + 0xA3, 0x00, 0x00, 0x00, 0xD8, 0x0A, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xDA, 0x0A, 0x00, 0x00, 0xD9, 0x0A, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8B, 0x0A, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x00, 0x8B, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8E, 0x0A, 0x00, 0x00, 0x8B, 0x0A, 0x00, 0x00, + 0x72, 0x0A, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x91, 0x0A, 0x00, 0x00, 0xCC, 0x09, 0x00, 0x00, 0x8E, 0x0A, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x94, 0x0A, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8B, 0x0A, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x96, 0x0A, 0x00, 0x00, + 0x94, 0x0A, 0x00, 0x00, 0xDA, 0x0A, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xE5, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x96, 0x0A, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9A, 0x0A, 0x00, 0x00, 0xE5, 0x0A, 0x00, 0x00, 0xE5, 0x0A, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9D, 0x0A, 0x00, 0x00, + 0x9A, 0x0A, 0x00, 0x00, 0x72, 0x0A, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9F, 0x0A, 0x00, 0x00, 0xFE, 0x09, 0x00, 0x00, + 0x9D, 0x0A, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA2, 0x0A, 0x00, 0x00, 0x9D, 0x06, 0x00, 0x00, 0x8D, 0x06, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA7, 0x0A, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xA2, 0x0A, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAA, 0x0A, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xA7, 0x0A, 0x00, 0x00, + 0x1D, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xF1, 0x0A, 0x00, 0x00, 0xAA, 0x0A, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0xF2, 0x0A, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0xF1, 0x0A, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF3, 0x0A, 0x00, 0x00, 0xF2, 0x0A, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xAF, 0x0A, 0x00, 0x00, 0x9D, 0x06, 0x00, 0x00, + 0x8F, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB2, 0x0A, 0x00, 0x00, 0xAF, 0x0A, 0x00, 0x00, 0x72, 0x0A, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB5, 0x0A, 0x00, 0x00, + 0xF0, 0x09, 0x00, 0x00, 0xB2, 0x0A, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB8, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xAF, 0x0A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xBA, 0x0A, 0x00, 0x00, 0xB8, 0x0A, 0x00, 0x00, + 0xF3, 0x0A, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xFE, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0xBA, 0x0A, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBE, 0x0A, 0x00, 0x00, + 0xFE, 0x0A, 0x00, 0x00, 0xFE, 0x0A, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC1, 0x0A, 0x00, 0x00, 0xBE, 0x0A, 0x00, 0x00, + 0x72, 0x0A, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC3, 0x0A, 0x00, 0x00, 0x9F, 0x0A, 0x00, 0x00, 0xC1, 0x0A, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x0B, 0x00, 0x00, + 0xCE, 0x08, 0x00, 0x00, 0xD2, 0x08, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x43, 0x0B, 0x00, 0x00, 0x95, 0x06, 0x00, 0x00, + 0x93, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x48, 0x0B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x43, 0x0B, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4B, 0x0B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x48, 0x0B, 0x00, 0x00, 0x83, 0x0A, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x9D, 0x0B, 0x00, 0x00, 0x4B, 0x0B, 0x00, 0x00, + 0x82, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x9E, 0x0B, 0x00, 0x00, + 0xA3, 0x00, 0x00, 0x00, 0x9D, 0x0B, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9F, 0x0B, 0x00, 0x00, 0x9E, 0x0B, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x0B, 0x00, 0x00, + 0x95, 0x06, 0x00, 0x00, 0x8D, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x53, 0x0B, 0x00, 0x00, 0x50, 0x0B, 0x00, 0x00, + 0x3F, 0x0B, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x56, 0x0B, 0x00, 0x00, 0x91, 0x0A, 0x00, 0x00, 0x53, 0x0B, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x2A, 0x11, 0x00, 0x00, + 0x56, 0x0B, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x59, 0x0B, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x50, 0x0B, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5B, 0x0B, 0x00, 0x00, + 0x59, 0x0B, 0x00, 0x00, 0x9F, 0x0B, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xAA, 0x0B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x00, 0x00, 0x5B, 0x0B, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5F, 0x0B, 0x00, 0x00, 0xAA, 0x0B, 0x00, 0x00, 0xAA, 0x0B, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x62, 0x0B, 0x00, 0x00, + 0x5F, 0x0B, 0x00, 0x00, 0x3F, 0x0B, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x64, 0x0B, 0x00, 0x00, 0xC3, 0x0A, 0x00, 0x00, + 0x62, 0x0B, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x67, 0x0B, 0x00, 0x00, 0x9B, 0x06, 0x00, 0x00, 0x93, 0x06, 0x00, 0x00, + 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6C, 0x0B, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x67, 0x0B, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6F, 0x0B, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x6C, 0x0B, 0x00, 0x00, + 0xE2, 0x09, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xB6, 0x0B, 0x00, 0x00, 0x6F, 0x0B, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0xB7, 0x0B, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0xB6, 0x0B, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB8, 0x0B, 0x00, 0x00, 0xB7, 0x0B, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x74, 0x0B, 0x00, 0x00, 0x9B, 0x06, 0x00, 0x00, + 0x99, 0x06, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x77, 0x0B, 0x00, 0x00, 0x74, 0x0B, 0x00, 0x00, 0x3F, 0x0B, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7A, 0x0B, 0x00, 0x00, + 0xB5, 0x0A, 0x00, 0x00, 0x77, 0x0B, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x2D, 0x11, 0x00, 0x00, 0x7A, 0x0B, 0x00, 0x00, + 0x2A, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7D, 0x0B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x74, 0x0B, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7F, 0x0B, 0x00, 0x00, 0x7D, 0x0B, 0x00, 0x00, + 0xB8, 0x0B, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC3, 0x0B, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, + 0x7F, 0x0B, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x83, 0x0B, 0x00, 0x00, + 0xC3, 0x0B, 0x00, 0x00, 0xC3, 0x0B, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x86, 0x0B, 0x00, 0x00, 0x83, 0x0B, 0x00, 0x00, + 0x3F, 0x0B, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x88, 0x0B, 0x00, 0x00, 0x64, 0x0B, 0x00, 0x00, 0x86, 0x0B, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xCE, 0x06, 0x00, 0x00, + 0x2D, 0x11, 0x00, 0x00, 0x2D, 0x11, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD0, 0x06, 0x00, 0x00, 0xCE, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xD2, 0x06, 0x00, 0x00, 0xCE, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD3, 0x06, 0x00, 0x00, + 0xD0, 0x06, 0x00, 0x00, 0xD2, 0x06, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, + 0x4F, 0x00, 0x00, 0x00, 0xD6, 0x06, 0x00, 0x00, 0xD3, 0x06, 0x00, 0x00, + 0xE6, 0x02, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xD3, 0x0B, 0x00, 0x00, 0xD3, 0x06, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0xD5, 0x0B, 0x00, 0x00, 0xD3, 0x0B, 0x00, 0x00, + 0xB1, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xD6, 0x0B, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0xD5, 0x0B, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD7, 0x0B, 0x00, 0x00, + 0xD6, 0x0B, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xDE, 0x06, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xDE, 0x06, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x58, 0x12, 0x00, 0x00, 0xD6, 0x06, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0xD7, 0x0B, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, + 0xE6, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, + 0xD6, 0x06, 0x00, 0x00, 0xE1, 0x06, 0x00, 0x00, 0xE3, 0x06, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0xE3, 0x06, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, + 0xE6, 0x06, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xE1, 0x06, 0x00, 0x00, + 0xF9, 0x00, 0x02, 0x00, 0xE6, 0x06, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0xE6, 0x06, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3A, 0x12, 0x00, 0x00, 0x56, 0x0B, 0x00, 0x00, 0xE3, 0x06, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0xE1, 0x06, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x32, 0x11, 0x00, 0x00, 0x3A, 0x12, 0x00, 0x00, + 0x2D, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xE8, 0x0B, 0x00, 0x00, 0x58, 0x12, 0x00, 0x00, + 0x58, 0x12, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xEC, 0x06, 0x00, 0x00, 0x32, 0x11, 0x00, 0x00, 0xE8, 0x0B, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xEF, 0x06, 0x00, 0x00, + 0x88, 0x0B, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, 0xEF, 0x06, 0x00, 0x00, + 0xEF, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0xEC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF7, 0x06, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, 0xEC, 0x06, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xFC, 0x06, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFD, 0x06, 0x00, 0x00, + 0xF7, 0x06, 0x00, 0x00, 0xFC, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xF1, 0x0B, 0x00, 0x00, + 0x04, 0x07, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0xF2, 0x0B, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 0xF1, 0x0B, 0x00, 0x00, + 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF3, 0x0B, 0x00, 0x00, + 0xF2, 0x0B, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x07, 0x00, 0x00, 0xFD, 0x06, 0x00, 0x00, 0xF3, 0x0B, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0A, 0x07, 0x00, 0x00, + 0x06, 0x07, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x0C, 0x07, 0x00, 0x00, 0x0A, 0x07, 0x00, 0x00, + 0xF2, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0D, 0x07, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x0C, 0x07, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x07, 0x00, 0x00, + 0x2F, 0x03, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x12, 0x07, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x11, 0x07, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x13, 0x07, 0x00, 0x00, 0x0D, 0x07, 0x00, 0x00, 0x12, 0x07, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x07, 0x00, 0x00, + 0x39, 0x03, 0x00, 0x00, 0xF2, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, + 0x17, 0x07, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x0C, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, + 0x0E, 0x0C, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x10, 0x0C, 0x00, 0x00, 0x0F, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1C, 0x07, 0x00, 0x00, 0x4D, 0x08, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1E, 0x07, 0x00, 0x00, 0x52, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x07, 0x00, 0x00, + 0x57, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x21, 0x07, 0x00, 0x00, 0x1C, 0x07, 0x00, 0x00, + 0x1E, 0x07, 0x00, 0x00, 0x20, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x23, 0x07, 0x00, 0x00, 0x5C, 0x08, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x25, 0x07, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x27, 0x07, 0x00, 0x00, + 0x66, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x28, 0x07, 0x00, 0x00, 0x23, 0x07, 0x00, 0x00, + 0x25, 0x07, 0x00, 0x00, 0x27, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2A, 0x07, 0x00, 0x00, 0x4D, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2C, 0x07, 0x00, 0x00, 0x52, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x07, 0x00, 0x00, + 0x57, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x2F, 0x07, 0x00, 0x00, 0x2A, 0x07, 0x00, 0x00, + 0x2C, 0x07, 0x00, 0x00, 0x2E, 0x07, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x19, 0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x28, 0x07, 0x00, 0x00, 0x2F, 0x07, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1A, 0x0C, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x21, 0x07, 0x00, 0x00, + 0x19, 0x0C, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x32, 0x07, 0x00, 0x00, 0x5C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x07, 0x00, 0x00, + 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0x66, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x37, 0x07, 0x00, 0x00, 0x32, 0x07, 0x00, 0x00, 0x34, 0x07, 0x00, 0x00, + 0x36, 0x07, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x38, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x1A, 0x0C, 0x00, 0x00, 0x37, 0x07, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x20, 0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x28, 0x07, 0x00, 0x00, 0x2F, 0x07, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x0C, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x21, 0x07, 0x00, 0x00, + 0x20, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x56, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x21, 0x0C, 0x00, 0x00, 0x37, 0x07, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x5A, 0x07, 0x00, 0x00, 0x8D, 0x03, 0x00, 0x00, + 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5C, 0x07, 0x00, 0x00, 0x3E, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5E, 0x07, 0x00, 0x00, + 0x43, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x48, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x61, 0x07, 0x00, 0x00, 0x5C, 0x07, 0x00, 0x00, 0x5E, 0x07, 0x00, 0x00, + 0x60, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x37, 0x0C, 0x00, 0x00, 0x5A, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3A, 0x0C, 0x00, 0x00, + 0x37, 0x0C, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3C, 0x0C, 0x00, 0x00, 0x5A, 0x07, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3F, 0x0C, 0x00, 0x00, 0x3C, 0x0C, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, + 0x3A, 0x0C, 0x00, 0x00, 0x3F, 0x0C, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x59, 0x11, 0x00, 0x00, 0x40, 0x0C, 0x00, 0x00, + 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x0C, 0x00, 0x00, + 0x37, 0x0C, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4C, 0x0C, 0x00, 0x00, 0x3C, 0x0C, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4D, 0x0C, 0x00, 0x00, 0x47, 0x0C, 0x00, 0x00, 0x4C, 0x0C, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x5F, 0x11, 0x00, 0x00, + 0x4D, 0x0C, 0x00, 0x00, 0x59, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, + 0x5F, 0x11, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x53, 0x0C, 0x00, 0x00, 0x51, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x56, 0x0C, 0x00, 0x00, 0x53, 0x0C, 0x00, 0x00, 0x53, 0x0C, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x58, 0x0C, 0x00, 0x00, + 0x51, 0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5B, 0x0C, 0x00, 0x00, 0x58, 0x0C, 0x00, 0x00, + 0x58, 0x0C, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5C, 0x0C, 0x00, 0x00, 0x56, 0x0C, 0x00, 0x00, 0x5B, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5F, 0x0C, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x5C, 0x0C, 0x00, 0x00, + 0x10, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x62, 0x0C, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x5F, 0x0C, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x64, 0x0C, 0x00, 0x00, + 0x62, 0x0C, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x67, 0x0C, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, + 0x5F, 0x0C, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x69, 0x0C, 0x00, 0x00, 0x67, 0x0C, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6C, 0x0C, 0x00, 0x00, + 0x64, 0x0C, 0x00, 0x00, 0x64, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6F, 0x0C, 0x00, 0x00, 0x69, 0x0C, 0x00, 0x00, + 0x69, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x72, 0x0C, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x6C, 0x0C, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x74, 0x0C, 0x00, 0x00, + 0x72, 0x0C, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x77, 0x0C, 0x00, 0x00, 0x74, 0x0C, 0x00, 0x00, + 0x6F, 0x0C, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x7A, 0x0C, 0x00, 0x00, 0x61, 0x07, 0x00, 0x00, 0x77, 0x0C, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x6C, 0x07, 0x00, 0x00, + 0xA8, 0x03, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6E, 0x07, 0x00, 0x00, 0x3E, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x70, 0x07, 0x00, 0x00, 0x43, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x72, 0x07, 0x00, 0x00, + 0x48, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x73, 0x07, 0x00, 0x00, 0x6E, 0x07, 0x00, 0x00, + 0x70, 0x07, 0x00, 0x00, 0x72, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9B, 0x0C, 0x00, 0x00, 0x6C, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9E, 0x0C, 0x00, 0x00, 0x9B, 0x0C, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA0, 0x0C, 0x00, 0x00, + 0x6C, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA3, 0x0C, 0x00, 0x00, 0xA0, 0x0C, 0x00, 0x00, + 0xF9, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA4, 0x0C, 0x00, 0x00, 0x9E, 0x0C, 0x00, 0x00, 0xA3, 0x0C, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x6C, 0x11, 0x00, 0x00, + 0xA4, 0x0C, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAB, 0x0C, 0x00, 0x00, + 0x9B, 0x0C, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB0, 0x0C, 0x00, 0x00, 0xA0, 0x0C, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB1, 0x0C, 0x00, 0x00, 0xAB, 0x0C, 0x00, 0x00, 0xB0, 0x0C, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x72, 0x11, 0x00, 0x00, + 0xB1, 0x0C, 0x00, 0x00, 0x6C, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xB5, 0x0C, 0x00, 0x00, + 0x72, 0x11, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB7, 0x0C, 0x00, 0x00, 0xB5, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xBA, 0x0C, 0x00, 0x00, 0xB7, 0x0C, 0x00, 0x00, 0xB7, 0x0C, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBC, 0x0C, 0x00, 0x00, + 0xB5, 0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xBF, 0x0C, 0x00, 0x00, 0xBC, 0x0C, 0x00, 0x00, + 0xBC, 0x0C, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC0, 0x0C, 0x00, 0x00, 0xBA, 0x0C, 0x00, 0x00, 0xBF, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC3, 0x0C, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, + 0x10, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC6, 0x0C, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xC3, 0x0C, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC8, 0x0C, 0x00, 0x00, + 0xC6, 0x0C, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCB, 0x0C, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, + 0xC3, 0x0C, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCD, 0x0C, 0x00, 0x00, 0xCB, 0x0C, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD0, 0x0C, 0x00, 0x00, + 0xC8, 0x0C, 0x00, 0x00, 0xC8, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD3, 0x0C, 0x00, 0x00, 0xCD, 0x0C, 0x00, 0x00, + 0xCD, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xD6, 0x0C, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0xD0, 0x0C, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD8, 0x0C, 0x00, 0x00, + 0xD6, 0x0C, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xDB, 0x0C, 0x00, 0x00, 0xD8, 0x0C, 0x00, 0x00, + 0xD3, 0x0C, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0xDE, 0x0C, 0x00, 0x00, 0x73, 0x07, 0x00, 0x00, 0xDB, 0x0C, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0xE0, 0x0C, 0x00, 0x00, + 0x7A, 0x0C, 0x00, 0x00, 0xDE, 0x0C, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xE3, 0x0C, 0x00, 0x00, 0x77, 0x0C, 0x00, 0x00, + 0xDB, 0x0C, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x7E, 0x07, 0x00, 0x00, 0xC3, 0x03, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, + 0x4D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x82, 0x07, 0x00, 0x00, 0x52, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x84, 0x07, 0x00, 0x00, 0x57, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, 0x85, 0x07, 0x00, 0x00, + 0x80, 0x07, 0x00, 0x00, 0x82, 0x07, 0x00, 0x00, 0x84, 0x07, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0x0C, 0x00, 0x00, + 0x7E, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x00, 0xFF, 0x0C, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x0D, 0x00, 0x00, 0x7E, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x0D, 0x00, 0x00, + 0x04, 0x0D, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x08, 0x0D, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x00, + 0x07, 0x0D, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x7F, 0x11, 0x00, 0x00, 0x08, 0x0D, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0F, 0x0D, 0x00, 0x00, 0xFF, 0x0C, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x0D, 0x00, 0x00, + 0x04, 0x0D, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x15, 0x0D, 0x00, 0x00, 0x0F, 0x0D, 0x00, 0x00, + 0x14, 0x0D, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x85, 0x11, 0x00, 0x00, 0x15, 0x0D, 0x00, 0x00, 0x7F, 0x11, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x19, 0x0D, 0x00, 0x00, 0x85, 0x11, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1B, 0x0D, 0x00, 0x00, + 0x19, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1E, 0x0D, 0x00, 0x00, 0x1B, 0x0D, 0x00, 0x00, + 0x1B, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x0D, 0x00, 0x00, 0x19, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x0D, 0x00, 0x00, + 0x20, 0x0D, 0x00, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x24, 0x0D, 0x00, 0x00, 0x1E, 0x0D, 0x00, 0x00, + 0x23, 0x0D, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x27, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x24, 0x0D, 0x00, 0x00, 0x10, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2A, 0x0D, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0x27, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2C, 0x0D, 0x00, 0x00, 0x2A, 0x0D, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x0D, 0x00, 0x00, + 0x18, 0x07, 0x00, 0x00, 0x27, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x31, 0x0D, 0x00, 0x00, 0x2F, 0x0D, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x34, 0x0D, 0x00, 0x00, 0x2C, 0x0D, 0x00, 0x00, 0x2C, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x37, 0x0D, 0x00, 0x00, + 0x31, 0x0D, 0x00, 0x00, 0x31, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x3A, 0x0D, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, + 0x34, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3C, 0x0D, 0x00, 0x00, 0x3A, 0x0D, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x0D, 0x00, 0x00, + 0x3C, 0x0D, 0x00, 0x00, 0x37, 0x0D, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x42, 0x0D, 0x00, 0x00, 0x85, 0x07, 0x00, 0x00, + 0x3F, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x44, 0x0D, 0x00, 0x00, 0xE0, 0x0C, 0x00, 0x00, 0x42, 0x0D, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x0D, 0x00, 0x00, + 0xE3, 0x0C, 0x00, 0x00, 0x3F, 0x0D, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0xDE, 0x03, 0x00, 0x00, + 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x63, 0x0D, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x66, 0x0D, 0x00, 0x00, + 0x63, 0x0D, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x68, 0x0D, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6B, 0x0D, 0x00, 0x00, 0x68, 0x0D, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6C, 0x0D, 0x00, 0x00, + 0x66, 0x0D, 0x00, 0x00, 0x6B, 0x0D, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x92, 0x11, 0x00, 0x00, 0x6C, 0x0D, 0x00, 0x00, + 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x73, 0x0D, 0x00, 0x00, 0x63, 0x0D, 0x00, 0x00, + 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x78, 0x0D, 0x00, 0x00, 0x68, 0x0D, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x79, 0x0D, 0x00, 0x00, + 0x73, 0x0D, 0x00, 0x00, 0x78, 0x0D, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x98, 0x11, 0x00, 0x00, 0x79, 0x0D, 0x00, 0x00, + 0x92, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x7D, 0x0D, 0x00, 0x00, 0x98, 0x11, 0x00, 0x00, + 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7F, 0x0D, 0x00, 0x00, 0x7D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x82, 0x0D, 0x00, 0x00, + 0x7F, 0x0D, 0x00, 0x00, 0x7F, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x84, 0x0D, 0x00, 0x00, 0x7D, 0x0D, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x87, 0x0D, 0x00, 0x00, 0x84, 0x0D, 0x00, 0x00, 0x84, 0x0D, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x88, 0x0D, 0x00, 0x00, + 0x82, 0x0D, 0x00, 0x00, 0x87, 0x0D, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8B, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x88, 0x0D, 0x00, 0x00, 0x10, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8E, 0x0D, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0x8B, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x0D, 0x00, 0x00, 0x8E, 0x0D, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x93, 0x0D, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, 0x8B, 0x0D, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x95, 0x0D, 0x00, 0x00, + 0x93, 0x0D, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x98, 0x0D, 0x00, 0x00, 0x90, 0x0D, 0x00, 0x00, + 0x90, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9B, 0x0D, 0x00, 0x00, 0x95, 0x0D, 0x00, 0x00, 0x95, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9E, 0x0D, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0x98, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA0, 0x0D, 0x00, 0x00, 0x9E, 0x0D, 0x00, 0x00, + 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA3, 0x0D, 0x00, 0x00, 0xA0, 0x0D, 0x00, 0x00, 0x9B, 0x0D, 0x00, 0x00, + 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0xA6, 0x0D, 0x00, 0x00, + 0x2F, 0x07, 0x00, 0x00, 0xA3, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xA8, 0x0D, 0x00, 0x00, 0x44, 0x0D, 0x00, 0x00, + 0xA6, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xAB, 0x0D, 0x00, 0x00, 0x47, 0x0D, 0x00, 0x00, 0xA3, 0x0D, 0x00, 0x00, + 0x7F, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xA2, 0x07, 0x00, 0x00, + 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC7, 0x0D, 0x00, 0x00, 0xA2, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCA, 0x0D, 0x00, 0x00, + 0xC7, 0x0D, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCC, 0x0D, 0x00, 0x00, 0xA2, 0x07, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCF, 0x0D, 0x00, 0x00, 0xCC, 0x0D, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD0, 0x0D, 0x00, 0x00, + 0xCA, 0x0D, 0x00, 0x00, 0xCF, 0x0D, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xA5, 0x11, 0x00, 0x00, 0xD0, 0x0D, 0x00, 0x00, + 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD7, 0x0D, 0x00, 0x00, 0xC7, 0x0D, 0x00, 0x00, + 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDC, 0x0D, 0x00, 0x00, 0xCC, 0x0D, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDD, 0x0D, 0x00, 0x00, + 0xD7, 0x0D, 0x00, 0x00, 0xDC, 0x0D, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xAB, 0x11, 0x00, 0x00, 0xDD, 0x0D, 0x00, 0x00, + 0xA5, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xE1, 0x0D, 0x00, 0x00, 0xAB, 0x11, 0x00, 0x00, + 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE3, 0x0D, 0x00, 0x00, 0xE1, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xE6, 0x0D, 0x00, 0x00, + 0xE3, 0x0D, 0x00, 0x00, 0xE3, 0x0D, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xE8, 0x0D, 0x00, 0x00, 0xE1, 0x0D, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xEB, 0x0D, 0x00, 0x00, 0xE8, 0x0D, 0x00, 0x00, 0xE8, 0x0D, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xEC, 0x0D, 0x00, 0x00, + 0xE6, 0x0D, 0x00, 0x00, 0xEB, 0x0D, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xEF, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0xEC, 0x0D, 0x00, 0x00, 0x10, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF2, 0x0D, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0xEF, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF4, 0x0D, 0x00, 0x00, 0xF2, 0x0D, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF7, 0x0D, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, 0xEF, 0x0D, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF9, 0x0D, 0x00, 0x00, + 0xF7, 0x0D, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xFC, 0x0D, 0x00, 0x00, 0xF4, 0x0D, 0x00, 0x00, + 0xF4, 0x0D, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xFF, 0x0D, 0x00, 0x00, 0xF9, 0x0D, 0x00, 0x00, 0xF9, 0x0D, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x0E, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0xFC, 0x0D, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x00, 0x00, 0x02, 0x0E, 0x00, 0x00, + 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x0E, 0x00, 0x00, 0x04, 0x0E, 0x00, 0x00, 0xFF, 0x0D, 0x00, 0x00, + 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0A, 0x0E, 0x00, 0x00, + 0x21, 0x07, 0x00, 0x00, 0x07, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x0C, 0x0E, 0x00, 0x00, 0xA8, 0x0D, 0x00, 0x00, + 0x0A, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0F, 0x0E, 0x00, 0x00, 0xAB, 0x0D, 0x00, 0x00, 0x07, 0x0E, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xB4, 0x07, 0x00, 0x00, + 0x14, 0x04, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB6, 0x07, 0x00, 0x00, 0x4D, 0x08, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB8, 0x07, 0x00, 0x00, 0x52, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBA, 0x07, 0x00, 0x00, + 0x57, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xBB, 0x07, 0x00, 0x00, 0xB6, 0x07, 0x00, 0x00, + 0xB8, 0x07, 0x00, 0x00, 0xBA, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2B, 0x0E, 0x00, 0x00, 0xB4, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2E, 0x0E, 0x00, 0x00, 0x2B, 0x0E, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x0E, 0x00, 0x00, + 0xB4, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x33, 0x0E, 0x00, 0x00, 0x30, 0x0E, 0x00, 0x00, + 0xF9, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x34, 0x0E, 0x00, 0x00, 0x2E, 0x0E, 0x00, 0x00, 0x33, 0x0E, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xB8, 0x11, 0x00, 0x00, + 0x34, 0x0E, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x0E, 0x00, 0x00, + 0x2B, 0x0E, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x40, 0x0E, 0x00, 0x00, 0x30, 0x0E, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x41, 0x0E, 0x00, 0x00, 0x3B, 0x0E, 0x00, 0x00, 0x40, 0x0E, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xBE, 0x11, 0x00, 0x00, + 0x41, 0x0E, 0x00, 0x00, 0xB8, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x45, 0x0E, 0x00, 0x00, + 0xBE, 0x11, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x00, 0x00, 0x45, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4A, 0x0E, 0x00, 0x00, 0x47, 0x0E, 0x00, 0x00, 0x47, 0x0E, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4C, 0x0E, 0x00, 0x00, + 0x45, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4F, 0x0E, 0x00, 0x00, 0x4C, 0x0E, 0x00, 0x00, + 0x4C, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x50, 0x0E, 0x00, 0x00, 0x4A, 0x0E, 0x00, 0x00, 0x4F, 0x0E, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x53, 0x0E, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x50, 0x0E, 0x00, 0x00, + 0x10, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x56, 0x0E, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x53, 0x0E, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x58, 0x0E, 0x00, 0x00, + 0x56, 0x0E, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5B, 0x0E, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, + 0x53, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5D, 0x0E, 0x00, 0x00, 0x5B, 0x0E, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x0E, 0x00, 0x00, + 0x58, 0x0E, 0x00, 0x00, 0x58, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x63, 0x0E, 0x00, 0x00, 0x5D, 0x0E, 0x00, 0x00, + 0x5D, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x66, 0x0E, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x60, 0x0E, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x68, 0x0E, 0x00, 0x00, + 0x66, 0x0E, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6B, 0x0E, 0x00, 0x00, 0x68, 0x0E, 0x00, 0x00, + 0x63, 0x0E, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x6E, 0x0E, 0x00, 0x00, 0xBB, 0x07, 0x00, 0x00, 0x6B, 0x0E, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x70, 0x0E, 0x00, 0x00, + 0x0C, 0x0E, 0x00, 0x00, 0x6E, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x73, 0x0E, 0x00, 0x00, 0x0F, 0x0E, 0x00, 0x00, + 0x6B, 0x0E, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xC6, 0x07, 0x00, 0x00, 0x2F, 0x04, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8F, 0x0E, 0x00, 0x00, + 0xC6, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x92, 0x0E, 0x00, 0x00, 0x8F, 0x0E, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x94, 0x0E, 0x00, 0x00, 0xC6, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x97, 0x0E, 0x00, 0x00, + 0x94, 0x0E, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x98, 0x0E, 0x00, 0x00, 0x92, 0x0E, 0x00, 0x00, + 0x97, 0x0E, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xCB, 0x11, 0x00, 0x00, 0x98, 0x0E, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x9F, 0x0E, 0x00, 0x00, 0x8F, 0x0E, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA4, 0x0E, 0x00, 0x00, + 0x94, 0x0E, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA5, 0x0E, 0x00, 0x00, 0x9F, 0x0E, 0x00, 0x00, + 0xA4, 0x0E, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xD1, 0x11, 0x00, 0x00, 0xA5, 0x0E, 0x00, 0x00, 0xCB, 0x11, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xA9, 0x0E, 0x00, 0x00, 0xD1, 0x11, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAB, 0x0E, 0x00, 0x00, + 0xA9, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xAE, 0x0E, 0x00, 0x00, 0xAB, 0x0E, 0x00, 0x00, + 0xAB, 0x0E, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB0, 0x0E, 0x00, 0x00, 0xA9, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB3, 0x0E, 0x00, 0x00, + 0xB0, 0x0E, 0x00, 0x00, 0xB0, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB4, 0x0E, 0x00, 0x00, 0xAE, 0x0E, 0x00, 0x00, + 0xB3, 0x0E, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB7, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xB4, 0x0E, 0x00, 0x00, 0x10, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xBA, 0x0E, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0xB7, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xBC, 0x0E, 0x00, 0x00, 0xBA, 0x0E, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBF, 0x0E, 0x00, 0x00, + 0x18, 0x07, 0x00, 0x00, 0xB7, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC1, 0x0E, 0x00, 0x00, 0xBF, 0x0E, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC4, 0x0E, 0x00, 0x00, 0xBC, 0x0E, 0x00, 0x00, 0xBC, 0x0E, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC7, 0x0E, 0x00, 0x00, + 0xC1, 0x0E, 0x00, 0x00, 0xC1, 0x0E, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCA, 0x0E, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, + 0xC4, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCC, 0x0E, 0x00, 0x00, 0xCA, 0x0E, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCF, 0x0E, 0x00, 0x00, + 0xCC, 0x0E, 0x00, 0x00, 0xC7, 0x0E, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xD2, 0x0E, 0x00, 0x00, 0x37, 0x07, 0x00, 0x00, + 0xCF, 0x0E, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0xD4, 0x0E, 0x00, 0x00, 0x70, 0x0E, 0x00, 0x00, 0xD2, 0x0E, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD7, 0x0E, 0x00, 0x00, + 0x73, 0x0E, 0x00, 0x00, 0xCF, 0x0E, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xD8, 0x07, 0x00, 0x00, 0x4B, 0x04, 0x00, 0x00, + 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDA, 0x07, 0x00, 0x00, 0x5C, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDC, 0x07, 0x00, 0x00, + 0x61, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xDE, 0x07, 0x00, 0x00, 0x66, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, + 0xDF, 0x07, 0x00, 0x00, 0xDA, 0x07, 0x00, 0x00, 0xDC, 0x07, 0x00, 0x00, + 0xDE, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF3, 0x0E, 0x00, 0x00, 0xD8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF6, 0x0E, 0x00, 0x00, + 0xF3, 0x0E, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF8, 0x0E, 0x00, 0x00, 0xD8, 0x07, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xFB, 0x0E, 0x00, 0x00, 0xF8, 0x0E, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFC, 0x0E, 0x00, 0x00, + 0xF6, 0x0E, 0x00, 0x00, 0xFB, 0x0E, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xDE, 0x11, 0x00, 0x00, 0xFC, 0x0E, 0x00, 0x00, + 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x00, 0x00, 0xF3, 0x0E, 0x00, 0x00, + 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x08, 0x0F, 0x00, 0x00, 0xF8, 0x0E, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x09, 0x0F, 0x00, 0x00, + 0x03, 0x0F, 0x00, 0x00, 0x08, 0x0F, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0xE4, 0x11, 0x00, 0x00, 0x09, 0x0F, 0x00, 0x00, + 0xDE, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x0F, 0x00, 0x00, 0xE4, 0x11, 0x00, 0x00, + 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0F, 0x0F, 0x00, 0x00, 0x0D, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x0F, 0x00, 0x00, + 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x14, 0x0F, 0x00, 0x00, 0x0D, 0x0F, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x17, 0x0F, 0x00, 0x00, 0x14, 0x0F, 0x00, 0x00, 0x14, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x0F, 0x00, 0x00, + 0x12, 0x0F, 0x00, 0x00, 0x17, 0x0F, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x1B, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x18, 0x0F, 0x00, 0x00, 0x10, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x0F, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0x1B, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x20, 0x0F, 0x00, 0x00, 0x1E, 0x0F, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x23, 0x0F, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, 0x1B, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x0F, 0x00, 0x00, + 0x23, 0x0F, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x28, 0x0F, 0x00, 0x00, 0x20, 0x0F, 0x00, 0x00, + 0x20, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x2B, 0x0F, 0x00, 0x00, 0x25, 0x0F, 0x00, 0x00, 0x25, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x0F, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0x28, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x30, 0x0F, 0x00, 0x00, 0x2E, 0x0F, 0x00, 0x00, + 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x33, 0x0F, 0x00, 0x00, 0x30, 0x0F, 0x00, 0x00, 0x2B, 0x0F, 0x00, 0x00, + 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x36, 0x0F, 0x00, 0x00, + 0xDF, 0x07, 0x00, 0x00, 0x33, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x38, 0x0F, 0x00, 0x00, 0xD4, 0x0E, 0x00, 0x00, + 0x36, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3B, 0x0F, 0x00, 0x00, 0xD7, 0x0E, 0x00, 0x00, 0x33, 0x0F, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xEA, 0x07, 0x00, 0x00, + 0x66, 0x04, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xEC, 0x07, 0x00, 0x00, 0x5C, 0x08, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xEE, 0x07, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, + 0x66, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xF1, 0x07, 0x00, 0x00, 0xEC, 0x07, 0x00, 0x00, + 0xEE, 0x07, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x57, 0x0F, 0x00, 0x00, 0xEA, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5A, 0x0F, 0x00, 0x00, 0x57, 0x0F, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x0F, 0x00, 0x00, + 0xEA, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0x00, 0x00, 0x5C, 0x0F, 0x00, 0x00, + 0xF9, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x60, 0x0F, 0x00, 0x00, 0x5A, 0x0F, 0x00, 0x00, 0x5F, 0x0F, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xF1, 0x11, 0x00, 0x00, + 0x60, 0x0F, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x67, 0x0F, 0x00, 0x00, + 0x57, 0x0F, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x6C, 0x0F, 0x00, 0x00, 0x5C, 0x0F, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6D, 0x0F, 0x00, 0x00, 0x67, 0x0F, 0x00, 0x00, 0x6C, 0x0F, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xF7, 0x11, 0x00, 0x00, + 0x6D, 0x0F, 0x00, 0x00, 0xF1, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x71, 0x0F, 0x00, 0x00, + 0xF7, 0x11, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x73, 0x0F, 0x00, 0x00, 0x71, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x76, 0x0F, 0x00, 0x00, 0x73, 0x0F, 0x00, 0x00, 0x73, 0x0F, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, + 0x71, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x7B, 0x0F, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, + 0x78, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x7C, 0x0F, 0x00, 0x00, 0x76, 0x0F, 0x00, 0x00, 0x7B, 0x0F, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7F, 0x0F, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x7C, 0x0F, 0x00, 0x00, + 0x10, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x82, 0x0F, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x7F, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x0F, 0x00, 0x00, + 0x82, 0x0F, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x87, 0x0F, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, + 0x7F, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x89, 0x0F, 0x00, 0x00, 0x87, 0x0F, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8C, 0x0F, 0x00, 0x00, + 0x84, 0x0F, 0x00, 0x00, 0x84, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8F, 0x0F, 0x00, 0x00, 0x89, 0x0F, 0x00, 0x00, + 0x89, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x92, 0x0F, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x8C, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x94, 0x0F, 0x00, 0x00, + 0x92, 0x0F, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x97, 0x0F, 0x00, 0x00, 0x94, 0x0F, 0x00, 0x00, + 0x8F, 0x0F, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x9A, 0x0F, 0x00, 0x00, 0xF1, 0x07, 0x00, 0x00, 0x97, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x9C, 0x0F, 0x00, 0x00, + 0x38, 0x0F, 0x00, 0x00, 0x9A, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9F, 0x0F, 0x00, 0x00, 0x3B, 0x0F, 0x00, 0x00, + 0x97, 0x0F, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xFC, 0x07, 0x00, 0x00, 0x81, 0x04, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBB, 0x0F, 0x00, 0x00, + 0xFC, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xBE, 0x0F, 0x00, 0x00, 0xBB, 0x0F, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xC0, 0x0F, 0x00, 0x00, 0xFC, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x00, 0x00, + 0xC0, 0x0F, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC4, 0x0F, 0x00, 0x00, 0xBE, 0x0F, 0x00, 0x00, + 0xC3, 0x0F, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x04, 0x12, 0x00, 0x00, 0xC4, 0x0F, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xCB, 0x0F, 0x00, 0x00, 0xBB, 0x0F, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD0, 0x0F, 0x00, 0x00, + 0xC0, 0x0F, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xD1, 0x0F, 0x00, 0x00, 0xCB, 0x0F, 0x00, 0x00, + 0xD0, 0x0F, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0A, 0x12, 0x00, 0x00, 0xD1, 0x0F, 0x00, 0x00, 0x04, 0x12, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0xD5, 0x0F, 0x00, 0x00, 0x0A, 0x12, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD7, 0x0F, 0x00, 0x00, + 0xD5, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xDA, 0x0F, 0x00, 0x00, 0xD7, 0x0F, 0x00, 0x00, + 0xD7, 0x0F, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xDC, 0x0F, 0x00, 0x00, 0xD5, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDF, 0x0F, 0x00, 0x00, + 0xDC, 0x0F, 0x00, 0x00, 0xDC, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xDA, 0x0F, 0x00, 0x00, + 0xDF, 0x0F, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE3, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0xE0, 0x0F, 0x00, 0x00, 0x10, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0xE3, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE8, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xEB, 0x0F, 0x00, 0x00, + 0x18, 0x07, 0x00, 0x00, 0xE3, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xED, 0x0F, 0x00, 0x00, 0xEB, 0x0F, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF0, 0x0F, 0x00, 0x00, 0xE8, 0x0F, 0x00, 0x00, 0xE8, 0x0F, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF3, 0x0F, 0x00, 0x00, + 0xED, 0x0F, 0x00, 0x00, 0xED, 0x0F, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xF6, 0x0F, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, + 0xF0, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xF8, 0x0F, 0x00, 0x00, 0xF6, 0x0F, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFB, 0x0F, 0x00, 0x00, + 0xF8, 0x0F, 0x00, 0x00, 0xF3, 0x0F, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0x28, 0x07, 0x00, 0x00, + 0xFB, 0x0F, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x9C, 0x0F, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, + 0x9F, 0x0F, 0x00, 0x00, 0xFB, 0x0F, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0E, 0x08, 0x00, 0x00, 0x9C, 0x04, 0x00, 0x00, + 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x10, 0x08, 0x00, 0x00, 0x6B, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x08, 0x00, 0x00, + 0x70, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x14, 0x08, 0x00, 0x00, 0x75, 0x08, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x15, 0x08, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x12, 0x08, 0x00, 0x00, + 0x14, 0x08, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x1F, 0x10, 0x00, 0x00, 0x0E, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x10, 0x00, 0x00, + 0x1F, 0x10, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0x0E, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x27, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0xF9, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x10, 0x00, 0x00, + 0x22, 0x10, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x17, 0x12, 0x00, 0x00, 0x28, 0x10, 0x00, 0x00, + 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x2F, 0x10, 0x00, 0x00, 0x1F, 0x10, 0x00, 0x00, + 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x34, 0x10, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, + 0x2F, 0x10, 0x00, 0x00, 0x34, 0x10, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x1D, 0x12, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, + 0x17, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x39, 0x10, 0x00, 0x00, 0x1D, 0x12, 0x00, 0x00, + 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3B, 0x10, 0x00, 0x00, 0x39, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3E, 0x10, 0x00, 0x00, + 0x3B, 0x10, 0x00, 0x00, 0x3B, 0x10, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x39, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x43, 0x10, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x10, 0x00, 0x00, + 0x3E, 0x10, 0x00, 0x00, 0x43, 0x10, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x47, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x44, 0x10, 0x00, 0x00, 0x10, 0x0C, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x10, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0x47, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x4C, 0x10, 0x00, 0x00, 0x4A, 0x10, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x4F, 0x10, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, 0x47, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x51, 0x10, 0x00, 0x00, + 0x4F, 0x10, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x54, 0x10, 0x00, 0x00, 0x4C, 0x10, 0x00, 0x00, + 0x4C, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x57, 0x10, 0x00, 0x00, 0x51, 0x10, 0x00, 0x00, 0x51, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5A, 0x10, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0x54, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x5C, 0x10, 0x00, 0x00, 0x5A, 0x10, 0x00, 0x00, + 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x5F, 0x10, 0x00, 0x00, 0x5C, 0x10, 0x00, 0x00, 0x57, 0x10, 0x00, 0x00, + 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x00, + 0x15, 0x08, 0x00, 0x00, 0x5F, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x64, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x62, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x67, 0x10, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x5F, 0x10, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, + 0xB7, 0x04, 0x00, 0x00, 0x22, 0x06, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x22, 0x08, 0x00, 0x00, 0x6B, 0x08, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x24, 0x08, 0x00, 0x00, 0x70, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x08, 0x00, 0x00, + 0x75, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x27, 0x08, 0x00, 0x00, 0x22, 0x08, 0x00, 0x00, + 0x24, 0x08, 0x00, 0x00, 0x26, 0x08, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x86, 0x10, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, 0xF4, 0x06, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x88, 0x10, 0x00, 0x00, + 0x20, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x8B, 0x10, 0x00, 0x00, 0x88, 0x10, 0x00, 0x00, + 0xF9, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x8C, 0x10, 0x00, 0x00, 0x86, 0x10, 0x00, 0x00, 0x8B, 0x10, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x2A, 0x12, 0x00, 0x00, + 0x8C, 0x10, 0x00, 0x00, 0x59, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x93, 0x10, 0x00, 0x00, + 0x83, 0x10, 0x00, 0x00, 0x46, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x98, 0x10, 0x00, 0x00, 0x88, 0x10, 0x00, 0x00, + 0xF4, 0x06, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x99, 0x10, 0x00, 0x00, 0x93, 0x10, 0x00, 0x00, 0x98, 0x10, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x30, 0x12, 0x00, 0x00, + 0x99, 0x10, 0x00, 0x00, 0x2A, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x9D, 0x10, 0x00, 0x00, + 0x30, 0x12, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x9F, 0x10, 0x00, 0x00, 0x9D, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA2, 0x10, 0x00, 0x00, 0x9F, 0x10, 0x00, 0x00, 0x9F, 0x10, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA4, 0x10, 0x00, 0x00, + 0x9D, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xA7, 0x10, 0x00, 0x00, 0xA4, 0x10, 0x00, 0x00, + 0xA4, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xA8, 0x10, 0x00, 0x00, 0xA2, 0x10, 0x00, 0x00, 0xA7, 0x10, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAB, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xA8, 0x10, 0x00, 0x00, + 0x10, 0x0C, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xAE, 0x10, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xAB, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB0, 0x10, 0x00, 0x00, + 0xAE, 0x10, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xB3, 0x10, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, + 0xAB, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xB5, 0x10, 0x00, 0x00, 0xB3, 0x10, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB8, 0x10, 0x00, 0x00, + 0xB0, 0x10, 0x00, 0x00, 0xB0, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xBB, 0x10, 0x00, 0x00, 0xB5, 0x10, 0x00, 0x00, + 0xB5, 0x10, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xBE, 0x10, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0xB8, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC0, 0x10, 0x00, 0x00, + 0xBE, 0x10, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xC3, 0x10, 0x00, 0x00, 0xC0, 0x10, 0x00, 0x00, + 0xBB, 0x10, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0xC6, 0x10, 0x00, 0x00, 0x27, 0x08, 0x00, 0x00, 0xC3, 0x10, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0xC8, 0x10, 0x00, 0x00, + 0x64, 0x10, 0x00, 0x00, 0xC6, 0x10, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xCB, 0x10, 0x00, 0x00, 0x67, 0x10, 0x00, 0x00, + 0xC3, 0x10, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xE0, 0x10, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0xCB, 0x10, 0x00, 0x00, + 0x50, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, 0xE9, 0x10, 0x00, 0x00, + 0xE0, 0x10, 0x00, 0x00, 0xE0, 0x10, 0x00, 0x00, 0xE0, 0x10, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x37, 0x08, 0x00, 0x00, + 0xC8, 0x10, 0x00, 0x00, 0xE9, 0x10, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x38, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x38, 0x07, 0x00, 0x00, 0x37, 0x08, 0x00, 0x00, + 0x0C, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x39, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x56, 0x07, 0x00, 0x00, + 0x38, 0x08, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x1A, 0x05, 0x00, 0x00, 0x08, 0x05, 0x00, 0x00, 0x4F, 0x00, 0x09, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x1B, 0x05, 0x00, 0x00, 0x1A, 0x05, 0x00, 0x00, + 0x39, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x08, 0x05, 0x00, 0x00, 0x1B, 0x05, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x1C, 0x05, 0x00, 0x00, 0x1D, 0x05, 0x00, 0x00, 0x08, 0x05, 0x00, 0x00, + 0x61, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1D, 0x05, 0x00, 0x00, + 0x93, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..f58c4026f10eaf00aaa06485b970f6acdfba23e4 GIT binary patch literal 15936 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 M&e=aY;VU%$1+wdxIRF3v literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_frag.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_frag.txt new file mode 100644 index 000000000..ca7e98acf --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_frag.txt @@ -0,0 +1,207 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 1557 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %xe_frag_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 420 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %xe_texture "xe_texture" + OpName %gl_FragCoord "gl_FragCoord" + OpName %XeFsrRcasConstants "XeFsrRcasConstants" + OpMemberName %XeFsrRcasConstants 0 "xe_fsr_rcas_output_offset" + OpMemberName %XeFsrRcasConstants 1 "xe_fsr_rcas_sharpness_post_setup" + OpName %_ "" + OpName %xe_frag_color "xe_frag_color" + OpDecorate %xe_texture DescriptorSet 0 + OpDecorate %xe_texture Binding 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpMemberDecorate %XeFsrRcasConstants 0 Offset 16 + OpMemberDecorate %XeFsrRcasConstants 1 Offset 24 + OpDecorate %XeFsrRcasConstants Block + OpDecorate %xe_frag_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %v4float = OpTypeVector %float 4 + %v2uint = OpTypeVector %uint 2 + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 +%uint_2129764351 = OpConstant %uint 2129764351 + %float_2 = OpConstant %float 2 + %120 = OpTypeImage %float 2D 0 0 0 1 Unknown + %121 = OpTypeSampledImage %120 +%_ptr_UniformConstant_121 = OpTypePointer UniformConstant %121 + %xe_texture = OpVariable %_ptr_UniformConstant_121 UniformConstant + %int_0 = OpConstant %int 0 + %int_n1 = OpConstant %int -1 + %144 = OpConstantComposite %v2int %int_0 %int_n1 + %151 = OpConstantComposite %v2int %int_n1 %int_0 + %int_1 = OpConstant %int 1 + %164 = OpConstantComposite %v2int %int_1 %int_0 + %171 = OpConstantComposite %v2int %int_0 %int_1 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %float_0_25 = OpConstant %float 0.25 + %v2float = OpTypeVector %float 2 + %float_n4 = OpConstant %float -4 + %float_4 = OpConstant %float 4 +%float_n0_1875 = OpConstant %float -0.1875 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%XeFsrRcasConstants = OpTypeStruct %v2int %float +%_ptr_PushConstant_XeFsrRcasConstants = OpTypePointer PushConstant %XeFsrRcasConstants + %_ = OpVariable %_ptr_PushConstant_XeFsrRcasConstants PushConstant +%_ptr_PushConstant_v2int = OpTypePointer PushConstant %v2int +%_ptr_PushConstant_float = OpTypePointer PushConstant %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%xe_frag_color = OpVariable %_ptr_Output_v4float Output +%_ptr_Output_float = OpTypePointer Output %float + %uint_3 = OpConstant %uint 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %655 = OpLoad %v4float %gl_FragCoord + %656 = OpVectorShuffle %v2float %655 %655 0 1 + %657 = OpConvertFToS %v2int %656 + %662 = OpAccessChain %_ptr_PushConstant_v2int %_ %int_0 + %663 = OpLoad %v2int %662 + %664 = OpISub %v2int %657 %663 + %665 = OpBitcast %v2uint %664 + %668 = OpAccessChain %_ptr_PushConstant_float %_ %int_1 + %669 = OpLoad %float %668 + %670 = OpBitcast %uint %669 + %830 = OpBitcast %v2int %665 + %832 = OpIAdd %v2int %830 %144 + %1194 = OpLoad %121 %xe_texture + %1196 = OpImage %120 %1194 + %1197 = OpImageFetch %v4float %1196 %832 Lod %int_0 + %1199 = OpCompositeExtract %float %1197 0 + %1200 = OpCompositeExtract %float %1197 1 + %1201 = OpCompositeExtract %float %1197 2 + %836 = OpIAdd %v2int %830 %151 + %1207 = OpImage %120 %1194 + %1208 = OpImageFetch %v4float %1207 %836 Lod %int_0 + %1210 = OpCompositeExtract %float %1208 0 + %1211 = OpCompositeExtract %float %1208 1 + %1212 = OpCompositeExtract %float %1208 2 + %1218 = OpImage %120 %1194 + %1219 = OpImageFetch %v4float %1218 %830 Lod %int_0 + %1221 = OpCompositeExtract %float %1219 0 + %1222 = OpCompositeExtract %float %1219 1 + %1223 = OpCompositeExtract %float %1219 2 + %843 = OpIAdd %v2int %830 %164 + %1229 = OpImage %120 %1194 + %1230 = OpImageFetch %v4float %1229 %843 Lod %int_0 + %1232 = OpCompositeExtract %float %1230 0 + %1233 = OpCompositeExtract %float %1230 1 + %1234 = OpCompositeExtract %float %1230 2 + %847 = OpIAdd %v2int %830 %171 + %1240 = OpImage %120 %1194 + %1241 = OpImageFetch %v4float %1240 %847 Lod %int_0 + %1243 = OpCompositeExtract %float %1241 0 + %1244 = OpCompositeExtract %float %1241 1 + %1245 = OpCompositeExtract %float %1241 2 + %1371 = OpExtInst %float %1 FMin %1210 %1232 + %1372 = OpExtInst %float %1 FMin %1199 %1371 + %1007 = OpExtInst %float %1 FMin %1372 %1243 + %1378 = OpExtInst %float %1 FMin %1211 %1233 + %1379 = OpExtInst %float %1 FMin %1200 %1378 + %1013 = OpExtInst %float %1 FMin %1379 %1244 + %1385 = OpExtInst %float %1 FMin %1212 %1234 + %1386 = OpExtInst %float %1 FMin %1201 %1385 + %1019 = OpExtInst %float %1 FMin %1386 %1245 + %1392 = OpExtInst %float %1 FMax %1210 %1232 + %1393 = OpExtInst %float %1 FMax %1199 %1392 + %1025 = OpExtInst %float %1 FMax %1393 %1243 + %1399 = OpExtInst %float %1 FMax %1211 %1233 + %1400 = OpExtInst %float %1 FMax %1200 %1399 + %1031 = OpExtInst %float %1 FMax %1400 %1244 + %1406 = OpExtInst %float %1 FMax %1212 %1234 + %1407 = OpExtInst %float %1 FMax %1201 %1406 + %1037 = OpExtInst %float %1 FMax %1407 %1245 + %1040 = OpExtInst %float %1 FMin %1007 %1221 + %1416 = OpFDiv %float %float_0_25 %1025 + %1045 = OpFMul %float %1040 %1416 + %1048 = OpExtInst %float %1 FMin %1013 %1222 + %1428 = OpFDiv %float %float_0_25 %1031 + %1053 = OpFMul %float %1048 %1428 + %1056 = OpExtInst %float %1 FMin %1019 %1223 + %1440 = OpFDiv %float %float_0_25 %1037 + %1061 = OpFMul %float %1056 %1440 + %1066 = OpExtInst %float %1 FMax %1025 %1221 + %1067 = OpFSub %float %float_1 %1066 + %1070 = OpFMul %float %float_4 %1007 + %1073 = OpFAdd %float %1070 %float_n4 + %1452 = OpFDiv %float %float_1 %1073 + %1075 = OpFMul %float %1067 %1452 + %1080 = OpExtInst %float %1 FMax %1031 %1222 + %1081 = OpFSub %float %float_1 %1080 + %1084 = OpFMul %float %float_4 %1013 + %1087 = OpFAdd %float %1084 %float_n4 + %1464 = OpFDiv %float %float_1 %1087 + %1089 = OpFMul %float %1081 %1464 + %1094 = OpExtInst %float %1 FMax %1037 %1223 + %1095 = OpFSub %float %float_1 %1094 + %1098 = OpFMul %float %float_4 %1019 + %1101 = OpFAdd %float %1098 %float_n4 + %1476 = OpFDiv %float %float_1 %1101 + %1103 = OpFMul %float %1095 %1476 + %1105 = OpFNegate %float %1045 + %1107 = OpExtInst %float %1 FMax %1105 %1075 + %1109 = OpFNegate %float %1053 + %1111 = OpExtInst %float %1 FMax %1109 %1089 + %1113 = OpFNegate %float %1061 + %1115 = OpExtInst %float %1 FMax %1113 %1103 + %1488 = OpExtInst %float %1 FMax %1111 %1115 + %1489 = OpExtInst %float %1 FMax %1107 %1488 + %1122 = OpExtInst %float %1 FMin %1489 %float_0 + %1123 = OpExtInst %float %1 FMax %float_n0_1875 %1122 + %1126 = OpBitcast %float %670 + %1127 = OpFMul %float %1123 %1126 + %1130 = OpFMul %float %float_4 %1127 + %1132 = OpFAdd %float %1130 %float_1 + %1506 = OpBitcast %uint %1132 + %1507 = OpISub %uint %uint_2129764351 %1506 + %1508 = OpBitcast %float %1507 + %1511 = OpFNegate %float %1508 + %1513 = OpFMul %float %1511 %1132 + %1515 = OpFAdd %float %1513 %float_2 + %1516 = OpFMul %float %1508 %1515 + %1548 = OpFAdd %float %1199 %1210 + %1549 = OpFAdd %float %1548 %1243 + %1550 = OpFAdd %float %1549 %1232 + %1148 = OpFMul %float %1127 %1550 + %1150 = OpFAdd %float %1148 %1221 + %1152 = OpFMul %float %1150 %1516 + %1551 = OpFAdd %float %1200 %1211 + %1552 = OpFAdd %float %1551 %1244 + %1553 = OpFAdd %float %1552 %1233 + %1167 = OpFMul %float %1127 %1553 + %1169 = OpFAdd %float %1167 %1222 + %1171 = OpFMul %float %1169 %1516 + %1554 = OpFAdd %float %1201 %1212 + %1555 = OpFAdd %float %1554 %1245 + %1556 = OpFAdd %float %1555 %1234 + %1186 = OpFMul %float %1127 %1556 + %1188 = OpFAdd %float %1186 %1223 + %1190 = OpFMul %float %1188 %1516 + %688 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_0 + OpStore %688 %1152 + %690 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_1 + OpStore %690 %1171 + %692 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_2 + OpStore %692 %1190 + %694 = OpAccessChain %_ptr_Output_float %xe_frag_color %uint_3 + OpStore %694 %float_1 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.h new file mode 100644 index 000000000..e5e48733c --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.h @@ -0,0 +1,109 @@ +// generated from `xb genspirv` +// source: guest_output_triangle_strip_rect.vert +const uint8_t guest_output_triangle_strip_rect_vert[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, + 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, + 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x69, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x58, 0x65, 0x54, 0x72, 0x69, 0x61, 0x6E, 0x67, 0x6C, 0x65, 0x53, 0x74, + 0x72, 0x69, 0x70, 0x52, 0x65, 0x63, 0x74, 0x43, 0x6F, 0x6E, 0x73, 0x74, + 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0B, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, + 0x72, 0x69, 0x61, 0x6E, 0x67, 0x6C, 0x65, 0x5F, 0x73, 0x74, 0x72, 0x69, + 0x70, 0x5F, 0x72, 0x65, 0x63, 0x74, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x74, 0x72, 0x69, 0x61, 0x6E, + 0x67, 0x6C, 0x65, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x70, 0x5F, 0x72, 0x65, + 0x63, 0x74, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x56, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x49, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x20, 0x00, 0x04, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0xC7, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x70, 0x00, 0x04, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, + 0x2D, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..3c2de3d30a70a3f4cad64f7f7257ee3c70d5f9ea GIT binary patch literal 1252 zcmZ{iO=}ZT6o#*fNo%Y9u(tKHOpV{I(S;U4RK!ITx`;-^T?p;84Ae=<%%X@3UHHe` z_?O%WKF{QixbcKH_nh~>_k1PIMrS63Q(-R5h6iD4%b@`i!hC2~XK#0~+bzod?xV*K zRV;*NB~-H*c#fS0pGH}(KL=JplaHDmzFH{$)wIYx%iZD8u%Avoq?0l|A%vas`reI; ztjxwa>`bLMU&Uv6ImkYz>RQyXrq;q+;QW}rpJb!_@F*RWlkE6ix>vp$=S4Zn%R*h7 z>C|Z_=@3mN9u}&H6Xe73!9kIh+_z&+YyU@E*ou8DnGkb6cR;Vx;oju?PWYM|=ho(@ z$6wC;a-KbMscQh=)V+3ywVG2ar`9*>eA8pw);HfhumUT;iN;?~e+{qAnSgcl{*uQ2 zP8)oiv*r@wGsM1?e;Q-=F1Jc-tpwQ9R*cW$Yvw=4X?|;)-(KZ}`4>5@A#Z+T_bKQ7 za`bEc1!8MiSG}<_%h}I5X96_qU#iY&ef#tNikDa&U&Z(RHak~;4XBZ?XS$BB#@Kmo z1FeqlRMFQL_PmYne%xc7vx{#&u|pmA;m$R+zhGLNFZp)n^T0j0%V#K^e+9@JJO3JR z_AkWFbun`NILGD4$^VGl2EM#A{)}9LFK>^>vDPMj0_uJ``1a!->V9tE%YP@2{oMq< zlm8aETln_v-CHvJoV(isYQ1yc?%00Z?|tC^(EU7TgU0HAli>;Y E1K{La5C8xG literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.txt new file mode 100644 index 000000000..e78174fa6 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.txt @@ -0,0 +1,74 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 47 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %gl_VertexIndex + OpSource ESSL 310 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpName %_ "" + OpName %XeTriangleStripRectConstants "XeTriangleStripRectConstants" + OpMemberName %XeTriangleStripRectConstants 0 "xe_triangle_strip_rect_offset" + OpMemberName %XeTriangleStripRectConstants 1 "xe_triangle_strip_rect_size" + OpName %__0 "" + OpName %gl_VertexIndex "gl_VertexIndex" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpDecorate %gl_PerVertex Block + OpMemberDecorate %XeTriangleStripRectConstants 0 Offset 0 + OpMemberDecorate %XeTriangleStripRectConstants 1 Offset 8 + OpDecorate %XeTriangleStripRectConstants Block + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float %float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v2float = OpTypeVector %float 2 +%XeTriangleStripRectConstants = OpTypeStruct %v2float %v2float +%_ptr_PushConstant_XeTriangleStripRectConstants = OpTypePointer PushConstant %XeTriangleStripRectConstants + %__0 = OpVariable %_ptr_PushConstant_XeTriangleStripRectConstants PushConstant +%_ptr_PushConstant_v2float = OpTypePointer PushConstant %v2float +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %29 = OpConstantComposite %v2uint %uint_0 %uint_1 + %int_1 = OpConstant %int 1 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %46 = OpConstantComposite %v2uint %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_PushConstant_v2float %__0 %int_0 + %19 = OpLoad %v2float %18 + %22 = OpLoad %int %gl_VertexIndex + %24 = OpBitcast %uint %22 + %26 = OpCompositeConstruct %v2uint %24 %24 + %30 = OpShiftRightLogical %v2uint %26 %29 + %32 = OpBitwiseAnd %v2uint %30 %46 + %33 = OpConvertUToF %v2float %32 + %35 = OpAccessChain %_ptr_PushConstant_v2float %__0 %int_1 + %36 = OpLoad %v2float %35 + %37 = OpFMul %v2float %33 %36 + %38 = OpFAdd %v2float %19 %37 + %41 = OpCompositeExtract %float %38 0 + %42 = OpCompositeExtract %float %38 1 + %43 = OpCompositeConstruct %v4float %41 %42 %float_0 %float_1 + %45 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %45 %43 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.h new file mode 100644 index 000000000..dc4a7f3df --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.h @@ -0,0 +1,71 @@ +// generated from `xb genspirv` +// source: immediate.frag +const uint8_t immediate_frag[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x66, 0x72, 0x61, 0x67, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, + 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x76, 0x61, 0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x69, 0x6D, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, + 0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x76, + 0x61, 0x72, 0x5F, 0x74, 0x65, 0x78, 0x63, 0x6F, 0x6F, 0x72, 0x64, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x09, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x58, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..d0d4c2beb39b4e5ae4d0e3df14bca25110865ce1 GIT binary patch literal 804 zcmYk3%}&Bl5QPT{SV2KVL1JP+mL|l75)-4_ZrHhTM;fSxL~K&4;RSqWHzuC%($-Eg zojGTI@6@YK_RK2QG~b4{lde_8n6<3oY%qVCPe0Pt^yc=OjJA0$k(`d|YO?4iT)z|8 zm$sxQ(j(;((-3){>o-$jU2M6gu36M=qs2=SzAl#WTbu~P(@XqUm=v^lU%ef_-fW`P zI!vQQ8g0|hBogiFn4i@tvW(+oWr22J4iabs{0oP`o|~1&m`jXz5<4Iu!Dlu?5By7q z{_(iLo^=bq=5A7+Hz;DBon?Mc_pizyYYvyj@5^h*j-&(4FE=!Kj)v2ujdGzaKNpQ>q=Qm-$V`2KSJNZ3Xdcl(q=KsK5 z36tBGA~XFx>UbN%8-B#pWgo=y2L1*6gV})_Gat;1Qz`wQl}$c(CI^0ZPqS}zB*xw8 QN6w?BaW}YMZFeXA1AaL#>Hq)$ literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.txt new file mode 100644 index 000000000..54583cc1b --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.txt @@ -0,0 +1,54 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %xe_frag_color %xe_var_color %xe_var_texcoord + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %xe_frag_color "xe_frag_color" + OpName %xe_var_color "xe_var_color" + OpName %xe_immediate_texture "xe_immediate_texture" + OpName %xe_var_texcoord "xe_var_texcoord" + OpDecorate %xe_frag_color RelaxedPrecision + OpDecorate %xe_frag_color Location 0 + OpDecorate %xe_var_color RelaxedPrecision + OpDecorate %xe_var_color Location 1 + OpDecorate %12 RelaxedPrecision + OpDecorate %xe_immediate_texture RelaxedPrecision + OpDecorate %xe_immediate_texture DescriptorSet 0 + OpDecorate %xe_immediate_texture Binding 0 + OpDecorate %17 RelaxedPrecision + OpDecorate %xe_var_texcoord Location 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%xe_frag_color = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%xe_var_color = OpVariable %_ptr_Input_v4float Input + %13 = OpTypeImage %float 2D 0 0 0 1 Unknown + %14 = OpTypeSampledImage %13 +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 +%xe_immediate_texture = OpVariable %_ptr_UniformConstant_14 UniformConstant + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%xe_var_texcoord = OpVariable %_ptr_Input_v2float Input + %float_0 = OpConstant %float 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpLoad %v4float %xe_var_color + %17 = OpLoad %14 %xe_immediate_texture + %21 = OpLoad %v2float %xe_var_texcoord + %23 = OpImageSampleExplicitLod %v4float %17 %21 Lod %float_0 + %24 = OpFMul %v4float %12 %23 + OpStore %xe_frag_color %24 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.h b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.h new file mode 100644 index 000000000..1a1299653 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.h @@ -0,0 +1,117 @@ +// generated from `xb genspirv` +// source: immediate.vert +const uint8_t immediate_vert[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x76, 0x61, 0x72, 0x5F, 0x74, 0x65, 0x78, 0x63, 0x6F, + 0x6F, 0x72, 0x64, 0x00, 0x05, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x78, 0x65, 0x5F, 0x61, 0x74, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x78, 0x63, + 0x6F, 0x6F, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x76, 0x61, 0x72, 0x5F, 0x63, + 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x61, 0x74, 0x74, 0x72, 0x5F, + 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, + 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, + 0x6F, 0x69, 0x6E, 0x74, 0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x00, 0x18, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x61, + 0x74, 0x74, 0x72, 0x5F, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x58, 0x65, 0x50, 0x75, 0x73, 0x68, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, + 0x6E, 0x74, 0x73, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x65, 0x5F, 0x63, 0x6F, 0x6F, 0x72, 0x64, + 0x69, 0x6E, 0x61, 0x74, 0x65, 0x5F, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5F, + 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x69, 0x6E, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2B, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x04, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x07, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x03, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, +}; diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.spv b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..b2c19c5f0658e04ef87ad8182aeb67139529f967 GIT binary patch literal 1352 zcmYk5%WD)t5XL(@+1)%eG3MnnnYj3t;6Z~RzVNUfa##t7w_(@}aUjlwnKlGZf_U}s z^&O?;1M)rhs zU-v)99dPk3;=25K+d=G$8qAH1y2E;dBYs`KoO|k**Mz+#YoIfgZmT~%OvCt6;RT(< z!oLidUBKaA(n(*@501T3@3JuUd3VIG=p+}OdSLby>s^sez32zCGq@eaGkdH_y$xY< z;i(7ajlgXRZ|O{>NSzmp;Jw*a>fmUEyP= z;Fxz^HgmpHHTvewFblhgF}s2P6?#jB$%FqLxI3~_DRce|++Er5%<@qjd$=!!MuPVHd{sU>6V;ukh literal 0 HcmV?d00001 diff --git a/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.txt b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.txt new file mode 100644 index 000000000..d5b18f232 --- /dev/null +++ b/src/xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.txt @@ -0,0 +1,82 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %xe_var_texcoord %xe_attr_texcoord %xe_var_color %xe_attr_color %_ %xe_attr_position + OpSource ESSL 310 + OpName %main "main" + OpName %xe_var_texcoord "xe_var_texcoord" + OpName %xe_attr_texcoord "xe_attr_texcoord" + OpName %xe_var_color "xe_var_color" + OpName %xe_attr_color "xe_attr_color" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpName %_ "" + OpName %xe_attr_position "xe_attr_position" + OpName %XePushConstants "XePushConstants" + OpMemberName %XePushConstants 0 "xe_coordinate_space_size_inv" + OpName %__0 "" + OpDecorate %xe_var_texcoord Location 0 + OpDecorate %xe_attr_texcoord Location 1 + OpDecorate %xe_var_color RelaxedPrecision + OpDecorate %xe_var_color Location 1 + OpDecorate %xe_attr_color RelaxedPrecision + OpDecorate %xe_attr_color Location 2 + OpDecorate %18 RelaxedPrecision + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpDecorate %gl_PerVertex Block + OpDecorate %xe_attr_position Location 0 + OpMemberDecorate %XePushConstants 0 Offset 0 + OpDecorate %XePushConstants Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v2float = OpTypePointer Output %v2float +%xe_var_texcoord = OpVariable %_ptr_Output_v2float Output +%_ptr_Input_v2float = OpTypePointer Input %v2float +%xe_attr_texcoord = OpVariable %_ptr_Input_v2float Input + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%xe_var_color = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%xe_attr_color = OpVariable %_ptr_Input_v4float Input +%gl_PerVertex = OpTypeStruct %v4float %float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%xe_attr_position = OpVariable %_ptr_Input_v2float Input +%XePushConstants = OpTypeStruct %v2float +%_ptr_PushConstant_XePushConstants = OpTypePointer PushConstant %XePushConstants + %__0 = OpVariable %_ptr_PushConstant_XePushConstants PushConstant +%_ptr_PushConstant_v2float = OpTypePointer PushConstant %v2float + %float_2 = OpConstant %float 2 + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %43 = OpConstantComposite %v2float %float_1 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpLoad %v2float %xe_attr_texcoord + OpStore %xe_var_texcoord %12 + %18 = OpLoad %v4float %xe_attr_color + OpStore %xe_var_color %18 + %25 = OpLoad %v2float %xe_attr_position + %30 = OpAccessChain %_ptr_PushConstant_v2float %__0 %int_0 + %31 = OpLoad %v2float %30 + %32 = OpFMul %v2float %25 %31 + %34 = OpVectorTimesScalar %v2float %32 %float_2 + %37 = OpFSub %v2float %34 %43 + %39 = OpCompositeExtract %float %37 0 + %40 = OpCompositeExtract %float %37 1 + %41 = OpCompositeConstruct %v4float %39 %40 %float_0 %float_1 + %42 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %42 %41 + OpReturn + OpFunctionEnd diff --git a/src/xenia/ui/shaders/dither_8bpc.xesli b/src/xenia/ui/shaders/dither_8bpc.xesli new file mode 100644 index 000000000..1f8b2946c --- /dev/null +++ b/src/xenia/ui/shaders/dither_8bpc.xesli @@ -0,0 +1,34 @@ +#ifndef XENIA_UI_SHADERS_DITHER_8BPC_XESLI_ +#define XENIA_UI_SHADERS_DITHER_8BPC_XESLI_ + +#include "xesl.xesli" + +#include "noise.xesli" + +xesl_static const float xe_dither_8bpc_noise[] = { + // The conversion to 8bpc in the fixed-function output-merger is done as + // floor(saturate(color) * 255.0 + 0.5). This dithering function effectively + // replaces that 0.5 offset, done for rounding to the nearest, with the noise. + // To do that, first, 0.5/255 needs to be subtracted from the color, and + // then the noise value `[0, 255/256] / 255` from xe_dither_8bpc_noise needs + // to be added. However, due to rounding (because division by 255.0 is + // inexact), subtracting 0.5/255 from exact 8-bit integer color values + // (including, for instance, 1.0, or 255/255) may result in the result being + // smaller than the original exact 8-bit value where the noise value is 0. So, + // remapping the noise to the `[0.5/256, 255.5/256] / 255` range. Another way + // of preventing rounding issues is doing it manually and returning only exact + // integer 8-bit color values divided by 255, but that would assume that the + // render target is specifically 8bpc, so it's better to avoid that in case + // the window system, for instance, provides a 10bpc render target (for which + // dithering is still done here for more consistency between displays, but + // some addition precision would still be desirable). + XeBlueNoise16x16Values0Until256(1.0 / 256.0 / 255.0, + (-0.5 + 0.5 / 256.0) / 255.0) +}; + +float XeDitherOffset8bpc(xesl_uint2 pixel_coord) { + pixel_coord &= 15u; + return xe_dither_8bpc_noise[pixel_coord.y * 16u + pixel_coord.x]; +} + +#endif // XENIA_UI_SHADERS_DITHER_8BPC_XESLI_ diff --git a/src/xenia/ui/shaders/guest_output_bilinear.frag b/src/xenia/ui/shaders/guest_output_bilinear.frag new file mode 100644 index 000000000..42fc74087 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_bilinear.frag @@ -0,0 +1,3 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require +#include "guest_output_bilinear.glsli" diff --git a/src/xenia/ui/shaders/guest_output_bilinear.glsli b/src/xenia/ui/shaders/guest_output_bilinear.glsli new file mode 100644 index 000000000..bffd8842e --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_bilinear.glsli @@ -0,0 +1,38 @@ +// At least #version 420. + +#if XE_GUEST_OUTPUT_DITHER + #include "dither_8bpc.xesli" +#endif // XE_GUEST_OUTPUT_DITHER + +layout(push_constant) uniform XeBilinearConstants { + // 16 occupied by the vertex shader. + layout(offset = 16) ivec2 xe_bilinear_output_offset; + layout(offset = 24) vec2 xe_bilinear_output_size_inv; +}; + +layout(set = 0, binding = 0) uniform sampler2D xe_texture; + +layout(location = 0) out vec4 xe_frag_color; + +void main() { + uvec2 pixel_coord = uvec2(ivec2(gl_FragCoord.xy) - xe_bilinear_output_offset); + // + 0.5 so the origin is at the pixel center, and at 1:1 the original pixel + // is taken. + // Interpolating the four colors in the perceptual space because doing it in + // linear space causes, in particular, bright text on a dark background to + // become too thick, and aliasing of bright parts on top of dark areas to be + // too apparent (4D5307E6 HUD, for example, mainly the edges of the + // multiplayer score bars). + xe_frag_color.rgb = + textureLod(xe_texture, + (vec2(pixel_coord) + 0.5) * xe_bilinear_output_size_inv, + 0.0).rgb; + #if XE_GUEST_OUTPUT_DITHER + // Clamping because on Vulkan, the surface may specify any format, including + // floating-point. + xe_frag_color.rgb = + clamp(xe_frag_color.rgb + XeDitherOffset8bpc(pixel_coord), + vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0)); + #endif // XE_GUEST_OUTPUT_DITHER + xe_frag_color.a = 1.0; +} diff --git a/src/xenia/ui/shaders/guest_output_bilinear.hlsli b/src/xenia/ui/shaders/guest_output_bilinear.hlsli new file mode 100644 index 000000000..6685bfc29 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_bilinear.hlsli @@ -0,0 +1,31 @@ +#if XE_GUEST_OUTPUT_DITHER + #include "dither_8bpc.xesli" +#endif // XE_GUEST_OUTPUT_DITHER + +cbuffer XeBilinearConstants : register(b0) { + int2 xe_bilinear_output_offset; + float2 xe_bilinear_output_size_inv; +}; + +Texture2D xe_texture : register(t0); +SamplerState xe_sampler_linear_clamp : register(s0); + +float4 main(float4 xe_position : SV_Position) : SV_Target { + uint2 pixel_coord = uint2(int2(xe_position.xy) - xe_bilinear_output_offset); + // + 0.5 so the origin is at the pixel center, and at 1:1 the original pixel + // is taken. + // Interpolating the four colors in the perceptual space because doing it in + // linear space causes, in particular, bright text on a dark background to + // become too thick, and aliasing of bright parts on top of dark areas to be + // too apparent (4D5307E6 HUD, for example, mainly the edges of the + // multiplayer score bars). + float3 color = xe_texture.SampleLevel( + xe_sampler_linear_clamp, + (float2(pixel_coord) + 0.5f) * xe_bilinear_output_size_inv, + 0.0f); + #if XE_GUEST_OUTPUT_DITHER + // Not clamping because a normalized format is explicitly requested on DXGI. + color += XeDitherOffset8bpc(pixel_coord); + #endif // XE_GUEST_OUTPUT_DITHER + return float4(color, 1.0f); +} diff --git a/src/xenia/ui/shaders/guest_output_bilinear.ps.hlsl b/src/xenia/ui/shaders/guest_output_bilinear.ps.hlsl new file mode 100644 index 000000000..dd4e37b28 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_bilinear.ps.hlsl @@ -0,0 +1 @@ +#include "guest_output_bilinear.hlsli" diff --git a/src/xenia/ui/shaders/guest_output_bilinear_dither.frag b/src/xenia/ui/shaders/guest_output_bilinear_dither.frag new file mode 100644 index 000000000..b78b5a9a4 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_bilinear_dither.frag @@ -0,0 +1,4 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require +#define XE_GUEST_OUTPUT_DITHER 1 +#include "guest_output_bilinear.glsli" diff --git a/src/xenia/ui/shaders/guest_output_bilinear_dither.ps.hlsl b/src/xenia/ui/shaders/guest_output_bilinear_dither.ps.hlsl new file mode 100644 index 000000000..13995e2bb --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_bilinear_dither.ps.hlsl @@ -0,0 +1,2 @@ +#define XE_GUEST_OUTPUT_DITHER 1 +#include "guest_output_bilinear.hlsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_resample.frag b/src/xenia/ui/shaders/guest_output_ffx_cas_resample.frag new file mode 100644 index 000000000..a495b7bb4 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_resample.frag @@ -0,0 +1,3 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require +#include "guest_output_ffx_cas_resample.glsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_resample.glsli b/src/xenia/ui/shaders/guest_output_ffx_cas_resample.glsli new file mode 100644 index 000000000..21a35528e --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_resample.glsli @@ -0,0 +1,57 @@ +// At least #version 420. + +#if XE_GUEST_OUTPUT_DITHER + #include "dither_8bpc.xesli" +#endif // XE_GUEST_OUTPUT_DITHER + +layout(push_constant) uniform XeCasResampleConstants { + // 16 occupied by the vertex shader. + layout(offset = 16) ivec2 xe_cas_output_offset; + // CasSetup const0.xy. + layout(offset = 24) vec2 xe_cas_input_output_size_ratio; + // CasSetup const1.x. + layout(offset = 32) float xe_cas_sharpness_post_setup; +}; + +layout(set = 0, binding = 0) uniform sampler2D xe_texture; + +layout(location = 0) out vec4 xe_frag_color; + +#define A_GPU 1 +#define A_GLSL 1 +#include "../../../../third_party/FidelityFX-CAS/ffx-cas/ffx_a.h" +vec3 CasLoad(ivec2 p) { + return texelFetch(xe_texture, p, 0).rgb; +} +void CasInput(inout float r, inout float g, inout float b) { + // Linear conversion approximation as recommended in the CAS presentation. + r *= r; + g *= g; + b *= b; +} +#include "../../../../third_party/FidelityFX-CAS/ffx-cas/ffx_cas.h" + +void main() { + uvec2 pixel_coord = uvec2(ivec2(gl_FragCoord.xy) - xe_cas_output_offset); + // CasSetup with smaller push constant usage. + uvec4 cas_const_0 = + uvec4(floatBitsToUint(xe_cas_input_output_size_ratio), + floatBitsToUint(0.5 * xe_cas_input_output_size_ratio - 0.5)); + uvec4 cas_const_1 = + uvec4(floatBitsToUint(xe_cas_sharpness_post_setup), + packHalf2x16(vec2(xe_cas_sharpness_post_setup, 0.0)), + floatBitsToUint(8.0 * xe_cas_input_output_size_ratio.x), 0u); + CasFilter(xe_frag_color.r, xe_frag_color.g, xe_frag_color.b, pixel_coord, + cas_const_0, cas_const_1, false); + // Linear conversion approximation as recommended in the CAS presentation. + xe_frag_color.rgb = sqrt(xe_frag_color.rgb); + #if XE_GUEST_OUTPUT_DITHER + // Clamping because on Vulkan, the surface may specify any format, including + // floating-point. + xe_frag_color.rgb = + clamp(xe_frag_color.rgb + XeDitherOffset8bpc(pixel_coord), + vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0)); + #endif // XE_GUEST_OUTPUT_DITHER + // Force alpha to 1 to make sure the surface won't be translucent. + xe_frag_color.a = 1.0; +} diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_resample.hlsli b/src/xenia/ui/shaders/guest_output_ffx_cas_resample.hlsli new file mode 100644 index 000000000..843a00caa --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_resample.hlsli @@ -0,0 +1,49 @@ +#if XE_GUEST_OUTPUT_DITHER + #include "dither_8bpc.xesli" +#endif // XE_GUEST_OUTPUT_DITHER + +cbuffer XeCasResampleConstants : register(b0) { + int2 xe_cas_output_offset; + // CasSetup const0.xy. + float2 xe_cas_input_output_size_ratio; + // CasSetup const1.x. + float xe_cas_sharpness_post_setup; +}; + +Texture2D xe_texture : register(t0); + +#define A_GPU 1 +#define A_HLSL 1 +#include "../../../../third_party/FidelityFX-CAS/ffx-cas/ffx_a.h" +float3 CasLoad(int2 p) { + return xe_texture.Load(int3(p, 0)).rgb; +} +void CasInput(inout float r, inout float g, inout float b) { + // Linear conversion approximation as recommended in the CAS presentation. + r *= r; + g *= g; + b *= b; +} +#include "../../../../third_party/FidelityFX-CAS/ffx-cas/ffx_cas.h" + +float4 main(float4 xe_position : SV_Position) : SV_Target { + uint2 pixel_coord = uint2(int2(xe_position.xy) - xe_cas_output_offset); + // CasSetup with smaller root signature usage. + uint4 cas_const_0 = + uint4(asuint(xe_cas_input_output_size_ratio), + asuint(0.5f * xe_cas_input_output_size_ratio - 0.5f)); + uint4 cas_const_1 = + uint4(asuint(xe_cas_sharpness_post_setup), + f32tof16(xe_cas_sharpness_post_setup), + asuint(8.0f * xe_cas_input_output_size_ratio.x), 0u); + float3 cas_color; + CasFilter(cas_color.r, cas_color.g, cas_color.b, pixel_coord, cas_const_0, + cas_const_1, false); + // Linear conversion approximation as recommended in the CAS presentation. + cas_color = sqrt(cas_color); + #if XE_GUEST_OUTPUT_DITHER + // Not clamping because a normalized format is explicitly requested on DXGI. + cas_color += XeDitherOffset8bpc(pixel_coord); + #endif // XE_GUEST_OUTPUT_DITHER + return float4(cas_color, 1.0f); +} diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_resample.ps.hlsl b/src/xenia/ui/shaders/guest_output_ffx_cas_resample.ps.hlsl new file mode 100644 index 000000000..f970e5404 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_resample.ps.hlsl @@ -0,0 +1 @@ +#include "guest_output_ffx_cas_resample.hlsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_resample_dither.frag b/src/xenia/ui/shaders/guest_output_ffx_cas_resample_dither.frag new file mode 100644 index 000000000..476c02dd3 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_resample_dither.frag @@ -0,0 +1,4 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require +#define XE_GUEST_OUTPUT_DITHER 1 +#include "guest_output_ffx_cas_resample.glsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_resample_dither.ps.hlsl b/src/xenia/ui/shaders/guest_output_ffx_cas_resample_dither.ps.hlsl new file mode 100644 index 000000000..4e76285b2 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_resample_dither.ps.hlsl @@ -0,0 +1,2 @@ +#define XE_GUEST_OUTPUT_DITHER 1 +#include "guest_output_ffx_cas_resample.hlsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.frag b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.frag new file mode 100644 index 000000000..059ea939d --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.frag @@ -0,0 +1,3 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require +#include "guest_output_ffx_cas_sharpen.glsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.glsli b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.glsli new file mode 100644 index 000000000..0cb6ea359 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.glsli @@ -0,0 +1,53 @@ +// At least #version 420. + +#if XE_GUEST_OUTPUT_DITHER + #include "dither_8bpc.xesli" +#endif // XE_GUEST_OUTPUT_DITHER + +layout(push_constant) uniform XeCasSharpenConstants { + // 16 occupied by the vertex shader. + layout(offset = 16) ivec2 xe_cas_output_offset; + // CasSetup const1.x. + layout(offset = 24) float xe_cas_sharpness_post_setup; +}; + +layout(set = 0, binding = 0) uniform sampler2D xe_texture; + +layout(location = 0) out vec4 xe_frag_color; + +#define A_GPU 1 +#define A_GLSL 1 +#include "../../../../third_party/FidelityFX-CAS/ffx-cas/ffx_a.h" +vec3 CasLoad(ivec2 p) { + return texelFetch(xe_texture, p, 0).rgb; +} +void CasInput(inout float r, inout float g, inout float b) { + // Linear conversion approximation as recommended in the CAS presentation. + r *= r; + g *= g; + b *= b; +} +#include "../../../../third_party/FidelityFX-CAS/ffx-cas/ffx_cas.h" + +void main() { + uvec2 pixel_coord = uvec2(ivec2(gl_FragCoord.xy) - xe_cas_output_offset); + // CasSetup with smaller push constant usage. + uvec4 cas_const_0 = floatBitsToUint(vec4(1.0, 1.0, 0.0, 0.0)); + uvec4 cas_const_1 = + uvec4(floatBitsToUint(xe_cas_sharpness_post_setup), + packHalf2x16(vec2(xe_cas_sharpness_post_setup, 0.0)), + floatBitsToUint(8.0), 0u); + CasFilter(xe_frag_color.r, xe_frag_color.g, xe_frag_color.b, pixel_coord, + cas_const_0, cas_const_1, false); + // Linear conversion approximation as recommended in the CAS presentation. + xe_frag_color.rgb = sqrt(xe_frag_color.rgb); + #if XE_GUEST_OUTPUT_DITHER + // Clamping because on Vulkan, the surface may specify any format, including + // floating-point. + xe_frag_color.rgb = + clamp(xe_frag_color.rgb + XeDitherOffset8bpc(pixel_coord), + vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0)); + #endif // XE_GUEST_OUTPUT_DITHER + // Force alpha to 1 to make sure the surface won't be translucent. + xe_frag_color.a = 1.0; +} diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.hlsli b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.hlsli new file mode 100644 index 000000000..0c47cc0e3 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.hlsli @@ -0,0 +1,44 @@ +#if XE_GUEST_OUTPUT_DITHER + #include "dither_8bpc.xesli" +#endif // XE_GUEST_OUTPUT_DITHER + +cbuffer XeCasSharpenConstants : register(b0) { + int2 xe_cas_output_offset; + // CasSetup const1.x. + float xe_cas_sharpness_post_setup; +}; + +Texture2D xe_texture : register(t0); + +#define A_GPU 1 +#define A_HLSL 1 +#include "../../../../third_party/FidelityFX-CAS/ffx-cas/ffx_a.h" +float3 CasLoad(int2 p) { + return xe_texture.Load(int3(p, 0)).rgb; +} +void CasInput(inout float r, inout float g, inout float b) { + // Linear conversion approximation as recommended in the CAS presentation. + r *= r; + g *= g; + b *= b; +} +#include "../../../../third_party/FidelityFX-CAS/ffx-cas/ffx_cas.h" + +float4 main(float4 xe_position : SV_Position) : SV_Target { + uint2 pixel_coord = uint2(int2(xe_position.xy) - xe_cas_output_offset); + // CasSetup with smaller root signature usage. + uint4 cas_const_0 = asuint(float4(1.0f, 1.0f, 0.0f, 0.0f)); + uint4 cas_const_1 = + uint4(asuint(xe_cas_sharpness_post_setup), + f32tof16(xe_cas_sharpness_post_setup), asuint(8.0f), 0u); + float3 cas_color; + CasFilter(cas_color.r, cas_color.g, cas_color.b, pixel_coord, cas_const_0, + cas_const_1, true); + // Linear conversion approximation as recommended in the CAS presentation. + cas_color = sqrt(cas_color); + #if XE_GUEST_OUTPUT_DITHER + // Not clamping because a normalized format is explicitly requested on DXGI. + cas_color += XeDitherOffset8bpc(pixel_coord); + #endif // XE_GUEST_OUTPUT_DITHER + return float4(cas_color, 1.0f); +} diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.ps.hlsl b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.ps.hlsl new file mode 100644 index 000000000..3ff89fe34 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen.ps.hlsl @@ -0,0 +1 @@ +#include "guest_output_ffx_cas_sharpen.hlsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen_dither.frag b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen_dither.frag new file mode 100644 index 000000000..60c6b2f8b --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen_dither.frag @@ -0,0 +1,4 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require +#define XE_GUEST_OUTPUT_DITHER 1 +#include "guest_output_ffx_cas_sharpen.glsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen_dither.ps.hlsl b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen_dither.ps.hlsl new file mode 100644 index 000000000..3ad7b01b7 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_cas_sharpen_dither.ps.hlsl @@ -0,0 +1,2 @@ +#define XE_GUEST_OUTPUT_DITHER 1 +#include "guest_output_ffx_cas_sharpen.hlsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_fsr_easu.frag b/src/xenia/ui/shaders/guest_output_ffx_fsr_easu.frag new file mode 100644 index 000000000..d5b2f1900 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_fsr_easu.frag @@ -0,0 +1,38 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require + +layout(push_constant) uniform XeFsrEasuConstants { + // 16 occupied by the vertex shader. + layout(offset = 16) vec2 xe_fsr_easu_input_output_size_ratio; + layout(offset = 24) vec2 xe_fsr_easu_input_size_inv; +}; + +layout(set = 0, binding = 0) uniform sampler2D xe_texture; + +layout(location = 0) out vec4 xe_frag_color; + +#define A_GPU 1 +#define A_GLSL 1 +#include "../../../../third_party/FidelityFX-FSR/ffx-fsr/ffx_a.h" +#define FSR_EASU_F 1 +vec4 FsrEasuRF(vec2 p) { return textureGather(xe_texture, p, 0); } +vec4 FsrEasuGF(vec2 p) { return textureGather(xe_texture, p, 1); } +vec4 FsrEasuBF(vec2 p) { return textureGather(xe_texture, p, 2); } +#include "../../../../third_party/FidelityFX-FSR/ffx-fsr/ffx_fsr1.h" + +void main() { + // FsrEasuCon with smaller push constant usage. + uvec4 easu_const_0 = + uvec4(floatBitsToUint(xe_fsr_easu_input_output_size_ratio), + floatBitsToUint(0.5 * xe_fsr_easu_input_output_size_ratio - 0.5)); + uvec4 easu_const_1 = floatBitsToUint(vec4(1.0, 1.0, 1.0, -1.0) * + xe_fsr_easu_input_size_inv.xyxy); + uvec4 easu_const_2 = floatBitsToUint(vec4(-1.0, 2.0, 1.0, 2.0) * + xe_fsr_easu_input_size_inv.xyxy); + uvec4 easu_const_3 = + uvec4(floatBitsToUint(0.0), + floatBitsToUint(4.0 * xe_fsr_easu_input_size_inv.y), 0u, 0u); + FsrEasuF(xe_frag_color.rgb, uvec2(gl_FragCoord.xy), easu_const_0, + easu_const_1, easu_const_2, easu_const_3); + xe_frag_color.a = 1.0; +} diff --git a/src/xenia/ui/shaders/guest_output_ffx_fsr_easu.ps.hlsl b/src/xenia/ui/shaders/guest_output_ffx_fsr_easu.ps.hlsl new file mode 100644 index 000000000..bd95e7a46 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_fsr_easu.ps.hlsl @@ -0,0 +1,39 @@ +cbuffer XeFsrEasuConstants : register(b0) { + float2 xe_fsr_easu_input_output_size_ratio; + float2 xe_fsr_easu_input_size_inv; +}; + +Texture2D xe_texture : register(t0); +SamplerState xe_sampler_linear_clamp : register(s0); + +#define A_GPU 1 +#define A_HLSL 1 +#include "../../../../third_party/FidelityFX-FSR/ffx-fsr/ffx_a.h" +#define FSR_EASU_F 1 +float4 FsrEasuRF(float2 p) { + return xe_texture.GatherRed(xe_sampler_linear_clamp, p); +} +float4 FsrEasuGF(float2 p) { + return xe_texture.GatherGreen(xe_sampler_linear_clamp, p); +} +float4 FsrEasuBF(float2 p) { + return xe_texture.GatherBlue(xe_sampler_linear_clamp, p); +} +#include "../../../../third_party/FidelityFX-FSR/ffx-fsr/ffx_fsr1.h" + +float4 main(float4 xe_position : SV_Position) : SV_Target { + // FsrEasuCon with smaller root signature usage. + uint4 easu_const_0 = + uint4(asuint(xe_fsr_easu_input_output_size_ratio), + asuint(0.5f * xe_fsr_easu_input_output_size_ratio - 0.5f)); + uint4 easu_const_1 = + asuint(float4(1.0f, 1.0f, 1.0f, -1.0f) * xe_fsr_easu_input_size_inv.xyxy); + uint4 easu_const_2 = + asuint(float4(-1.0f, 2.0f, 1.0f, 2.0f) * xe_fsr_easu_input_size_inv.xyxy); + uint4 easu_const_3 = + uint4(asuint(0.0f), asuint(4.0f * xe_fsr_easu_input_size_inv.y), 0u, 0u); + float3 easu_color; + FsrEasuF(easu_color, uint2(xe_position.xy), easu_const_0, easu_const_1, + easu_const_2, easu_const_3); + return float4(easu_color, 1.0f); +} diff --git a/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.frag b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.frag new file mode 100644 index 000000000..a5bb9f7af --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.frag @@ -0,0 +1,3 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require +#include "guest_output_ffx_fsr_rcas.glsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.glsli b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.glsli new file mode 100644 index 000000000..5c60cd8ea --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.glsli @@ -0,0 +1,45 @@ +// At least #version 420. + +#if XE_GUEST_OUTPUT_DITHER + #include "dither_8bpc.xesli" +#endif // XE_GUEST_OUTPUT_DITHER + +layout(push_constant) uniform XeFsrRcasConstants { + // 16 occupied by the vertex shader. + layout(offset = 16) ivec2 xe_fsr_rcas_output_offset; + // FsrRcasCon const0.x. + layout(offset = 24) float xe_fsr_rcas_sharpness_post_setup; +}; + +layout(set = 0, binding = 0) uniform sampler2D xe_texture; + +layout(location = 0) out vec4 xe_frag_color; + +#define A_GPU 1 +#define A_GLSL 1 +#include "../../../../third_party/FidelityFX-FSR/ffx-fsr/ffx_a.h" +#define FSR_RCAS_F 1 +vec4 FsrRcasLoadF(ivec2 p) { + return vec4(texelFetch(xe_texture, p, 0).rgb, 1.0); +} +void FsrRcasInputF(inout float r, inout float g, inout float b) {} +#include "../../../../third_party/FidelityFX-FSR/ffx-fsr/ffx_fsr1.h" + +void main() { + uvec2 pixel_coord = uvec2(ivec2(gl_FragCoord.xy) - xe_fsr_rcas_output_offset); + // FsrRcasCon with smaller push constant usage. + uvec4 rcas_const = + uvec4(floatBitsToUint(xe_fsr_rcas_sharpness_post_setup), + packHalf2x16(vec2(xe_fsr_rcas_sharpness_post_setup)), 0u, 0u); + FsrRcasF(xe_frag_color.r, xe_frag_color.g, xe_frag_color.b, pixel_coord, + rcas_const); + #if XE_GUEST_OUTPUT_DITHER + // Clamping because on Vulkan, the surface may specify any format, including + // floating-point. + xe_frag_color.rgb = + clamp(xe_frag_color.rgb + XeDitherOffset8bpc(pixel_coord), + vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0)); + #endif // XE_GUEST_OUTPUT_DITHER + // Force alpha to 1 to make sure the surface won't be translucent. + xe_frag_color.a = 1.0; +} diff --git a/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.hlsli b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.hlsli new file mode 100644 index 000000000..c9bc3e7bf --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.hlsli @@ -0,0 +1,36 @@ +#if XE_GUEST_OUTPUT_DITHER + #include "dither_8bpc.xesli" +#endif // XE_GUEST_OUTPUT_DITHER + +cbuffer XeFsrRcasConstants : register(b0) { + int2 xe_fsr_rcas_output_offset; + // FsrRcasCon const0.x. + float xe_fsr_rcas_sharpness_post_setup; +}; + +Texture2D xe_texture : register(t0); + +#define A_GPU 1 +#define A_HLSL 1 +#include "../../../../third_party/FidelityFX-FSR/ffx-fsr/ffx_a.h" +#define FSR_RCAS_F 1 +float4 FsrRcasLoadF(int2 p) { + return float4(xe_texture.Load(int3(p, 0)).rgb, 1.0f); +} +void FsrRcasInputF(inout float r, inout float g, inout float b) {} +#include "../../../../third_party/FidelityFX-FSR/ffx-fsr/ffx_fsr1.h" + +float4 main(float4 xe_position : SV_Position) : SV_Target { + uint2 pixel_coord = uint2(int2(xe_position.xy) - xe_fsr_rcas_output_offset); + // FsrRcasCon with smaller root signature usage. + uint4 rcas_const = + uint4(asuint(xe_fsr_rcas_sharpness_post_setup), + f32tof16(xe_fsr_rcas_sharpness_post_setup) * 0x00010001u, 0u, 0u); + float3 rcas_color; + FsrRcasF(rcas_color.r, rcas_color.g, rcas_color.b, pixel_coord, rcas_const); + #if XE_GUEST_OUTPUT_DITHER + // Not clamping because a normalized format is explicitly requested on DXGI. + rcas_color += XeDitherOffset8bpc(pixel_coord); + #endif // XE_GUEST_OUTPUT_DITHER + return float4(rcas_color, 1.0f); +} diff --git a/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.ps.hlsl b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.ps.hlsl new file mode 100644 index 000000000..6133ff540 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas.ps.hlsl @@ -0,0 +1 @@ +#include "guest_output_ffx_fsr_rcas.hlsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas_dither.frag b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas_dither.frag new file mode 100644 index 000000000..2e0f18cb8 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas_dither.frag @@ -0,0 +1,4 @@ +#version 420 +#extension GL_GOOGLE_include_directive : require +#define XE_GUEST_OUTPUT_DITHER 1 +#include "guest_output_ffx_fsr_rcas.glsli" diff --git a/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas_dither.ps.hlsl b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas_dither.ps.hlsl new file mode 100644 index 000000000..ed669fa64 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_ffx_fsr_rcas_dither.ps.hlsl @@ -0,0 +1,2 @@ +#define XE_GUEST_OUTPUT_DITHER 1 +#include "guest_output_ffx_fsr_rcas.hlsli" diff --git a/src/xenia/ui/shaders/guest_output_triangle_strip_rect.vert b/src/xenia/ui/shaders/guest_output_triangle_strip_rect.vert new file mode 100644 index 000000000..5127c10ae --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_triangle_strip_rect.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(push_constant) uniform XeTriangleStripRectConstants { + // If the layout is changed, update the base offset in all guest output + // fragment shaders! + vec2 xe_triangle_strip_rect_offset; + // Can be negative. + vec2 xe_triangle_strip_rect_size; +}; + +void main() { + // Passthrough - coordinate system differences are to be handled externally. + gl_Position = vec4(xe_triangle_strip_rect_offset + + vec2((uvec2(gl_VertexIndex) >> uvec2(0u, 1u)) & 1u) * + xe_triangle_strip_rect_size, + 0.0, 1.0); +} diff --git a/src/xenia/ui/shaders/guest_output_triangle_strip_rect.vs.hlsl b/src/xenia/ui/shaders/guest_output_triangle_strip_rect.vs.hlsl new file mode 100644 index 000000000..f265ac0a3 --- /dev/null +++ b/src/xenia/ui/shaders/guest_output_triangle_strip_rect.vs.hlsl @@ -0,0 +1,14 @@ +cbuffer XeTriangleStripRectConstants : register(b0) { + float2 xe_triangle_strip_rect_offset; + // Can be negative. + float2 xe_triangle_strip_rect_size; +}; + +void main(uint xe_vertex_id : SV_VertexID, + out float4 xe_position : SV_Position) { + // Passthrough - coordinate system differences are to be handled externally. + xe_position = float4(xe_triangle_strip_rect_offset + + float2((xe_vertex_id >> uint2(0u, 1u)) & 1u) * + xe_triangle_strip_rect_size, + 0.0f, 1.0f); +} diff --git a/src/xenia/ui/shaders/immediate.frag b/src/xenia/ui/shaders/immediate.frag new file mode 100644 index 000000000..29e32f2bb --- /dev/null +++ b/src/xenia/ui/shaders/immediate.frag @@ -0,0 +1,14 @@ +#version 310 es +precision highp float; + +layout(set = 0, binding = 0) uniform lowp sampler2D xe_immediate_texture; + +layout(location = 0) in vec2 xe_var_texcoord; +layout(location = 1) in lowp vec4 xe_var_color; + +layout(location = 0) out lowp vec4 xe_frag_color; + +void main() { + xe_frag_color = + xe_var_color * textureLod(xe_immediate_texture, xe_var_texcoord, 0.0); +} diff --git a/src/xenia/ui/shaders/immediate.vert b/src/xenia/ui/shaders/immediate.vert new file mode 100644 index 000000000..ce84d6311 --- /dev/null +++ b/src/xenia/ui/shaders/immediate.vert @@ -0,0 +1,20 @@ +#version 310 es + +layout(push_constant) uniform XePushConstants { + vec2 xe_coordinate_space_size_inv; +}; + +layout(location = 0) in vec2 xe_attr_position; +layout(location = 1) in vec2 xe_attr_texcoord; +layout(location = 2) in lowp vec4 xe_attr_color; + +layout(location = 0) out vec2 xe_var_texcoord; +layout(location = 1) out lowp vec4 xe_var_color; + +void main() { + xe_var_texcoord = xe_attr_texcoord; + xe_var_color = xe_attr_color; + gl_Position = + vec4(xe_attr_position * xe_coordinate_space_size_inv * 2.0 - 1.0, 0.0, + 1.0); +} diff --git a/src/xenia/ui/shaders/immediate.vs.hlsl b/src/xenia/ui/shaders/immediate.vs.hlsl index d003fa030..0746fa4c4 100644 --- a/src/xenia/ui/shaders/immediate.vs.hlsl +++ b/src/xenia/ui/shaders/immediate.vs.hlsl @@ -1,5 +1,5 @@ cbuffer XeImmediateVertexConstants : register(b0) { - float2 xe_viewport_size_inv; + float2 xe_coordinate_space_size_inv; }; struct XeVertexShaderInput { @@ -17,7 +17,7 @@ struct XeVertexShaderOutput { XeVertexShaderOutput main(XeVertexShaderInput input) { XeVertexShaderOutput output; output.position = float4( - input.position * xe_viewport_size_inv * float2(2.0, -2.0) + + input.position * xe_coordinate_space_size_inv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); output.texcoord = input.texcoord; output.color = input.color; diff --git a/src/xenia/ui/shaders/noise.xesli b/src/xenia/ui/shaders/noise.xesli new file mode 100644 index 000000000..4e3c897fa --- /dev/null +++ b/src/xenia/ui/shaders/noise.xesli @@ -0,0 +1,47 @@ +#ifndef XENIA_UI_SHADERS_NOISE_XESLI_ +#define XENIA_UI_SHADERS_NOISE_XESLI_ + +#define XeNoiseRow16(scale, offset, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, \ + va, vb, vc, vd, ve, vf) \ + v0 * scale + offset, v1 * scale + offset, v2 * scale + offset, \ + v3 * scale + offset, v4 * scale + offset, v5 * scale + offset, \ + v6 * scale + offset, v7 * scale + offset, v8 * scale + offset, \ + v9 * scale + offset, va * scale + offset, vb * scale + offset, \ + vc * scale + offset, vd * scale + offset, ve * scale + offset, \ + vf * scale + offset + +#define XeBlueNoise16x16Values0Until256(scale, offset) \ + XeNoiseRow16(scale, offset, 62, 185, 238, 26, 211, 88, 197, 37, 216, 161, \ + 207, 23, 166, 109, 200, 35), \ + XeNoiseRow16(scale, offset, 222, 92, 136, 49, 156, 59, 168, 96, 250, 56, \ + 121, 93, 221, 1, 82, 130), \ + XeNoiseRow16(scale, offset, 174, 42, 199, 108, 242, 18, 224, 125, 179, 8, \ + 147, 192, 51, 139, 235, 24), \ + XeNoiseRow16(scale, offset, 150, 249, 12, 71, 186, 138, 67, 28, 85, 212, 75, \ + 32, 245, 72, 183, 98), \ + XeNoiseRow16(scale, offset, 58, 118, 143, 220, 89, 115, 232, 194, 154, 114, \ + 231, 170, 123, 155, 20, 217), \ + XeNoiseRow16(scale, offset, 188, 84, 205, 33, 165, 4, 177, 43, 253, 19, 60, \ + 95, 6, 206, 106, 38), \ + XeNoiseRow16(scale, offset, 234, 7, 175, 68, 246, 53, 103, 134, 79, 204, \ + 132, 225, 191, 65, 254, 162), \ + XeNoiseRow16(scale, offset, 131, 46, 110, 228, 127, 148, 215, 13, 160, 50, \ + 178, 40, 145, 31, 119, 77), \ + XeNoiseRow16(scale, offset, 213, 153, 190, 97, 27, 195, 83, 237, 193, 100, \ + 244, 112, 87, 236, 182, 15), \ + XeNoiseRow16(scale, offset, 57, 243, 76, 11, 172, 63, 41, 116, 29, 149, 0, \ + 171, 22, 203, 157, 99), \ + XeNoiseRow16(scale, offset, 140, 30, 202, 133, 251, 219, 164, 135, 210, 90, \ + 64, 214, 142, 78, 39, 223), \ + XeNoiseRow16(scale, offset, 184, 104, 163, 54, 111, 91, 9, 240, 52, 176, \ + 230, 120, 48, 247, 129, 2), \ + XeNoiseRow16(scale, offset, 241, 81, 229, 16, 208, 141, 196, 80, 21, 107, \ + 159, 10, 189, 105, 173, 61), \ + XeNoiseRow16(scale, offset, 25, 126, 44, 180, 69, 34, 167, 122, 187, 255, \ + 36, 86, 226, 74, 209, 144), \ + XeNoiseRow16(scale, offset, 227, 201, 151, 102, 252, 218, 55, 233, 70, 137, \ + 198, 124, 146, 17, 45, 94), \ + XeNoiseRow16(scale, offset, 117, 5, 73, 169, 128, 3, 113, 152, 14, 101, 47, \ + 239, 66, 181, 248, 158) + +#endif // XENIA_UI_SHADERS_NOISE_XESLI_ diff --git a/src/xenia/ui/shaders/xesl.xesli b/src/xenia/ui/shaders/xesl.xesli new file mode 100644 index 000000000..d112c08a2 --- /dev/null +++ b/src/xenia/ui/shaders/xesl.xesli @@ -0,0 +1,45 @@ +#ifndef XENIA_UI_SHADERS_XESL_XESLI_ +#define XENIA_UI_SHADERS_XESL_XESLI_ + +// XESL_LANGUAGE_GLSL / HLSL are expected to be defined via compiler arguments. + +// Vectors. +#if XESL_LANGUAGE_GLSL + #define xesl_bool2 bvec2 + #define xesl_bool3 bvec3 + #define xesl_bool4 bvec4 + #define xesl_int2 ivec2 + #define xesl_int3 ivec3 + #define xesl_int4 ivec4 + #define xesl_uint2 uvec2 + #define xesl_uint3 uvec3 + #define xesl_uint4 uvec4 + #define xesl_float2 vec2 + #define xesl_float3 vec3 + #define xesl_float4 vec4 +#else + #define xesl_bool2 bool2 + #define xesl_bool3 bool3 + #define xesl_bool4 bool4 + #define xesl_int2 int2 + #define xesl_int3 int3 + #define xesl_int4 int4 + #define xesl_uint2 uint2 + #define xesl_uint3 uint3 + #define xesl_uint4 uint4 + #define xesl_float2 float2 + #define xesl_float3 float3 + #define xesl_float4 float4 +#endif // XESL_LANGUAGE_GLSL + +// Declarations. +#if XESL_LANGUAGE_HLSL + // HLSL requires static const to declare a constant in the global scope so it + // doesn't go to $Globals instead. + #define xesl_static static +#else + // GLSL requires just const to declare a constant in the global scope. + #define xesl_static +#endif // XESL_LANGUAGE_HLSL + +#endif // XENIA_UI_SHADERS_XESL_XESLI_ diff --git a/src/xenia/ui/surface.h b/src/xenia/ui/surface.h new file mode 100644 index 000000000..a37bcd8a3 --- /dev/null +++ b/src/xenia/ui/surface.h @@ -0,0 +1,83 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_SURFACE_H_ +#define XENIA_UI_SURFACE_H_ + +#include + +#include "xenia/base/assert.h" + +namespace xe { +namespace ui { + +// Represents a surface that presenting can be performed to. +// Surface methods can be called only from the UI thread. + +class Surface { + public: + enum TypeIndex { + // Within one platform, the more preferable surface types are earlier in + // this enumeration, so xe::bit_scan_forward can be used to try creating + // surfaces of all types supported by both the graphics provider and the + // window. + // Android. + kTypeIndex_AndroidNativeWindow, + // GNU/Linux. + kTypeIndex_XcbWindow, + // Windows. + kTypeIndex_Win32Hwnd, + }; + using TypeFlags = uint32_t; + enum : TypeFlags { + kTypeFlag_AndroidNativeWindow = TypeFlags(1) + << kTypeIndex_AndroidNativeWindow, + kTypeFlag_XcbWindow = TypeFlags(1) << kTypeIndex_XcbWindow, + kTypeFlag_Win32Hwnd = TypeFlags(1) << kTypeIndex_Win32Hwnd, + }; + + Surface(const Surface& surface) = delete; + Surface& operator=(const Surface& surface) = delete; + virtual ~Surface() = default; + + virtual TypeIndex GetType() const = 0; + + // Returns the up-to-date size (and true), or zeros (and false) if not ready + // to open a presentation connection yet. The size preferably should be + // exactly the dimensions of the surface in physical pixels of the display + // (without stretching performed by the platform's composition), but even if + // stretching happens, it is required that surface pixels have 1:1 aspect + // ratio relatively to the physical display. + bool GetSize(uint32_t& width_out, uint32_t& height_out) { + // If any dimension is 0 (like, resized completely to zero in one direction, + // but not in another), the surface is zero-area - don't try to present to + // it. + uint32_t width, height; + if (!GetSizeImpl(width, height) || !width || !height) { + width_out = 0; + height_out = 0; + return false; + } + width_out = width; + height_out = height; + return true; + } + + protected: + Surface() = default; + + // Returns the up-to-date size in physical pixels (and true), or zeros (and + // optionally false) if not ready to open a presentation connection yet. + virtual bool GetSizeImpl(uint32_t& width_out, uint32_t& height_out) const = 0; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_SURFACE_H_ diff --git a/src/xenia/ui/surface_android.cc b/src/xenia/ui/surface_android.cc new file mode 100644 index 000000000..f4ea30a39 --- /dev/null +++ b/src/xenia/ui/surface_android.cc @@ -0,0 +1,34 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/surface_android.h" + +#include + +namespace xe { +namespace ui { + +bool AndroidNativeWindowSurface::GetSizeImpl(uint32_t& width_out, + uint32_t& height_out) const { + if (!window_) { + return false; + } + int width = ANativeWindow_getWidth(window_); + int height = ANativeWindow_getHeight(window_); + // Negative value is returned in case of an error. + if (width <= 0 || height <= 0) { + return false; + } + width_out = uint32_t(width); + height_out = uint32_t(height); + return true; +} + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/surface_android.h b/src/xenia/ui/surface_android.h new file mode 100644 index 000000000..7b9c684bd --- /dev/null +++ b/src/xenia/ui/surface_android.h @@ -0,0 +1,37 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_SURFACE_ANDROID_H_ +#define XENIA_UI_SURFACE_ANDROID_H_ + +#include + +#include "xenia/ui/surface.h" + +namespace xe { +namespace ui { + +class AndroidNativeWindowSurface : public Surface { + public: + explicit AndroidNativeWindowSurface(ANativeWindow* window) + : window_(window) {} + TypeIndex GetType() const override { return kTypeIndex_AndroidNativeWindow; } + ANativeWindow* window() const { return window_; } + + protected: + bool GetSizeImpl(uint32_t& width_out, uint32_t& height_out) const override; + + private: + ANativeWindow* window_; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_SURFACE_ANDROID_H_ diff --git a/src/xenia/ui/surface_gnulinux.cc b/src/xenia/ui/surface_gnulinux.cc new file mode 100644 index 000000000..9d3a990aa --- /dev/null +++ b/src/xenia/ui/surface_gnulinux.cc @@ -0,0 +1,31 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/surface_gnulinux.h" + +#include + +namespace xe { +namespace ui { + +bool XcbWindowSurface::GetSizeImpl(uint32_t& width_out, + uint32_t& height_out) const { + xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply( + connection_, xcb_get_geometry(connection_, window_), nullptr); + if (!reply) { + return false; + } + width_out = reply->width; + height_out = reply->height; + std::free(reply); + return true; +} + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/surface_gnulinux.h b/src/xenia/ui/surface_gnulinux.h new file mode 100644 index 000000000..44c6b98bc --- /dev/null +++ b/src/xenia/ui/surface_gnulinux.h @@ -0,0 +1,39 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_SURFACE_GNULINUX_H_ +#define XENIA_UI_SURFACE_GNULINUX_H_ + +#include + +#include "xenia/ui/surface.h" + +namespace xe { +namespace ui { + +class XcbWindowSurface final : public Surface { + public: + explicit XcbWindowSurface(xcb_connection_t* connection, xcb_window_t window) + : connection_(connection), window_(window) {} + TypeIndex GetType() const override { return kTypeIndex_XcbWindow; } + xcb_connection_t* connection() const { return connection_; } + xcb_window_t window() const { return window_; } + + protected: + bool GetSizeImpl(uint32_t& width_out, uint32_t& height_out) const override; + + private: + xcb_connection_t* connection_; + xcb_window_t window_; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_SURFACE_LINUX_H_ diff --git a/src/xenia/ui/surface_win.cc b/src/xenia/ui/surface_win.cc new file mode 100644 index 000000000..3cdc0e271 --- /dev/null +++ b/src/xenia/ui/surface_win.cc @@ -0,0 +1,35 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/surface_win.h" + +#include +#include + +#include "xenia/base/logging.h" + +namespace xe { +namespace ui { + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_GAMES) +bool Win32HwndSurface::GetSizeImpl(uint32_t& width_out, + uint32_t& height_out) const { + RECT client_rect; + if (!GetClientRect(hwnd(), &client_rect)) { + return false; + } + width_out = uint32_t(std::max(client_rect.right - client_rect.left, LONG(0))); + height_out = + uint32_t(std::max(client_rect.bottom - client_rect.top, LONG(0))); + return true; +} +#endif + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/surface_win.h b/src/xenia/ui/surface_win.h new file mode 100644 index 000000000..68d6f0e5f --- /dev/null +++ b/src/xenia/ui/surface_win.h @@ -0,0 +1,45 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_SURFACE_WIN_H_ +#define XENIA_UI_SURFACE_WIN_H_ + +#include +#include + +#include "xenia/ui/surface.h" + +// Must be included before Windows headers for things like NOMINMAX. +#include "xenia/base/platform_win.h" + +namespace xe { +namespace ui { + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_GAMES) +class Win32HwndSurface final : public Surface { + public: + explicit Win32HwndSurface(HINSTANCE hinstance, HWND hwnd) + : hinstance_(hinstance), hwnd_(hwnd) {} + TypeIndex GetType() const override { return kTypeIndex_Win32Hwnd; } + HINSTANCE hinstance() const { return hinstance_; } + HWND hwnd() const { return hwnd_; } + + protected: + bool GetSizeImpl(uint32_t& width_out, uint32_t& height_out) const override; + + private: + HINSTANCE hinstance_; + HWND hwnd_; +}; +#endif + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_SURFACE_WIN_H_ diff --git a/src/xenia/ui/ui_drawer.h b/src/xenia/ui/ui_drawer.h new file mode 100644 index 000000000..b5173dfd6 --- /dev/null +++ b/src/xenia/ui/ui_drawer.h @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_UI_DRAWER_H_ +#define XENIA_UI_UI_DRAWER_H_ + +#include + +namespace xe { +namespace ui { + +class UIDrawContext; + +class UIDrawer { + public: + // No preparation or finishing callbacks because drawers may register or + // unregister each other, so between the loops the list may be different. + + // The draw function may register or unregister drawers (and depending on the + // Z order the changes may or may not effect immediately). However, they must + // not perform any lifetime management of the presenter and of its connection + // to the surface. Ideally drawing should not be changing any state at all, + // however, in Dear ImGui, input is handled directly during drawing - any + // quitting or resizing, if done in the UI, must be deferred via something + // like WindowedAppContext::CallInUIThreadDeferred. + virtual void Draw(UIDrawContext& context) = 0; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_UI_DRAWER_H_ diff --git a/src/xenia/ui/ui_event.h b/src/xenia/ui/ui_event.h index 993eda672..4d5520782 100644 --- a/src/xenia/ui/ui_event.h +++ b/src/xenia/ui/ui_event.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -27,12 +27,40 @@ class UIEvent { Window* target() const { return target_; } private: - Window* target_ = nullptr; + Window* target_; +}; + +class UISetupEvent : public UIEvent { + public: + explicit UISetupEvent(Window* target = nullptr, bool is_initial_setup = false) + : UIEvent(target), is_initial_setup_(is_initial_setup) {} + + bool is_initial_setup() const { return is_initial_setup_; } + + private: + bool is_initial_setup_; +}; + +class MonitorUpdateEvent : public UISetupEvent { + public: + explicit MonitorUpdateEvent(Window* target, + bool old_monitor_potentially_disconnected, + bool is_initial_setup = false) + : UISetupEvent(target, is_initial_setup), + old_monitor_potentially_disconnected_( + old_monitor_potentially_disconnected) {} + + bool old_monitor_potentially_disconnected() const { + return old_monitor_potentially_disconnected_; + } + + private: + bool old_monitor_potentially_disconnected_; }; class FileDropEvent : public UIEvent { public: - FileDropEvent(Window* target, std::filesystem::path filename) + explicit FileDropEvent(Window* target, std::filesystem::path filename) : UIEvent(target), filename_(std::move(filename)) {} ~FileDropEvent() override = default; @@ -44,10 +72,10 @@ class FileDropEvent : public UIEvent { class KeyEvent : public UIEvent { public: - KeyEvent(Window* target, VirtualKey virtual_key, int repeat_count, - bool prev_state, bool modifier_shift_pressed, - bool modifier_ctrl_pressed, bool modifier_alt_pressed, - bool modifier_super_pressed) + explicit KeyEvent(Window* target, VirtualKey virtual_key, int repeat_count, + bool prev_state, bool modifier_shift_pressed, + bool modifier_ctrl_pressed, bool modifier_alt_pressed, + bool modifier_super_pressed) : UIEvent(target), virtual_key_(virtual_key), repeat_count_(repeat_count), @@ -96,9 +124,17 @@ class MouseEvent : public UIEvent { }; public: - MouseEvent(Window* target, Button button, int32_t x, int32_t y, - int32_t dx = 0, int32_t dy = 0) - : UIEvent(target), button_(button), x_(x), y_(y), dx_(dx), dy_(dy) {} + // Matching Windows WHEEL_DELTA. + static constexpr uint32_t kScrollPerDetent = 120; + + explicit MouseEvent(Window* target, Button button, int32_t x, int32_t y, + int32_t scroll_x = 0, int32_t scroll_y = 0) + : UIEvent(target), + button_(button), + x_(x), + y_(y), + scroll_x_(scroll_x), + scroll_y_(scroll_y) {} ~MouseEvent() override = default; bool is_handled() const { return handled_; } @@ -107,16 +143,17 @@ class MouseEvent : public UIEvent { Button button() const { return button_; } int32_t x() const { return x_; } int32_t y() const { return y_; } - int32_t dx() const { return dx_; } - int32_t dy() const { return dy_; } + int32_t scroll_x() const { return scroll_x_; } + int32_t scroll_y() const { return scroll_y_; } private: bool handled_ = false; Button button_; int32_t x_ = 0; int32_t y_ = 0; - int32_t dx_ = 0; - int32_t dy_ = 0; + int32_t scroll_x_ = 0; + // Positive is up (away from the user), negative is down (towards the user). + int32_t scroll_y_ = 0; }; } // namespace ui diff --git a/src/xenia/ui/vulkan/blitter.cc b/src/xenia/ui/vulkan/blitter.cc index 165301d3c..25c6be7eb 100644 --- a/src/xenia/ui/vulkan/blitter.cc +++ b/src/xenia/ui/vulkan/blitter.cc @@ -10,23 +10,25 @@ #include "xenia/ui/vulkan/blitter.h" #include "xenia/base/math.h" #include "xenia/ui/vulkan/fenced_pools.h" +#include "xenia/ui/vulkan/vulkan_util.h" namespace xe { namespace ui { namespace vulkan { -// Generated with `xenia-build genspirv`. -#include "xenia/ui/vulkan/shaders/bin/blit_color_frag.h" -#include "xenia/ui/vulkan/shaders/bin/blit_depth_frag.h" -#include "xenia/ui/vulkan/shaders/bin/blit_vert.h" +using util::CheckResult; -Blitter::Blitter() {} +// Generated with `xenia-build genspirv`. +#include "xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_color_frag.h" +#include "xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.h" +#include "xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.h" + +Blitter::Blitter(const VulkanProvider& provider) : provider_(provider) {} Blitter::~Blitter() { Shutdown(); } -VkResult Blitter::Initialize(VulkanDevice* device) { - device_ = device; - - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); +VkResult Blitter::Initialize() { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; // Shaders @@ -35,39 +37,36 @@ VkResult Blitter::Initialize(VulkanDevice* device) { shader_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shader_create_info.codeSize = sizeof(blit_vert); shader_create_info.pCode = reinterpret_cast(blit_vert); - status = dfn.vkCreateShaderModule(*device_, &shader_create_info, nullptr, + status = dfn.vkCreateShaderModule(device, &shader_create_info, nullptr, &blit_vertex_); CheckResult(status, "vkCreateShaderModule"); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName(uint64_t(blit_vertex_), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - "S(B): Vertex"); + provider_.SetDeviceObjectName(VK_OBJECT_TYPE_SHADER_MODULE, + uint64_t(blit_vertex_), "S(B): Vertex"); shader_create_info.codeSize = sizeof(blit_color_frag); shader_create_info.pCode = reinterpret_cast(blit_color_frag); - status = dfn.vkCreateShaderModule(*device_, &shader_create_info, nullptr, + status = dfn.vkCreateShaderModule(device, &shader_create_info, nullptr, &blit_color_); CheckResult(status, "vkCreateShaderModule"); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName(uint64_t(blit_color_), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - "S(B): Color"); + provider_.SetDeviceObjectName(VK_OBJECT_TYPE_SHADER_MODULE, + uint64_t(blit_color_), "S(B): Color"); shader_create_info.codeSize = sizeof(blit_depth_frag); shader_create_info.pCode = reinterpret_cast(blit_depth_frag); - status = dfn.vkCreateShaderModule(*device_, &shader_create_info, nullptr, + status = dfn.vkCreateShaderModule(device, &shader_create_info, nullptr, &blit_depth_); CheckResult(status, "vkCreateShaderModule"); if (status != VK_SUCCESS) { return status; } - device_->DbgSetObjectName(uint64_t(blit_depth_), - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, - "S(B): Depth"); + provider_.SetDeviceObjectName(VK_OBJECT_TYPE_SHADER_MODULE, + uint64_t(blit_depth_), "S(B): Depth"); // Create the descriptor set layout used for our texture sampler. // As it changes almost every draw we cache it per texture. @@ -84,7 +83,7 @@ VkResult Blitter::Initialize(VulkanDevice* device) { texture_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; texture_binding.pImmutableSamplers = nullptr; texture_set_layout_info.pBindings = &texture_binding; - status = dfn.vkCreateDescriptorSetLayout(*device_, &texture_set_layout_info, + status = dfn.vkCreateDescriptorSetLayout(device, &texture_set_layout_info, nullptr, &descriptor_set_layout_); CheckResult(status, "vkCreateDescriptorSetLayout"); if (status != VK_SUCCESS) { @@ -96,7 +95,7 @@ VkResult Blitter::Initialize(VulkanDevice* device) { pool_sizes[0].descriptorCount = 4096; pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptor_pool_ = std::make_unique( - *device_, 4096, + provider_, 4096, std::vector(pool_sizes, std::end(pool_sizes))); // Create the pipeline layout used for our pipeline. @@ -120,7 +119,7 @@ VkResult Blitter::Initialize(VulkanDevice* device) { pipeline_layout_info.pushConstantRangeCount = static_cast(xe::countof(push_constant_ranges)); pipeline_layout_info.pPushConstantRanges = push_constant_ranges; - status = dfn.vkCreatePipelineLayout(*device_, &pipeline_layout_info, nullptr, + status = dfn.vkCreatePipelineLayout(device, &pipeline_layout_info, nullptr, &pipeline_layout_); CheckResult(status, "vkCreatePipelineLayout"); if (status != VK_SUCCESS) { @@ -148,7 +147,7 @@ VkResult Blitter::Initialize(VulkanDevice* device) { VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, VK_FALSE, }; - status = dfn.vkCreateSampler(*device_, &sampler_create_info, nullptr, + status = dfn.vkCreateSampler(device, &sampler_create_info, nullptr, &samp_nearest_); CheckResult(status, "vkCreateSampler"); if (status != VK_SUCCESS) { @@ -158,8 +157,8 @@ VkResult Blitter::Initialize(VulkanDevice* device) { sampler_create_info.minFilter = VK_FILTER_LINEAR; sampler_create_info.magFilter = VK_FILTER_LINEAR; sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - status = dfn.vkCreateSampler(*device_, &sampler_create_info, nullptr, - &samp_linear_); + status = + dfn.vkCreateSampler(device, &sampler_create_info, nullptr, &samp_linear_); CheckResult(status, "vkCreateSampler"); if (status != VK_SUCCESS) { return status; @@ -169,50 +168,26 @@ VkResult Blitter::Initialize(VulkanDevice* device) { } void Blitter::Shutdown() { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - if (samp_nearest_) { - dfn.vkDestroySampler(*device_, samp_nearest_, nullptr); - samp_nearest_ = nullptr; - } - if (samp_linear_) { - dfn.vkDestroySampler(*device_, samp_linear_, nullptr); - samp_linear_ = nullptr; - } - if (blit_vertex_) { - dfn.vkDestroyShaderModule(*device_, blit_vertex_, nullptr); - blit_vertex_ = nullptr; - } - if (blit_color_) { - dfn.vkDestroyShaderModule(*device_, blit_color_, nullptr); - blit_color_ = nullptr; - } - if (blit_depth_) { - dfn.vkDestroyShaderModule(*device_, blit_depth_, nullptr); - blit_depth_ = nullptr; - } - if (pipeline_color_) { - dfn.vkDestroyPipeline(*device_, pipeline_color_, nullptr); - pipeline_color_ = nullptr; - } - if (pipeline_depth_) { - dfn.vkDestroyPipeline(*device_, pipeline_depth_, nullptr); - pipeline_depth_ = nullptr; - } - if (pipeline_layout_) { - dfn.vkDestroyPipelineLayout(*device_, pipeline_layout_, nullptr); - pipeline_layout_ = nullptr; - } - if (descriptor_set_layout_) { - dfn.vkDestroyDescriptorSetLayout(*device_, descriptor_set_layout_, nullptr); - descriptor_set_layout_ = nullptr; - } + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + util::DestroyAndNullHandle(dfn.vkDestroySampler, device, samp_nearest_); + util::DestroyAndNullHandle(dfn.vkDestroySampler, device, samp_linear_); + util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device, blit_vertex_); + util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device, blit_color_); + util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device, blit_depth_); + util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, pipeline_color_); + util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, pipeline_depth_); + util::DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, device, + pipeline_layout_); + util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, device, + descriptor_set_layout_); for (auto& pipeline : pipelines_) { - dfn.vkDestroyPipeline(*device_, pipeline.second, nullptr); + dfn.vkDestroyPipeline(device, pipeline.second, nullptr); } pipelines_.clear(); for (auto& pass : render_passes_) { - dfn.vkDestroyRenderPass(*device_, pass.second, nullptr); + dfn.vkDestroyRenderPass(device, pass.second, nullptr); } render_passes_.clear(); } @@ -232,7 +207,8 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence, VkFramebuffer dst_framebuffer, VkViewport viewport, VkRect2D scissor, VkFilter filter, bool color_or_depth, bool swap_channels) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); // Do we need a full draw, or can we cheap out with a blit command? bool full_draw = swap_channels || true; @@ -291,7 +267,7 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence, write.pImageInfo = ℑ write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - dfn.vkUpdateDescriptorSets(*device_, 1, &write, 0, nullptr); + dfn.vkUpdateDescriptorSets(device, 1, &write, 0, nullptr); dfn.vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_, 0, 1, &set, 0, nullptr); @@ -425,9 +401,10 @@ VkRenderPass Blitter::CreateRenderPass(VkFormat output_format, nullptr, }; VkRenderPass renderpass = nullptr; - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult result = - dfn.vkCreateRenderPass(*device_, &renderpass_info, nullptr, &renderpass); + dfn.vkCreateRenderPass(device, &renderpass_info, nullptr, &renderpass); CheckResult(result, "vkCreateRenderPass"); return renderpass; @@ -436,7 +413,8 @@ VkRenderPass Blitter::CreateRenderPass(VkFormat output_format, VkPipeline Blitter::CreatePipeline(VkRenderPass render_pass, VkShaderModule frag_shader, bool color_or_depth) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult result = VK_SUCCESS; // Pipeline @@ -582,7 +560,7 @@ VkPipeline Blitter::CreatePipeline(VkRenderPass render_pass, pipeline_info.basePipelineIndex = -1; VkPipeline pipeline = nullptr; - result = dfn.vkCreateGraphicsPipelines(*device_, nullptr, 1, &pipeline_info, + result = dfn.vkCreateGraphicsPipelines(device, nullptr, 1, &pipeline_info, nullptr, &pipeline); CheckResult(result, "vkCreateGraphicsPipelines"); diff --git a/src/xenia/ui/vulkan/blitter.h b/src/xenia/ui/vulkan/blitter.h index 2de0997d1..817b5efc7 100644 --- a/src/xenia/ui/vulkan/blitter.h +++ b/src/xenia/ui/vulkan/blitter.h @@ -13,8 +13,7 @@ #include #include -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace ui { @@ -24,10 +23,10 @@ class DescriptorPool; class Blitter { public: - Blitter(); + Blitter(const VulkanProvider& provider); ~Blitter(); - VkResult Initialize(VulkanDevice* device); + VkResult Initialize(); void Scavenge(); void Shutdown(); @@ -79,7 +78,7 @@ class Blitter { VkShaderModule frag_shader, bool color_or_depth); std::unique_ptr descriptor_pool_ = nullptr; - VulkanDevice* device_ = nullptr; + const VulkanProvider& provider_; VkPipeline pipeline_color_ = nullptr; VkPipeline pipeline_depth_ = nullptr; VkPipelineLayout pipeline_layout_ = nullptr; diff --git a/src/xenia/ui/vulkan/circular_buffer.cc b/src/xenia/ui/vulkan/circular_buffer.cc index 543342efe..30ba025cf 100644 --- a/src/xenia/ui/vulkan/circular_buffer.cc +++ b/src/xenia/ui/vulkan/circular_buffer.cc @@ -7,22 +7,27 @@ ****************************************************************************** */ +#include "xenia/ui/vulkan/circular_buffer.h" + #include #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" - -#include "xenia/ui/vulkan/circular_buffer.h" +#include "xenia/ui/vulkan/vulkan_util.h" namespace xe { namespace ui { namespace vulkan { -CircularBuffer::CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage, - VkDeviceSize capacity, VkDeviceSize alignment) - : device_(device), capacity_(capacity) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); +using util::CheckResult; + +CircularBuffer::CircularBuffer(const VulkanProvider& provider, + VkBufferUsageFlags usage, VkDeviceSize capacity, + VkDeviceSize alignment) + : provider_(provider), capacity_(capacity) { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; // Create our internal buffer. @@ -35,14 +40,14 @@ CircularBuffer::CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage, buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; buffer_info.queueFamilyIndexCount = 0; buffer_info.pQueueFamilyIndices = nullptr; - status = dfn.vkCreateBuffer(*device_, &buffer_info, nullptr, &gpu_buffer_); + status = dfn.vkCreateBuffer(device, &buffer_info, nullptr, &gpu_buffer_); CheckResult(status, "vkCreateBuffer"); if (status != VK_SUCCESS) { assert_always(); } VkMemoryRequirements reqs; - dfn.vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs); + dfn.vkGetBufferMemoryRequirements(device, gpu_buffer_, &reqs); alignment_ = xe::round_up(alignment, reqs.alignment); } CircularBuffer::~CircularBuffer() { Shutdown(); } @@ -53,12 +58,12 @@ VkResult CircularBuffer::Initialize(VkDeviceMemory memory, gpu_memory_ = memory; gpu_base_ = offset; - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; // Bind the buffer to its backing memory. - status = - dfn.vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_); + status = dfn.vkBindBufferMemory(device, gpu_buffer_, gpu_memory_, gpu_base_); CheckResult(status, "vkBindBufferMemory"); if (status != VK_SUCCESS) { XELOGE("CircularBuffer::Initialize - Failed to bind memory!"); @@ -67,7 +72,7 @@ VkResult CircularBuffer::Initialize(VkDeviceMemory memory, } // Map the memory so we can access it. - status = dfn.vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0, + status = dfn.vkMapMemory(device, gpu_memory_, gpu_base_, capacity_, 0, reinterpret_cast(&host_base_)); CheckResult(status, "vkMapMemory"); if (status != VK_SUCCESS) { @@ -80,27 +85,39 @@ VkResult CircularBuffer::Initialize(VkDeviceMemory memory, } VkResult CircularBuffer::Initialize() { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkResult status = VK_SUCCESS; VkMemoryRequirements reqs; - dfn.vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs); + dfn.vkGetBufferMemoryRequirements(device, gpu_buffer_, &reqs); // Allocate memory from the device to back the buffer. owns_gpu_memory_ = true; - gpu_memory_ = device_->AllocateMemory(reqs); - if (!gpu_memory_) { - XELOGE("CircularBuffer::Initialize - Failed to allocate memory!"); + VkMemoryAllocateInfo memory_allocate_info; + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = nullptr; + memory_allocate_info.allocationSize = reqs.size; + memory_allocate_info.memoryTypeIndex = ui::vulkan::util::ChooseHostMemoryType( + provider_, reqs.memoryTypeBits, false); + if (memory_allocate_info.memoryTypeIndex == UINT32_MAX) { + XELOGE("CircularBuffer::Initialize - Failed to get memory type!"); Shutdown(); return VK_ERROR_INITIALIZATION_FAILED; } + status = dfn.vkAllocateMemory(device, &memory_allocate_info, nullptr, + &gpu_memory_); + if (status != VK_SUCCESS) { + XELOGE("CircularBuffer::Initialize - Failed to allocate memory!"); + Shutdown(); + return status; + } capacity_ = reqs.size; gpu_base_ = 0; // Bind the buffer to its backing memory. - status = - dfn.vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_); + status = dfn.vkBindBufferMemory(device, gpu_buffer_, gpu_memory_, gpu_base_); CheckResult(status, "vkBindBufferMemory"); if (status != VK_SUCCESS) { XELOGE("CircularBuffer::Initialize - Failed to bind memory!"); @@ -109,7 +126,7 @@ VkResult CircularBuffer::Initialize() { } // Map the memory so we can access it. - status = dfn.vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0, + status = dfn.vkMapMemory(device, gpu_memory_, gpu_base_, capacity_, 0, reinterpret_cast(&host_base_)); CheckResult(status, "vkMapMemory"); if (status != VK_SUCCESS) { @@ -123,24 +140,26 @@ VkResult CircularBuffer::Initialize() { void CircularBuffer::Shutdown() { Clear(); - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); if (host_base_) { - dfn.vkUnmapMemory(*device_, gpu_memory_); + dfn.vkUnmapMemory(device, gpu_memory_); host_base_ = nullptr; } if (gpu_buffer_) { - dfn.vkDestroyBuffer(*device_, gpu_buffer_, nullptr); + dfn.vkDestroyBuffer(device, gpu_buffer_, nullptr); gpu_buffer_ = nullptr; } if (gpu_memory_ && owns_gpu_memory_) { - dfn.vkFreeMemory(*device_, gpu_memory_, nullptr); + dfn.vkFreeMemory(device, gpu_memory_, nullptr); gpu_memory_ = nullptr; } } void CircularBuffer::GetBufferMemoryRequirements(VkMemoryRequirements* reqs) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - dfn.vkGetBufferMemoryRequirements(*device_, gpu_buffer_, reqs); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkGetBufferMemoryRequirements(device, gpu_buffer_, reqs); } bool CircularBuffer::CanAcquire(VkDeviceSize length) { @@ -231,25 +250,27 @@ CircularBuffer::Allocation* CircularBuffer::Acquire(VkDeviceSize length, } void CircularBuffer::Flush(Allocation* allocation) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkMappedMemoryRange range; range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range.pNext = nullptr; range.memory = gpu_memory_; range.offset = gpu_base_ + allocation->offset; range.size = allocation->length; - dfn.vkFlushMappedMemoryRanges(*device_, 1, &range); + dfn.vkFlushMappedMemoryRanges(device, 1, &range); } void CircularBuffer::Flush(VkDeviceSize offset, VkDeviceSize length) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); VkMappedMemoryRange range; range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range.pNext = nullptr; range.memory = gpu_memory_; range.offset = gpu_base_ + offset; range.size = length; - dfn.vkFlushMappedMemoryRanges(*device_, 1, &range); + dfn.vkFlushMappedMemoryRanges(device, 1, &range); } void CircularBuffer::Clear() { @@ -258,14 +279,15 @@ void CircularBuffer::Clear() { } void CircularBuffer::Scavenge() { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); // Stash the last signalled fence VkFence fence = nullptr; while (!allocations_.empty()) { Allocation& alloc = allocations_.front(); if (fence != alloc.fence && - dfn.vkGetFenceStatus(*device_, alloc.fence) != VK_SUCCESS) { + dfn.vkGetFenceStatus(device, alloc.fence) != VK_SUCCESS) { // Don't bother freeing following allocations to ensure proper ordering. break; } diff --git a/src/xenia/ui/vulkan/circular_buffer.h b/src/xenia/ui/vulkan/circular_buffer.h index 85b069aa0..9f54b706a 100644 --- a/src/xenia/ui/vulkan/circular_buffer.h +++ b/src/xenia/ui/vulkan/circular_buffer.h @@ -12,8 +12,7 @@ #include -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace ui { @@ -27,7 +26,7 @@ namespace vulkan { // ends of the buffer), where trailing older allocations are freed after use. class CircularBuffer { public: - CircularBuffer(VulkanDevice* device, VkBufferUsageFlags usage, + CircularBuffer(const VulkanProvider& provider, VkBufferUsageFlags usage, VkDeviceSize capacity, VkDeviceSize alignment = 256); ~CircularBuffer(); @@ -76,7 +75,7 @@ class CircularBuffer { VkDeviceSize write_head_ = 0; VkDeviceSize read_head_ = 0; - VulkanDevice* device_; + const VulkanProvider& provider_; bool owns_gpu_memory_ = false; VkBuffer gpu_buffer_ = nullptr; VkDeviceMemory gpu_memory_ = nullptr; diff --git a/src/xenia/ui/vulkan/fenced_pools.cc b/src/xenia/ui/vulkan/fenced_pools.cc index f6ce79662..ef4794c71 100644 --- a/src/xenia/ui/vulkan/fenced_pools.cc +++ b/src/xenia/ui/vulkan/fenced_pools.cc @@ -17,12 +17,13 @@ namespace xe { namespace ui { namespace vulkan { -using xe::ui::vulkan::CheckResult; +using util::CheckResult; -CommandBufferPool::CommandBufferPool(const VulkanDevice& device, +CommandBufferPool::CommandBufferPool(const VulkanProvider& provider, uint32_t queue_family_index) - : BaseFencedPool(device) { - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); + : BaseFencedPool(provider) { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); // Create the pool used for allocating buffers. // They are marked as transient (short-lived) and cycled frequently. @@ -33,7 +34,7 @@ CommandBufferPool::CommandBufferPool(const VulkanDevice& device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; cmd_pool_info.queueFamilyIndex = queue_family_index; auto err = - dfn.vkCreateCommandPool(device_, &cmd_pool_info, nullptr, &command_pool_); + dfn.vkCreateCommandPool(device, &cmd_pool_info, nullptr, &command_pool_); CheckResult(err, "vkCreateCommandPool"); // Allocate a bunch of command buffers to start. @@ -45,7 +46,7 @@ CommandBufferPool::CommandBufferPool(const VulkanDevice& device, command_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; command_buffer_info.commandBufferCount = kDefaultCount; VkCommandBuffer command_buffers[kDefaultCount]; - err = dfn.vkAllocateCommandBuffers(device_, &command_buffer_info, + err = dfn.vkAllocateCommandBuffers(device, &command_buffer_info, command_buffers); CheckResult(err, "vkCreateCommandBuffer"); for (size_t i = 0; i < xe::countof(command_buffers); ++i) { @@ -55,8 +56,9 @@ CommandBufferPool::CommandBufferPool(const VulkanDevice& device, CommandBufferPool::~CommandBufferPool() { FreeAllEntries(); - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - dfn.vkDestroyCommandPool(device_, command_pool_, nullptr); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkDestroyCommandPool(device, command_pool_, nullptr); command_pool_ = nullptr; } @@ -70,21 +72,24 @@ VkCommandBuffer CommandBufferPool::AllocateEntry(void* data) { VkCommandBufferLevel(reinterpret_cast(data)); command_buffer_info.commandBufferCount = 1; VkCommandBuffer command_buffer; - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - auto err = dfn.vkAllocateCommandBuffers(device_, &command_buffer_info, + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + auto err = dfn.vkAllocateCommandBuffers(device, &command_buffer_info, &command_buffer); CheckResult(err, "vkCreateCommandBuffer"); return command_buffer; } void CommandBufferPool::FreeEntry(VkCommandBuffer handle) { - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - dfn.vkFreeCommandBuffers(device_, command_pool_, 1, &handle); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkFreeCommandBuffers(device, command_pool_, 1, &handle); } -DescriptorPool::DescriptorPool(const VulkanDevice& device, uint32_t max_count, +DescriptorPool::DescriptorPool(const VulkanProvider& provider, + uint32_t max_count, std::vector pool_sizes) - : BaseFencedPool(device) { + : BaseFencedPool(provider) { VkDescriptorPoolCreateInfo descriptor_pool_info; descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptor_pool_info.pNext = nullptr; @@ -93,15 +98,17 @@ DescriptorPool::DescriptorPool(const VulkanDevice& device, uint32_t max_count, descriptor_pool_info.maxSets = max_count; descriptor_pool_info.poolSizeCount = uint32_t(pool_sizes.size()); descriptor_pool_info.pPoolSizes = pool_sizes.data(); - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); auto err = dfn.vkCreateDescriptorPool(device, &descriptor_pool_info, nullptr, &descriptor_pool_); CheckResult(err, "vkCreateDescriptorPool"); } DescriptorPool::~DescriptorPool() { FreeAllEntries(); - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - dfn.vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkDestroyDescriptorPool(device, descriptor_pool_, nullptr); descriptor_pool_ = nullptr; } @@ -115,17 +122,19 @@ VkDescriptorSet DescriptorPool::AllocateEntry(void* data) { set_alloc_info.descriptorPool = descriptor_pool_; set_alloc_info.descriptorSetCount = 1; set_alloc_info.pSetLayouts = &layout; - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); auto err = - dfn.vkAllocateDescriptorSets(device_, &set_alloc_info, &descriptor_set); + dfn.vkAllocateDescriptorSets(device, &set_alloc_info, &descriptor_set); CheckResult(err, "vkAllocateDescriptorSets"); return descriptor_set; } void DescriptorPool::FreeEntry(VkDescriptorSet handle) { - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - dfn.vkFreeDescriptorSets(device_, descriptor_pool_, 1, &handle); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkFreeDescriptorSets(device, descriptor_pool_, 1, &handle); } } // namespace vulkan diff --git a/src/xenia/ui/vulkan/fenced_pools.h b/src/xenia/ui/vulkan/fenced_pools.h index 0af6dd694..728b7aaae 100644 --- a/src/xenia/ui/vulkan/fenced_pools.h +++ b/src/xenia/ui/vulkan/fenced_pools.h @@ -13,8 +13,7 @@ #include #include "xenia/base/assert.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" +#include "xenia/ui/vulkan/vulkan_provider.h" #include "xenia/ui/vulkan/vulkan_util.h" namespace xe { @@ -29,7 +28,7 @@ namespace vulkan { template class BaseFencedPool { public: - BaseFencedPool(const VulkanDevice& device) : device_(device) {} + BaseFencedPool(const VulkanProvider& provider) : provider_(provider) {} virtual ~BaseFencedPool() { // TODO(benvanik): wait on fence until done. @@ -48,12 +47,13 @@ class BaseFencedPool { // Checks all pending batches for completion and scavenges their entries. // This should be called as frequently as reasonable. void Scavenge() { - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); while (pending_batch_list_head_) { auto batch = pending_batch_list_head_; assert_not_null(batch->fence); - VkResult status = dfn.vkGetFenceStatus(device_, batch->fence); + VkResult status = dfn.vkGetFenceStatus(device, batch->fence); if (status == VK_SUCCESS || status == VK_ERROR_DEVICE_LOST) { // Batch has completed. Reclaim. pending_batch_list_head_ = batch->next; @@ -82,7 +82,8 @@ class BaseFencedPool { VkFence BeginBatch(VkFence fence = nullptr) { assert_null(open_batch_); Batch* batch = nullptr; - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); if (free_batch_list_head_) { // Reuse a batch. batch = free_batch_list_head_; @@ -91,10 +92,10 @@ class BaseFencedPool { if (batch->flags & kBatchOwnsFence && !fence) { // Reset owned fence. - dfn.vkResetFences(device_, 1, &batch->fence); + dfn.vkResetFences(device, 1, &batch->fence); } else if ((batch->flags & kBatchOwnsFence) && fence) { // Transfer owned -> external - dfn.vkDestroyFence(device_, batch->fence, nullptr); + dfn.vkDestroyFence(device, batch->fence, nullptr); batch->fence = fence; batch->flags &= ~kBatchOwnsFence; } else if (!(batch->flags & kBatchOwnsFence) && !fence) { @@ -103,8 +104,7 @@ class BaseFencedPool { info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; - VkResult res = - dfn.vkCreateFence(device_, &info, nullptr, &batch->fence); + VkResult res = dfn.vkCreateFence(device, &info, nullptr, &batch->fence); if (res != VK_SUCCESS) { assert_always(); } @@ -125,8 +125,7 @@ class BaseFencedPool { info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; - VkResult res = - dfn.vkCreateFence(device_, &info, nullptr, &batch->fence); + VkResult res = dfn.vkCreateFence(device, &info, nullptr, &batch->fence); if (res != VK_SUCCESS) { assert_always(); } @@ -244,14 +243,15 @@ class BaseFencedPool { } void FreeAllEntries() { - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); // Run down free lists. while (free_batch_list_head_) { auto batch = free_batch_list_head_; free_batch_list_head_ = batch->next; if (batch->flags & kBatchOwnsFence) { - dfn.vkDestroyFence(device_, batch->fence, nullptr); + dfn.vkDestroyFence(device, batch->fence, nullptr); batch->fence = nullptr; } delete batch; @@ -264,7 +264,7 @@ class BaseFencedPool { } } - const VulkanDevice& device_; + const VulkanProvider& provider_; private: struct Entry { @@ -294,7 +294,8 @@ class CommandBufferPool public: typedef BaseFencedPool Base; - CommandBufferPool(const VulkanDevice& device, uint32_t queue_family_index); + CommandBufferPool(const VulkanProvider& provider, + uint32_t queue_family_index); ~CommandBufferPool() override; VkCommandBuffer AcquireEntry( @@ -314,7 +315,7 @@ class DescriptorPool : public BaseFencedPool { public: typedef BaseFencedPool Base; - DescriptorPool(const VulkanDevice& device, uint32_t max_count, + DescriptorPool(const VulkanProvider& provider, uint32_t max_count, std::vector pool_sizes); ~DescriptorPool() override; diff --git a/src/xenia/ui/vulkan/functions/device_1_0.inc b/src/xenia/ui/vulkan/functions/device_1_0.inc index aed4f435a..58e18bb7f 100644 --- a/src/xenia/ui/vulkan/functions/device_1_0.inc +++ b/src/xenia/ui/vulkan/functions/device_1_0.inc @@ -3,16 +3,18 @@ XE_UI_VULKAN_FUNCTION(vkAllocateCommandBuffers) XE_UI_VULKAN_FUNCTION(vkAllocateDescriptorSets) XE_UI_VULKAN_FUNCTION(vkAllocateMemory) XE_UI_VULKAN_FUNCTION(vkBeginCommandBuffer) -XE_UI_VULKAN_FUNCTION(vkCmdBeginRenderPass) -XE_UI_VULKAN_FUNCTION(vkCmdBindDescriptorSets) XE_UI_VULKAN_FUNCTION(vkBindBufferMemory) XE_UI_VULKAN_FUNCTION(vkBindImageMemory) +XE_UI_VULKAN_FUNCTION(vkCmdBeginRenderPass) +XE_UI_VULKAN_FUNCTION(vkCmdBindDescriptorSets) XE_UI_VULKAN_FUNCTION(vkCmdBindIndexBuffer) XE_UI_VULKAN_FUNCTION(vkCmdBindPipeline) XE_UI_VULKAN_FUNCTION(vkCmdBindVertexBuffers) XE_UI_VULKAN_FUNCTION(vkCmdBlitImage) +XE_UI_VULKAN_FUNCTION(vkCmdClearAttachments) XE_UI_VULKAN_FUNCTION(vkCmdClearColorImage) XE_UI_VULKAN_FUNCTION(vkCmdClearDepthStencilImage) +XE_UI_VULKAN_FUNCTION(vkCmdCopyBuffer) XE_UI_VULKAN_FUNCTION(vkCmdCopyBufferToImage) XE_UI_VULKAN_FUNCTION(vkCmdCopyImageToBuffer) XE_UI_VULKAN_FUNCTION(vkCmdDraw) @@ -67,17 +69,20 @@ XE_UI_VULKAN_FUNCTION(vkFlushMappedMemoryRanges) XE_UI_VULKAN_FUNCTION(vkFreeCommandBuffers) XE_UI_VULKAN_FUNCTION(vkFreeDescriptorSets) XE_UI_VULKAN_FUNCTION(vkFreeMemory) -XE_UI_VULKAN_FUNCTION(vkGetDeviceQueue) XE_UI_VULKAN_FUNCTION(vkGetBufferMemoryRequirements) +XE_UI_VULKAN_FUNCTION(vkGetDeviceQueue) XE_UI_VULKAN_FUNCTION(vkGetFenceStatus) XE_UI_VULKAN_FUNCTION(vkGetImageMemoryRequirements) XE_UI_VULKAN_FUNCTION(vkGetImageSubresourceLayout) XE_UI_VULKAN_FUNCTION(vkGetPipelineCacheData) +XE_UI_VULKAN_FUNCTION(vkInvalidateMappedMemoryRanges) XE_UI_VULKAN_FUNCTION(vkMapMemory) +XE_UI_VULKAN_FUNCTION(vkResetCommandBuffer) +XE_UI_VULKAN_FUNCTION(vkResetCommandPool) +XE_UI_VULKAN_FUNCTION(vkResetDescriptorPool) +XE_UI_VULKAN_FUNCTION(vkResetFences) XE_UI_VULKAN_FUNCTION(vkQueueSubmit) XE_UI_VULKAN_FUNCTION(vkQueueWaitIdle) -XE_UI_VULKAN_FUNCTION(vkResetCommandBuffer) -XE_UI_VULKAN_FUNCTION(vkResetFences) XE_UI_VULKAN_FUNCTION(vkUnmapMemory) XE_UI_VULKAN_FUNCTION(vkUpdateDescriptorSets) XE_UI_VULKAN_FUNCTION(vkWaitForFences) diff --git a/src/xenia/ui/vulkan/functions/device_ext_debug_marker.inc b/src/xenia/ui/vulkan/functions/device_ext_debug_marker.inc deleted file mode 100644 index 11a44aa81..000000000 --- a/src/xenia/ui/vulkan/functions/device_ext_debug_marker.inc +++ /dev/null @@ -1,5 +0,0 @@ -// VK_EXT_debug_marker functions used in Xenia. -XE_UI_VULKAN_FUNCTION(vkDebugMarkerSetObjectNameEXT) -XE_UI_VULKAN_FUNCTION(vkCmdDebugMarkerBeginEXT) -XE_UI_VULKAN_FUNCTION(vkCmdDebugMarkerEndEXT) -XE_UI_VULKAN_FUNCTION(vkCmdDebugMarkerInsertEXT) diff --git a/src/xenia/ui/vulkan/functions/instance_1_0.inc b/src/xenia/ui/vulkan/functions/instance_1_0.inc index 8fbb7f8ca..0da71a57c 100644 --- a/src/xenia/ui/vulkan/functions/instance_1_0.inc +++ b/src/xenia/ui/vulkan/functions/instance_1_0.inc @@ -2,7 +2,6 @@ XE_UI_VULKAN_FUNCTION(vkCreateDevice) XE_UI_VULKAN_FUNCTION(vkDestroyDevice) XE_UI_VULKAN_FUNCTION(vkEnumerateDeviceExtensionProperties) -XE_UI_VULKAN_FUNCTION(vkEnumerateDeviceLayerProperties) XE_UI_VULKAN_FUNCTION(vkEnumeratePhysicalDevices) XE_UI_VULKAN_FUNCTION(vkGetDeviceProcAddr) XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceFeatures) diff --git a/src/xenia/ui/vulkan/functions/instance_ext_debug_report.inc b/src/xenia/ui/vulkan/functions/instance_ext_debug_report.inc deleted file mode 100644 index 4d57b9552..000000000 --- a/src/xenia/ui/vulkan/functions/instance_ext_debug_report.inc +++ /dev/null @@ -1,3 +0,0 @@ -// VK_EXT_debug_report functions used in Xenia. -XE_UI_VULKAN_FUNCTION(vkCreateDebugReportCallbackEXT) -XE_UI_VULKAN_FUNCTION(vkDestroyDebugReportCallbackEXT) diff --git a/src/xenia/ui/vulkan/functions/instance_ext_debug_utils.inc b/src/xenia/ui/vulkan/functions/instance_ext_debug_utils.inc new file mode 100644 index 000000000..c0c9df20c --- /dev/null +++ b/src/xenia/ui/vulkan/functions/instance_ext_debug_utils.inc @@ -0,0 +1,4 @@ +// VK_EXT_debug_utils functions used in Xenia. +XE_UI_VULKAN_FUNCTION(vkCreateDebugUtilsMessengerEXT) +XE_UI_VULKAN_FUNCTION(vkDestroyDebugUtilsMessengerEXT) +XE_UI_VULKAN_FUNCTION(vkSetDebugUtilsObjectNameEXT) diff --git a/src/xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc b/src/xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc new file mode 100644 index 000000000..05b2fe800 --- /dev/null +++ b/src/xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc @@ -0,0 +1,4 @@ +// VK_KHR_get_physical_device_properties2 functions used in Xenia. +// Promoted to Vulkan 1.1 core. +XE_UI_VULKAN_FUNCTION_PROMOTED(vkGetPhysicalDeviceProperties2KHR, + vkGetPhysicalDeviceProperties2) diff --git a/src/xenia/ui/vulkan/functions/instance_khr_win32_surface.inc b/src/xenia/ui/vulkan/functions/instance_khr_win32_surface.inc index bbda68af6..9b6d673d5 100644 --- a/src/xenia/ui/vulkan/functions/instance_khr_win32_surface.inc +++ b/src/xenia/ui/vulkan/functions/instance_khr_win32_surface.inc @@ -1,2 +1,3 @@ // VK_KHR_win32_surface functions used in Xenia. XE_UI_VULKAN_FUNCTION(vkCreateWin32SurfaceKHR) +XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceWin32PresentationSupportKHR) diff --git a/src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.spv b/src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.spv deleted file mode 100644 index cb458c810ea73ba9eed8f20690e77dbd46713986..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmYk3OG^V$429#|POVm3YoQ`OXmwv)s0gAPMK|4u3ztLDp$fj3-j@D8f0Y}-^JN%l zhLD?cPm*&pAvs<(Td}$|?9fW@nv{=b^@bif#7#-)zx&yKgMsL z;0^hf{J>-II}H(5o;P$3`Vz$-4M@dUu1{T!+2lRDE5?)C{5!siDqd>TPK)O(=8HHh za<669JT9`?98U1LUpLaNu4rk>dvbE>kK|aD56aYYdR2Sio;Or^SBlX*aP{Wrp3Ze) zefOI2PPPMfsd{oWfbH6vzVeQ}E?brFiMg%|czxBQ9Rs5uId8yrl(!U_g)aDQMS6k5 zC+AII@V6D=vv2tOif99ePtKdc&b3YY!o|RTg`|pCLeaD-M-MqOJ96ediAB$@91i${ UFlO^Fz_Dv}Wj|nlQh6c&2exQGeyF1(;rrhQ1 zbCe%XaE(}m+ZkmaUJnOZ-W%i@ek10_Jl9D3S)T6g=H0x1oR89eH|rf7?)OcjV}tia z98yoye+}HneVsKrD)MgO|KB2w4tegm2|W7lsIOLUnBab%E$|X_c(d zz17>Ju0LVw*k`cndJ3rNbE3Xm{}mbPung2neau&r6(IkG9Q9ub!kpKVy diff --git a/src/xenia/ui/vulkan/shaders/bin/immediate_frag.txt b/src/xenia/ui/vulkan/shaders/bin/immediate_frag.txt deleted file mode 100644 index 68aeadb07..000000000 --- a/src/xenia/ui/vulkan/shaders/bin/immediate_frag.txt +++ /dev/null @@ -1,83 +0,0 @@ -; SPIR-V -; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 6 -; Bound: 51 -; Schema: 0 - OpCapability Shader - OpCapability Sampled1D - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" %out_color %vtx_color %vtx_uv - OpExecutionMode %main OriginUpperLeft - OpSource GLSL 450 - OpName %main "main" - OpName %out_color "out_color" - OpName %vtx_color "vtx_color" - OpName %PushConstants "PushConstants" - OpMemberName %PushConstants 0 "restrict_texture_samples" - OpName %push_constants "push_constants" - OpName %vtx_uv "vtx_uv" - OpName %texture_sampler "texture_sampler" - OpDecorate %out_color Location 0 - OpDecorate %vtx_color Location 1 - OpMemberDecorate %PushConstants 0 Offset 64 - OpDecorate %PushConstants Block - OpDecorate %vtx_uv Location 0 - OpDecorate %texture_sampler DescriptorSet 0 - OpDecorate %texture_sampler Binding 0 - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 -%_ptr_Output_v4float = OpTypePointer Output %v4float - %out_color = OpVariable %_ptr_Output_v4float Output -%_ptr_Input_v4float = OpTypePointer Input %v4float - %vtx_color = OpVariable %_ptr_Input_v4float Input - %bool = OpTypeBool - %int = OpTypeInt 32 1 -%PushConstants = OpTypeStruct %int -%_ptr_PushConstant_PushConstants = OpTypePointer PushConstant %PushConstants -%push_constants = OpVariable %_ptr_PushConstant_PushConstants PushConstant - %int_0 = OpConstant %int 0 -%_ptr_PushConstant_int = OpTypePointer PushConstant %int - %v2float = OpTypeVector %float 2 -%_ptr_Input_v2float = OpTypePointer Input %v2float - %vtx_uv = OpVariable %_ptr_Input_v2float Input - %uint = OpTypeInt 32 0 - %uint_0 = OpConstant %uint 0 -%_ptr_Input_float = OpTypePointer Input %float - %float_1 = OpConstant %float 1 - %41 = OpTypeImage %float 2D 0 0 0 1 Unknown - %42 = OpTypeSampledImage %41 -%_ptr_UniformConstant_42 = OpTypePointer UniformConstant %42 -%texture_sampler = OpVariable %_ptr_UniformConstant_42 UniformConstant - %main = OpFunction %void None %3 - %5 = OpLabel - %12 = OpLoad %v4float %vtx_color - OpStore %out_color %12 - %20 = OpAccessChain %_ptr_PushConstant_int %push_constants %int_0 - %21 = OpLoad %int %20 - %22 = OpIEqual %bool %21 %int_0 - %23 = OpLogicalNot %bool %22 - OpSelectionMerge %25 None - OpBranchConditional %23 %24 %25 - %24 = OpLabel - %32 = OpAccessChain %_ptr_Input_float %vtx_uv %uint_0 - %33 = OpLoad %float %32 - %35 = OpFOrdLessThanEqual %bool %33 %float_1 - OpBranch %25 - %25 = OpLabel - %36 = OpPhi %bool %22 %5 %35 %24 - OpSelectionMerge %38 None - OpBranchConditional %36 %37 %38 - %37 = OpLabel - %45 = OpLoad %42 %texture_sampler - %46 = OpLoad %v2float %vtx_uv - %47 = OpImageSampleImplicitLod %v4float %45 %46 - %49 = OpLoad %v4float %out_color - %50 = OpFMul %v4float %49 %47 - OpStore %out_color %50 - OpBranch %38 - %38 = OpLabel - OpReturn - OpFunctionEnd diff --git a/src/xenia/ui/vulkan/shaders/bin/immediate_vert.h b/src/xenia/ui/vulkan/shaders/bin/immediate_vert.h deleted file mode 100644 index 7a2941e27..000000000 --- a/src/xenia/ui/vulkan/shaders/bin/immediate_vert.h +++ /dev/null @@ -1,128 +0,0 @@ -// generated from `xb genspirv` -// source: immediate.vert -const uint8_t immediate_vert[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, - 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x69, 0x6E, 0x74, - 0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, - 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, - 0x06, 0x00, 0x07, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x67, 0x6C, 0x5F, 0x43, 0x75, 0x6C, 0x6C, 0x44, 0x69, 0x73, 0x74, 0x61, - 0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x50, 0x75, 0x73, 0x68, 0x43, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, - 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x6F, 0x6A, 0x65, 0x63, 0x74, 0x69, - 0x6F, 0x6E, 0x5F, 0x6D, 0x61, 0x74, 0x72, 0x69, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x13, 0x00, 0x00, 0x00, 0x70, 0x75, 0x73, 0x68, - 0x5F, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x70, - 0x6F, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x76, 0x74, 0x78, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F, - 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x2C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x20, 0x00, 0x04, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x2D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x22, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x2F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, -}; diff --git a/src/xenia/ui/vulkan/shaders/bin/immediate_vert.spv b/src/xenia/ui/vulkan/shaders/bin/immediate_vert.spv deleted file mode 100644 index a8f67216478dd1c38799e3a7ba3e9c2d6f00bdb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1488 zcmYk4T~8B16o!YE1w;h_`B1U8Rt;ZPFVvVAH3XA#!9@)Tx0|MHV9c`H>~0C+2L1wn z$zSD-iSM&JQ#+fpXWsXG%sFSKUTe=9Gi7GXoOy4mwPb2yjJYos-90)!>g2^~=flU+ zWx>=-M{JAoVoggf`Zi(zQ1V2wE!mOmNjj3czMAxJ1qOB4SHVKER5`me|WpNdb zE-TEeWCzL~lyk#ANaHkf9CMV9r_7=JsvF?Gs^vLm1Bdn;Gl8Rj&#{N&KDx57$L~;# z?=>iKFve|R7CF3+9+G(^AqN~ex~}hr_AD@cPh$E8Qzv%b%Os{}F!@&WN4Inab+cd7 zZbrH(Syw!Dfazyd|2gTVgthDNJ?%MfQ+Qq)f4GKr^zUQ zepO!10WV5e8^X+nywufD6f=S&_p&tS!;u@zJA%Xh*zNJB$8~9Hrhn`k(o+(6;=#N( zIBLQ7nS|x>W{GaU6W`LFKBy1ec9=Numcu?jc-wLQJnoV6z~ti|$w~ZMX>8=j4(49r zKIsk72XkQ&^G6JCZeM?TUzIQq=J{2g%wbIe52miC5_W&kG(1G)eJ diff --git a/src/xenia/ui/vulkan/shaders/bin/blit_color_frag.txt b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_color_frag.txt similarity index 98% rename from src/xenia/ui/vulkan/shaders/bin/blit_color_frag.txt rename to src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_color_frag.txt index 5720bad67..36ae0fb3f 100644 --- a/src/xenia/ui/vulkan/shaders/bin/blit_color_frag.txt +++ b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_color_frag.txt @@ -1,6 +1,6 @@ ; SPIR-V ; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 6 +; Generator: Khronos Glslang Reference Front End; 10 ; Bound: 36 ; Schema: 0 OpCapability Shader diff --git a/src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.h b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.h similarity index 98% rename from src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.h rename to src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.h index ac499a78f..ceec5cedb 100644 --- a/src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.h +++ b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.h @@ -1,7 +1,7 @@ // generated from `xb genspirv` // source: blit_depth.frag const uint8_t blit_depth_frag[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, diff --git a/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.spv b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..59b51dfd660e6af10033a4c71225857585e7d9f8 GIT binary patch literal 660 zcmYk3OH0F05QWF2skPPCTBwK*`q&p2DuU=nv72thh09PhRKXYNZRzjxSGf^9--JLX z40F%9XC8MFN5f6C4XaztPOPxDMPkevR`ocZJWfXQ@G?3(KSj~B*oA0X%Bv{iAJex_ zE^6{!`LV~?@6<$Gao*NB))y)Ms7NBkdVZq5)R?`e_xW^omwks z*K@BJ?_@i0SIP%71Kfda=_~Ho-MW?dP|CV4(Djth>=+#Nz`Oz57j7!ji@DIZ6sZM| z9?YBI(C;atXW!_L6qyYkJ(xGcU1*!sMT^1xib)i&#WK^5oO!_XY|H8QBo#F~ay0M{ U;^@u4fX8lBmHoi|Y069aKf)*=>i_@% literal 0 HcmV?d00001 diff --git a/src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.txt b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.txt similarity index 97% rename from src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.txt rename to src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.txt index e2bd81d13..563dc1550 100644 --- a/src/xenia/ui/vulkan/shaders/bin/blit_depth_frag.txt +++ b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_depth_frag.txt @@ -1,6 +1,6 @@ ; SPIR-V ; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 6 +; Generator: Khronos Glslang Reference Front End; 10 ; Bound: 30 ; Schema: 0 OpCapability Shader diff --git a/src/xenia/ui/vulkan/shaders/bin/blit_vert.h b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.h similarity index 99% rename from src/xenia/ui/vulkan/shaders/bin/blit_vert.h rename to src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.h index f101b89c9..53e39aa65 100644 --- a/src/xenia/ui/vulkan/shaders/bin/blit_vert.h +++ b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.h @@ -1,7 +1,7 @@ // generated from `xb genspirv` // source: blit.vert const uint8_t blit_vert[] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, diff --git a/src/xenia/ui/vulkan/shaders/bin/blit_vert.spv b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.spv similarity index 61% rename from src/xenia/ui/vulkan/shaders/bin/blit_vert.spv rename to src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.spv index f68aabdb86a17173775b417187502b5e319b9fbc..4a7dc6d1784d06a14808b667186bedac6a4aa3e4 100644 GIT binary patch delta 18 ZcmX@YdxV#hnMs+Qfq{{MYa{1wHUJ{h17QFF delta 18 ZcmX@YdxV#hnMs+Qfq{{MZ6oJyHUJ{N16=?B diff --git a/src/xenia/ui/vulkan/shaders/bin/blit_vert.txt b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.txt similarity index 98% rename from src/xenia/ui/vulkan/shaders/bin/blit_vert.txt rename to src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.txt index 7f423f63d..216c2cbec 100644 --- a/src/xenia/ui/vulkan/shaders/bin/blit_vert.txt +++ b/src/xenia/ui/vulkan/shaders/bytecode/vulkan_spirv/blit_vert.txt @@ -1,6 +1,6 @@ ; SPIR-V ; Version: 1.0 -; Generator: Khronos Glslang Reference Front End; 6 +; Generator: Khronos Glslang Reference Front End; 10 ; Bound: 76 ; Schema: 0 OpCapability Shader diff --git a/src/xenia/ui/vulkan/shaders/immediate.frag b/src/xenia/ui/vulkan/shaders/immediate.frag deleted file mode 100644 index bf505bf46..000000000 --- a/src/xenia/ui/vulkan/shaders/immediate.frag +++ /dev/null @@ -1,28 +0,0 @@ -// NOTE: This file is compiled and embedded into the exe. -// Use `xenia-build genspirv` and check in any changes under bin/. - -#version 450 core -precision highp float; - -layout(push_constant) uniform PushConstants { - layout(offset = 64) int restrict_texture_samples; -} push_constants; - -layout(set = 0, binding = 0) uniform sampler2D texture_sampler; - -layout(set = 0, binding = 1) uniform sampler1D tex1D; -layout(set = 0, binding = 1) uniform sampler2D tex2D; - -layout(location = 0) in vec2 vtx_uv; -layout(location = 1) in vec4 vtx_color; - -layout(location = 0) out vec4 out_color; - -void main() { - out_color = vtx_color; - if (push_constants.restrict_texture_samples == 0 || vtx_uv.x <= 1.0) { - vec4 tex_color = texture(texture_sampler, vtx_uv); - out_color *= tex_color; - // TODO(benvanik): microprofiler shadows. - } -} diff --git a/src/xenia/ui/vulkan/shaders/immediate.vert b/src/xenia/ui/vulkan/shaders/immediate.vert deleted file mode 100644 index 8ac6c4b70..000000000 --- a/src/xenia/ui/vulkan/shaders/immediate.vert +++ /dev/null @@ -1,23 +0,0 @@ -// NOTE: This file is compiled and embedded into the exe. -// Use `xenia-build genspirv` and check in any changes under bin/. - -#version 450 core -precision highp float; - -layout(push_constant) uniform PushConstants { - layout(offset = 0) mat4 projection_matrix; -} push_constants; - -layout(location = 0) in vec2 in_pos; -layout(location = 1) in vec2 in_uv; -layout(location = 2) in vec4 in_color; - -layout(location = 0) out vec2 vtx_uv; -layout(location = 1) out vec4 vtx_color; - -void main() { - gl_Position = push_constants.projection_matrix * vec4(in_pos.xy, 0.0, 1.0); - gl_Position.y = -gl_Position.y; - vtx_uv = in_uv; - vtx_color = in_color; -} diff --git a/src/xenia/ui/vulkan/vulkan.cc b/src/xenia/ui/vulkan/vulkan.cc deleted file mode 100644 index 5bc610557..000000000 --- a/src/xenia/ui/vulkan/vulkan.cc +++ /dev/null @@ -1,18 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/vulkan/vulkan.h" - -DEFINE_bool(vulkan_validation, false, "Enable Vulkan validation layers.", - "Vulkan"); - -DEFINE_bool(vulkan_primary_queue_only, false, - "Force the use of the primary queue, ignoring any additional that " - "may be present.", - "Vulkan"); diff --git a/src/xenia/ui/vulkan/vulkan.h b/src/xenia/ui/vulkan/vulkan.h deleted file mode 100644 index 9ba44197c..000000000 --- a/src/xenia/ui/vulkan/vulkan.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_VULKAN_VULKAN_H_ -#define XENIA_UI_VULKAN_VULKAN_H_ - -#include "xenia/base/cvar.h" -#include "xenia/base/platform.h" - -#if XE_PLATFORM_ANDROID -#ifndef VK_USE_PLATFORM_ANDROID_KHR -#define VK_USE_PLATFORM_ANDROID_KHR 1 -#endif -#elif XE_PLATFORM_GNU_LINUX -#ifndef VK_USE_PLATFORM_XCB_KHR -#define VK_USE_PLATFORM_XCB_KHR 1 -#endif -#elif XE_PLATFORM_WIN32 -// Must be included before vulkan.h with VK_USE_PLATFORM_WIN32_KHR because it -// includes Windows.h too. -#include "xenia/base/platform_win.h" -#ifndef VK_USE_PLATFORM_WIN32_KHR -#define VK_USE_PLATFORM_WIN32_KHR 1 -#endif -#endif - -#ifndef VK_NO_PROTOTYPES -#define VK_NO_PROTOTYPES 1 -#endif -#include "third_party/vulkan/vulkan.h" - -#define XELOGVK XELOGI - -DECLARE_bool(vulkan_validation); -DECLARE_bool(vulkan_primary_queue_only); - -#endif // XENIA_UI_VULKAN_VULKAN_H_ diff --git a/src/xenia/ui/vulkan/vulkan_context.cc b/src/xenia/ui/vulkan/vulkan_context.cc deleted file mode 100644 index 50a4e7a8a..000000000 --- a/src/xenia/ui/vulkan/vulkan_context.cc +++ /dev/null @@ -1,206 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/vulkan/vulkan_context.h" - -#include -#include - -#include "xenia/base/assert.h" -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/profiling.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" -#include "xenia/ui/vulkan/vulkan_immediate_drawer.h" -#include "xenia/ui/vulkan/vulkan_instance.h" -#include "xenia/ui/vulkan/vulkan_provider.h" -#include "xenia/ui/vulkan/vulkan_swap_chain.h" -#include "xenia/ui/vulkan/vulkan_util.h" -#include "xenia/ui/window.h" - -#if XE_PLATFORM_GNU_LINUX -#include "xenia/ui/window_gtk.h" - -#include -#endif - -namespace xe { -namespace ui { -namespace vulkan { - -VulkanContext::VulkanContext(VulkanProvider* provider, Window* target_window) - : GraphicsContext(provider, target_window) {} - -VulkanContext::~VulkanContext() { - VkResult status; - auto provider = static_cast(provider_); - VulkanDevice* device = provider->device(); - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); - { - std::lock_guard queue_lock(device->primary_queue_mutex()); - status = dfn.vkQueueWaitIdle(device->primary_queue()); - } - immediate_drawer_.reset(); - swap_chain_.reset(); -} - -bool VulkanContext::Initialize() { - auto provider = static_cast(provider_); - VulkanInstance* instance = provider->instance(); - const VulkanInstance::InstanceFunctions& ifn = instance->ifn(); - - if (target_window_) { - // Create swap chain used to present to the window. - VkResult status = VK_ERROR_FEATURE_NOT_PRESENT; - VkSurfaceKHR surface = nullptr; -#if XE_PLATFORM_WIN32 - VkWin32SurfaceCreateInfoKHR create_info; - create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - create_info.pNext = nullptr; - create_info.flags = 0; - create_info.hinstance = - static_cast(target_window_->native_platform_handle()); - create_info.hwnd = static_cast(target_window_->native_handle()); - status = ifn.vkCreateWin32SurfaceKHR(*provider->instance(), &create_info, - nullptr, &surface); - CheckResult(status, "vkCreateWin32SurfaceKHR"); -#elif XE_PLATFORM_GNU_LINUX -#ifdef GDK_WINDOWING_X11 - GtkWidget* window_handle = - dynamic_cast(target_window_)->native_window_handle(); - xcb_window_t window = - gdk_x11_window_get_xid(gtk_widget_get_window(window_handle)); - VkXcbSurfaceCreateInfoKHR create_info; - create_info.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; - create_info.pNext = nullptr; - create_info.flags = 0; - create_info.connection = static_cast( - target_window_->native_platform_handle()); - create_info.window = static_cast(window); - status = ifn.vkCreateXcbSurfaceKHR(*provider->instance(), &create_info, - nullptr, &surface); - CheckResult(status, "vkCreateXcbSurfaceKHR"); -#else -#error Unsupported GDK Backend on Linux. -#endif // GDK_WINDOWING_X11 -#else -#error Platform not yet implemented. -#endif // XE_PLATFORM_WIN32 - if (status != VK_SUCCESS) { - XELOGE("Failed to create presentation surface"); - return false; - } - - swap_chain_ = - std::make_unique(instance, provider->device()); - if (swap_chain_->Initialize(surface) != VK_SUCCESS) { - XELOGE("Unable to initialize swap chain"); - return false; - } - - // Only initialize immediate mode drawer if we are not an offscreen context. - immediate_drawer_ = std::make_unique(this); - status = immediate_drawer_->Initialize(); - if (status != VK_SUCCESS) { - XELOGE("Failed to initialize the immediate mode drawer"); - immediate_drawer_.reset(); - return false; - } - } - - return true; -} - -ImmediateDrawer* VulkanContext::immediate_drawer() { - return immediate_drawer_.get(); -} - -VulkanInstance* VulkanContext::instance() const { - return static_cast(provider_)->instance(); -} - -VulkanDevice* VulkanContext::device() const { - return static_cast(provider_)->device(); -} - -bool VulkanContext::is_current() { return false; } - -bool VulkanContext::MakeCurrent() { - SCOPE_profile_cpu_f("gpu"); - return true; -} - -void VulkanContext::ClearCurrent() {} - -bool VulkanContext::BeginSwap() { - SCOPE_profile_cpu_f("gpu"); - auto provider = static_cast(provider_); - VulkanDevice* device = provider->device(); - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); - - VkResult status; - - // If we have a window see if it's been resized since we last swapped. - // If it has been, we'll need to reinitialize the swap chain before we - // start touching it. - if (target_window_) { - if (target_window_->scaled_width() != swap_chain_->surface_width() || - target_window_->scaled_height() != swap_chain_->surface_height()) { - // Resized! - swap_chain_->Reinitialize(); - } - } - - if (!context_lost_) { - // Acquire the next image and set it up for use. - status = swap_chain_->Begin(); - if (status == VK_ERROR_DEVICE_LOST) { - context_lost_ = true; - } - } - - // TODO(benvanik): use a fence instead? May not be possible with target image. - std::lock_guard queue_lock(device->primary_queue_mutex()); - status = dfn.vkQueueWaitIdle(device->primary_queue()); - - return true; -} - -void VulkanContext::EndSwap() { - SCOPE_profile_cpu_f("gpu"); - auto provider = static_cast(provider_); - VulkanDevice* device = provider->device(); - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); - - VkResult status; - - if (!context_lost_) { - // Notify the presentation engine the image is ready. - // The contents must be in a coherent state. - status = swap_chain_->End(); - if (status == VK_ERROR_DEVICE_LOST) { - context_lost_ = true; - } - } - - // Wait until the queue is idle. - // TODO(benvanik): is this required? - std::lock_guard queue_lock(device->primary_queue_mutex()); - status = dfn.vkQueueWaitIdle(device->primary_queue()); -} - -std::unique_ptr VulkanContext::Capture() { - // TODO(benvanik): read back swap chain front buffer. - return nullptr; -} - -} // namespace vulkan -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_context.h b/src/xenia/ui/vulkan/vulkan_context.h deleted file mode 100644 index f5658bdd1..000000000 --- a/src/xenia/ui/vulkan/vulkan_context.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_VULKAN_VULKAN_CONTEXT_H_ -#define XENIA_UI_VULKAN_VULKAN_CONTEXT_H_ - -#include - -#include "xenia/ui/graphics_context.h" -#include "xenia/ui/vulkan/vulkan.h" - -namespace xe { -namespace ui { -namespace vulkan { - -class VulkanDevice; -class VulkanImmediateDrawer; -class VulkanInstance; -class VulkanProvider; -class VulkanSwapChain; - -class VulkanContext : public GraphicsContext { - public: - ~VulkanContext() override; - - ImmediateDrawer* immediate_drawer() override; - VulkanSwapChain* swap_chain() const { return swap_chain_.get(); } - VulkanInstance* instance() const; - VulkanDevice* device() const; - - bool is_current() override; - bool MakeCurrent() override; - void ClearCurrent() override; - - bool WasLost() override { return context_lost_; } - - bool BeginSwap() override; - void EndSwap() override; - - std::unique_ptr Capture() override; - - protected: - bool context_lost_ = false; - - private: - friend class VulkanProvider; - - explicit VulkanContext(VulkanProvider* provider, Window* target_window); - - private: - bool Initialize(); - - std::unique_ptr swap_chain_; - std::unique_ptr immediate_drawer_; -}; - -} // namespace vulkan -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_VULKAN_VULKAN_CONTEXT_H_ diff --git a/src/xenia/ui/vulkan/vulkan_device.cc b/src/xenia/ui/vulkan/vulkan_device.cc deleted file mode 100644 index 704e43c2d..000000000 --- a/src/xenia/ui/vulkan/vulkan_device.cc +++ /dev/null @@ -1,427 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2017 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/vulkan/vulkan_device.h" - -#include -#include -#include -#include -#include - -#include "third_party/renderdoc/renderdoc_app.h" - -#include "xenia/base/assert.h" -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/profiling.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_immediate_drawer.h" -#include "xenia/ui/vulkan/vulkan_instance.h" -#include "xenia/ui/vulkan/vulkan_util.h" -#include "xenia/ui/window.h" - -namespace xe { -namespace ui { -namespace vulkan { - -VulkanDevice::VulkanDevice(VulkanInstance* instance) : instance_(instance) { - if (cvars::vulkan_validation) { - DeclareRequiredLayer("VK_LAYER_LUNARG_standard_validation", - Version::Make(0, 0, 0), true); - // DeclareRequiredLayer("VK_LAYER_GOOGLE_unique_objects", Version::Make(0, - // 0, 0), true); - /* - DeclareRequiredLayer("VK_LAYER_GOOGLE_threading", Version::Make(0, 0, 0), - true); - DeclareRequiredLayer("VK_LAYER_LUNARG_core_validation", - Version::Make(0, 0, 0), true); - DeclareRequiredLayer("VK_LAYER_LUNARG_object_tracker", - Version::Make(0, 0, 0), true); - DeclareRequiredLayer("VK_LAYER_LUNARG_draw_state", Version::Make(0, 0, 0), - true); - DeclareRequiredLayer("VK_LAYER_LUNARG_parameter_validation", - Version::Make(0, 0, 0), true); - DeclareRequiredLayer("VK_LAYER_LUNARG_swapchain", Version::Make(0, 0, 0), - true); - DeclareRequiredLayer("VK_LAYER_LUNARG_device_limits", - Version::Make(0, 0, 0), true); - DeclareRequiredLayer("VK_LAYER_LUNARG_image", Version::Make(0, 0, 0), true); - */ - } - - // AMD shader info (optional) - DeclareRequiredExtension(VK_AMD_SHADER_INFO_EXTENSION_NAME, - Version::Make(0, 0, 0), true); - // Debug markers (optional) - DeclareRequiredExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME, - Version::Make(0, 0, 0), true); - - DeclareRequiredExtension(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, - Version::Make(0, 0, 0), false); -} - -VulkanDevice::~VulkanDevice() { - if (handle) { - const VulkanInstance::InstanceFunctions& ifn = instance_->ifn(); - ifn.vkDestroyDevice(handle, nullptr); - handle = nullptr; - } -} - -bool VulkanDevice::Initialize(DeviceInfo device_info) { - // Gather list of enabled layer names. - auto layers_result = CheckRequirements(required_layers_, device_info.layers); - auto& enabled_layers = layers_result.second; - - // Gather list of enabled extension names. - auto extensions_result = - CheckRequirements(required_extensions_, device_info.extensions); - enabled_extensions_ = extensions_result.second; - - // We wait until both extensions and layers are checked before failing out so - // that the user gets a complete list of what they have/don't. - if (!extensions_result.first || !layers_result.first) { - FatalVulkanError( - "Layer and extension verification failed; aborting initialization"); - return false; - } - - const VulkanInstance::InstanceFunctions& ifn = instance_->ifn(); - - // Query supported features so we can make sure we have what we need. - VkPhysicalDeviceFeatures supported_features; - ifn.vkGetPhysicalDeviceFeatures(device_info.handle, &supported_features); - VkPhysicalDeviceFeatures enabled_features = {0}; - bool any_features_missing = false; -#define ENABLE_AND_EXPECT(name) \ - if (!supported_features.name) { \ - any_features_missing = true; \ - FatalVulkanError("Vulkan device is missing feature " #name); \ - } else { \ - enabled_features.name = VK_TRUE; \ - } - ENABLE_AND_EXPECT(shaderClipDistance); - ENABLE_AND_EXPECT(shaderCullDistance); - ENABLE_AND_EXPECT(shaderStorageImageExtendedFormats); - ENABLE_AND_EXPECT(shaderTessellationAndGeometryPointSize); - ENABLE_AND_EXPECT(samplerAnisotropy); - ENABLE_AND_EXPECT(geometryShader); - ENABLE_AND_EXPECT(depthClamp); - ENABLE_AND_EXPECT(multiViewport); - ENABLE_AND_EXPECT(independentBlend); - ENABLE_AND_EXPECT(textureCompressionBC); - // TODO(benvanik): add other features. - if (any_features_missing) { - XELOGE( - "One or more required device features are missing; aborting " - "initialization"); - return false; - } - - // Pick a queue. - // Any queue we use must support both graphics and presentation. - // TODO(benvanik): use multiple queues (DMA-only, compute-only, etc). - if (device_info.queue_family_properties.empty()) { - FatalVulkanError("No queue families available"); - return false; - } - uint32_t ideal_queue_family_index = UINT_MAX; - uint32_t queue_count = 1; - for (size_t i = 0; i < device_info.queue_family_properties.size(); ++i) { - auto queue_flags = device_info.queue_family_properties[i].queueFlags; - if (queue_flags & VK_QUEUE_GRAPHICS_BIT && - queue_flags & VK_QUEUE_TRANSFER_BIT) { - // Can do graphics and transfer - good! - ideal_queue_family_index = static_cast(i); - // Grab all the queues we can. - queue_count = device_info.queue_family_properties[i].queueCount; - break; - } - } - if (ideal_queue_family_index == UINT_MAX) { - FatalVulkanError( - "No queue families available that can both do graphics and transfer"); - return false; - } - - // Some tools *cough* renderdoc *cough* can't handle multiple queues. - if (cvars::vulkan_primary_queue_only) { - queue_count = 1; - } - - std::vector queue_infos; - std::vector> queue_priorities; - queue_infos.resize(device_info.queue_family_properties.size()); - queue_priorities.resize(queue_infos.size()); - for (int i = 0; i < queue_infos.size(); i++) { - VkDeviceQueueCreateInfo& queue_info = queue_infos[i]; - VkQueueFamilyProperties& family_props = - device_info.queue_family_properties[i]; - - queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_info.pNext = nullptr; - queue_info.flags = 0; - queue_info.queueFamilyIndex = i; - queue_info.queueCount = family_props.queueCount; - - queue_priorities[i].resize(family_props.queueCount, 0.f); - if (i == ideal_queue_family_index) { - // Prioritize the first queue on the primary queue family. - queue_priorities[i][0] = 1.0f; - } - - queue_info.pQueuePriorities = queue_priorities[i].data(); - } - - VkDeviceCreateInfo create_info; - create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - create_info.pNext = nullptr; - create_info.flags = 0; - create_info.queueCreateInfoCount = static_cast(queue_infos.size()); - create_info.pQueueCreateInfos = queue_infos.data(); - create_info.enabledLayerCount = static_cast(enabled_layers.size()); - create_info.ppEnabledLayerNames = enabled_layers.data(); - create_info.enabledExtensionCount = - static_cast(enabled_extensions_.size()); - create_info.ppEnabledExtensionNames = enabled_extensions_.data(); - create_info.pEnabledFeatures = &enabled_features; - - auto err = - ifn.vkCreateDevice(device_info.handle, &create_info, nullptr, &handle); - switch (err) { - case VK_SUCCESS: - // Ok! - break; - case VK_ERROR_INITIALIZATION_FAILED: - FatalVulkanError("Device initialization failed; generic"); - return false; - case VK_ERROR_EXTENSION_NOT_PRESENT: - FatalVulkanError( - "Device initialization failed; requested extension not present"); - return false; - case VK_ERROR_LAYER_NOT_PRESENT: - FatalVulkanError( - "Device initialization failed; requested layer not present"); - return false; - default: - FatalVulkanError(std::string("Device initialization failed; unknown: ") + - to_string(err)); - return false; - } - - // Get device functions. - std::memset(&dfn_, 0, sizeof(dfn_)); - bool device_functions_loaded = true; - debug_marker_ena_ = false; -#define XE_UI_VULKAN_FUNCTION(name) \ - device_functions_loaded &= \ - (dfn_.name = PFN_##name(ifn.vkGetDeviceProcAddr(handle, #name))) != \ - nullptr; -#include "xenia/ui/vulkan/functions/device_1_0.inc" -#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc" - if (HasEnabledExtension(VK_AMD_SHADER_INFO_EXTENSION_NAME)) { -#include "xenia/ui/vulkan/functions/device_amd_shader_info.inc" - } - debug_marker_ena_ = HasEnabledExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); - if (debug_marker_ena_) { -#include "xenia/ui/vulkan/functions/device_ext_debug_marker.inc" - } -#undef XE_UI_VULKAN_FUNCTION - if (!device_functions_loaded) { - XELOGE("Failed to get Vulkan device function pointers"); - return false; - } - - device_info_ = std::move(device_info); - queue_family_index_ = ideal_queue_family_index; - - // Get the primary queue used for most submissions/etc. - dfn_.vkGetDeviceQueue(handle, queue_family_index_, 0, &primary_queue_); - if (!primary_queue_) { - XELOGE("vkGetDeviceQueue returned nullptr!"); - return false; - } - - // Get all additional queues, if we got any. - free_queues_.resize(device_info_.queue_family_properties.size()); - for (uint32_t i = 0; i < device_info_.queue_family_properties.size(); i++) { - VkQueueFamilyProperties& family_props = - device_info_.queue_family_properties[i]; - - for (uint32_t j = 0; j < family_props.queueCount; j++) { - VkQueue queue = nullptr; - if (i == queue_family_index_ && j == 0) { - // Already retrieved the primary queue index. - continue; - } - - dfn_.vkGetDeviceQueue(handle, i, j, &queue); - if (queue) { - free_queues_[i].push_back(queue); - } - } - } - - XELOGVK("Device initialized successfully!"); - return true; -} - -bool VulkanDevice::HasEnabledExtension(const char* name) { - for (auto extension : enabled_extensions_) { - if (!std::strcmp(extension, name)) { - return true; - } - } - - return false; -} - -VkQueue VulkanDevice::AcquireQueue(uint32_t queue_family_index) { - std::lock_guard lock(queue_mutex_); - if (free_queues_[queue_family_index].empty()) { - return nullptr; - } - - auto queue = free_queues_[queue_family_index].back(); - free_queues_[queue_family_index].pop_back(); - return queue; -} - -void VulkanDevice::ReleaseQueue(VkQueue queue, uint32_t queue_family_index) { - std::lock_guard lock(queue_mutex_); - free_queues_[queue_family_index].push_back(queue); -} - -void VulkanDevice::DbgSetObjectName(uint64_t object, - VkDebugReportObjectTypeEXT object_type, - const std::string& name) const { - if (!debug_marker_ena_) { - // Extension disabled. - return; - } - - VkDebugMarkerObjectNameInfoEXT info; - info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT; - info.pNext = nullptr; - info.objectType = object_type; - info.object = object; - info.pObjectName = name.c_str(); - dfn_.vkDebugMarkerSetObjectNameEXT(*this, &info); -} - -void VulkanDevice::DbgMarkerBegin(VkCommandBuffer command_buffer, - std::string name, float r, float g, float b, - float a) const { - if (!debug_marker_ena_) { - // Extension disabled. - return; - } - - VkDebugMarkerMarkerInfoEXT info; - info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; - info.pNext = nullptr; - info.pMarkerName = name.c_str(); - info.color[0] = r; - info.color[1] = g; - info.color[2] = b; - info.color[3] = a; - dfn_.vkCmdDebugMarkerBeginEXT(command_buffer, &info); -} - -void VulkanDevice::DbgMarkerEnd(VkCommandBuffer command_buffer) const { - if (!debug_marker_ena_) { - // Extension disabled. - return; - } - - dfn_.vkCmdDebugMarkerEndEXT(command_buffer); -} - -void VulkanDevice::DbgMarkerInsert(VkCommandBuffer command_buffer, - std::string name, float r, float g, float b, - float a) const { - if (!debug_marker_ena_) { - // Extension disabled. - return; - } - - VkDebugMarkerMarkerInfoEXT info; - info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; - info.pNext = nullptr; - info.pMarkerName = name.c_str(); - info.color[0] = r; - info.color[1] = g; - info.color[2] = g; - info.color[3] = b; - dfn_.vkCmdDebugMarkerInsertEXT(command_buffer, &info); -} - -bool VulkanDevice::is_renderdoc_attached() const { - return instance_->is_renderdoc_attached(); -} - -void VulkanDevice::BeginRenderDocFrameCapture() { - auto api = reinterpret_cast(instance_->renderdoc_api()); - if (!api) { - return; - } - assert_true(api->IsFrameCapturing() == 0); - - api->StartFrameCapture(nullptr, nullptr); -} - -void VulkanDevice::EndRenderDocFrameCapture() { - auto api = reinterpret_cast(instance_->renderdoc_api()); - if (!api) { - return; - } - assert_true(api->IsFrameCapturing() == 1); - - api->EndFrameCapture(nullptr, nullptr); -} - -VkDeviceMemory VulkanDevice::AllocateMemory( - const VkMemoryRequirements& requirements, - VkFlags required_properties) const { - // Search memory types to find one matching our requirements and our - // properties. - uint32_t type_index = UINT_MAX; - for (uint32_t i = 0; i < device_info_.memory_properties.memoryTypeCount; - ++i) { - const auto& memory_type = device_info_.memory_properties.memoryTypes[i]; - if (((requirements.memoryTypeBits >> i) & 1) == 1) { - // Type is available for use; check for a match on properties. - if ((memory_type.propertyFlags & required_properties) == - required_properties) { - type_index = i; - break; - } - } - } - if (type_index == UINT_MAX) { - XELOGE("Unable to find a matching memory type"); - return nullptr; - } - - // Allocate the memory. - VkMemoryAllocateInfo memory_info; - memory_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memory_info.pNext = nullptr; - memory_info.allocationSize = requirements.size; - memory_info.memoryTypeIndex = type_index; - VkDeviceMemory memory = nullptr; - auto err = dfn_.vkAllocateMemory(handle, &memory_info, nullptr, &memory); - CheckResult(err, "vkAllocateMemory"); - return memory; -} - -} // namespace vulkan -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_device.h b/src/xenia/ui/vulkan/vulkan_device.h deleted file mode 100644 index 30af10040..000000000 --- a/src/xenia/ui/vulkan/vulkan_device.h +++ /dev/null @@ -1,145 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_VULKAN_VULKAN_DEVICE_H_ -#define XENIA_UI_VULKAN_VULKAN_DEVICE_H_ - -#include -#include -#include -#include - -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_util.h" - -namespace xe { -namespace ui { -namespace vulkan { - -class VulkanInstance; - -// Wrapper and utilities for VkDevice. -// Prefer passing this around over a VkDevice and casting as needed to call -// APIs. -class VulkanDevice { - public: - VulkanDevice(VulkanInstance* instance); - ~VulkanDevice(); - - VulkanInstance* instance() const { return instance_; } - - VkDevice handle = nullptr; - - operator VkDevice() const { return handle; } - operator VkPhysicalDevice() const { return device_info_.handle; } - - struct DeviceFunctions { -#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name; -#include "xenia/ui/vulkan/functions/device_1_0.inc" -#include "xenia/ui/vulkan/functions/device_amd_shader_info.inc" -#include "xenia/ui/vulkan/functions/device_ext_debug_marker.inc" -#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc" -#undef XE_UI_VULKAN_FUNCTION - }; - const DeviceFunctions& dfn() const { return dfn_; } - - // Declares a layer to verify and enable upon initialization. - // Must be called before Initialize. - void DeclareRequiredLayer(std::string name, uint32_t min_version, - bool is_optional) { - required_layers_.push_back({name, min_version, is_optional}); - } - - // Declares an extension to verify and enable upon initialization. - // Must be called before Initialize. - void DeclareRequiredExtension(std::string name, uint32_t min_version, - bool is_optional) { - required_extensions_.push_back({name, min_version, is_optional}); - } - - // Initializes the device, querying and enabling extensions and layers and - // preparing the device for general use. - // If initialization succeeds it's likely that no more failures beyond runtime - // issues will occur. - bool Initialize(DeviceInfo device_info); - - bool HasEnabledExtension(const char* name); - - uint32_t queue_family_index() const { return queue_family_index_; } - std::mutex& primary_queue_mutex() { return queue_mutex_; } - // Access to the primary queue must be synchronized with primary_queue_mutex. - VkQueue primary_queue() const { return primary_queue_; } - const DeviceInfo& device_info() const { return device_info_; } - - // Acquires a queue for exclusive use by the caller. - // The queue will not be touched by any other code until it's returned with - // ReleaseQueue. - // Not all devices support queues or only support a limited number. If this - // returns null the primary_queue should be used with the - // primary_queue_mutex. - // This method is thread safe. - VkQueue AcquireQueue(uint32_t queue_family_index); - // Releases a queue back to the device pool. - // This method is thread safe. - void ReleaseQueue(VkQueue queue, uint32_t queue_family_index); - - void DbgSetObjectName(uint64_t object, VkDebugReportObjectTypeEXT object_type, - const std::string& name) const; - - void DbgMarkerBegin(VkCommandBuffer command_buffer, std::string name, - float r = 0.0f, float g = 0.0f, float b = 0.0f, - float a = 0.0f) const; - void DbgMarkerEnd(VkCommandBuffer command_buffer) const; - - void DbgMarkerInsert(VkCommandBuffer command_buffer, std::string name, - float r = 0.0f, float g = 0.0f, float b = 0.0f, - float a = 0.0f) const; - - // True if RenderDoc is attached and available for use. - bool is_renderdoc_attached() const; - // Begins capturing the current frame in RenderDoc, if it is attached. - // Must be paired with EndRenderDocCapture. Multiple frames cannot be - // captured at the same time. - void BeginRenderDocFrameCapture(); - // Ends a capture. - void EndRenderDocFrameCapture(); - - // Allocates memory of the given size matching the required properties. - VkDeviceMemory AllocateMemory( - const VkMemoryRequirements& requirements, - VkFlags required_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) const; - - private: - VulkanInstance* instance_ = nullptr; - - std::vector required_layers_; - std::vector required_extensions_; - std::vector enabled_extensions_; - - DeviceFunctions dfn_ = {}; - - bool debug_marker_ena_ = false; - PFN_vkDebugMarkerSetObjectNameEXT pfn_vkDebugMarkerSetObjectNameEXT_ = - nullptr; - PFN_vkCmdDebugMarkerBeginEXT pfn_vkCmdDebugMarkerBeginEXT_ = nullptr; - PFN_vkCmdDebugMarkerEndEXT pfn_vkCmdDebugMarkerEndEXT_ = nullptr; - PFN_vkCmdDebugMarkerInsertEXT pfn_vkCmdDebugMarkerInsertEXT_ = nullptr; - - DeviceInfo device_info_; - uint32_t queue_family_index_ = 0; - std::mutex queue_mutex_; - VkQueue primary_queue_ = nullptr; - std::vector> free_queues_; -}; - -} // namespace vulkan -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_VULKAN_VULKAN_DEVICE_H_ diff --git a/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc b/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc index 6d95143ba..7d2753a28 100644 --- a/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc +++ b/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc @@ -2,920 +2,1065 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2020 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/ui/vulkan/vulkan_immediate_drawer.h" +#include +#include +#include +#include + #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" -#include "xenia/ui/graphics_context.h" -#include "xenia/ui/vulkan/vulkan_context.h" -#include "xenia/ui/vulkan/vulkan_device.h" -#include "xenia/ui/vulkan/vulkan_swap_chain.h" +#include "xenia/ui/vulkan/vulkan_presenter.h" +#include "xenia/ui/vulkan/vulkan_util.h" namespace xe { namespace ui { namespace vulkan { -// Generated with `xenia-build genspirv`. -#include "xenia/ui/vulkan/shaders/bin/immediate_frag.h" -#include "xenia/ui/vulkan/shaders/bin/immediate_vert.h" +// Generated with `xb buildshaders`. +#include "xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.h" -constexpr uint32_t kCircularBufferCapacity = 2 * 1024 * 1024; - -class LightweightCircularBuffer { - public: - LightweightCircularBuffer(const VulkanDevice* device) : device_(*device) { - buffer_capacity_ = xe::round_up(kCircularBufferCapacity, 4096); - - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); - - // Index buffer. - VkBufferCreateInfo index_buffer_info; - index_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - index_buffer_info.pNext = nullptr; - index_buffer_info.flags = 0; - index_buffer_info.size = buffer_capacity_; - index_buffer_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - index_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - index_buffer_info.queueFamilyIndexCount = 0; - index_buffer_info.pQueueFamilyIndices = nullptr; - auto status = dfn.vkCreateBuffer(device_, &index_buffer_info, nullptr, - &index_buffer_); - CheckResult(status, "vkCreateBuffer"); - - // Vertex buffer. - VkBufferCreateInfo vertex_buffer_info; - vertex_buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - vertex_buffer_info.pNext = nullptr; - vertex_buffer_info.flags = 0; - vertex_buffer_info.size = buffer_capacity_; - vertex_buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - vertex_buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - vertex_buffer_info.queueFamilyIndexCount = 0; - vertex_buffer_info.pQueueFamilyIndices = nullptr; - status = dfn.vkCreateBuffer(*device, &vertex_buffer_info, nullptr, - &vertex_buffer_); - CheckResult(status, "vkCreateBuffer"); - - // Allocate underlying buffer. - // We alias it for both vertices and indices. - VkMemoryRequirements buffer_requirements; - dfn.vkGetBufferMemoryRequirements(device_, index_buffer_, - &buffer_requirements); - buffer_memory_ = device->AllocateMemory( - buffer_requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - dfn.vkBindBufferMemory(*device, index_buffer_, buffer_memory_, 0); - dfn.vkBindBufferMemory(*device, vertex_buffer_, buffer_memory_, 0); - - // Persistent mapping. - status = dfn.vkMapMemory(device_, buffer_memory_, 0, VK_WHOLE_SIZE, 0, - &buffer_data_); - CheckResult(status, "vkMapMemory"); +VulkanImmediateDrawer::VulkanImmediateTexture::~VulkanImmediateTexture() { + if (immediate_drawer_) { + immediate_drawer_->OnImmediateTextureDestroyed(*this); } - - ~LightweightCircularBuffer() { - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - - if (buffer_memory_) { - dfn.vkUnmapMemory(device_, buffer_memory_); - buffer_memory_ = nullptr; - } - - DestroyAndNullHandle(dfn.vkDestroyBuffer, device_, index_buffer_); - DestroyAndNullHandle(dfn.vkDestroyBuffer, device_, vertex_buffer_); - DestroyAndNullHandle(dfn.vkFreeMemory, device_, buffer_memory_); - } - - VkBuffer vertex_buffer() const { return vertex_buffer_; } - VkBuffer index_buffer() const { return index_buffer_; } - - // Allocates space for data and copies it into the buffer. - // Returns the offset in the buffer of the data or VK_WHOLE_SIZE if the buffer - // is full. - VkDeviceSize Emplace(const void* source_data, size_t source_length) { - // TODO(benvanik): query actual alignment. - source_length = xe::round_up(source_length, 256); - - // Run down old fences to free up space. - - // Check to see if we have space. - // return VK_WHOLE_SIZE; - - // Compute new range and mark as in use. - if (current_offset_ + source_length > buffer_capacity_) { - // Wraps around. - current_offset_ = 0; - } - VkDeviceSize offset = current_offset_; - current_offset_ += source_length; - - // Copy data. - auto dest_ptr = reinterpret_cast(buffer_data_) + offset; - std::memcpy(dest_ptr, source_data, source_length); - - // Insert fence. - // TODO(benvanik): coarse-grained fences, these may be too fine. - - // Flush memory. - // TODO(benvanik): do only in large batches? can barrier it. - const VulkanDevice::DeviceFunctions& dfn = device_.dfn(); - VkMappedMemoryRange dirty_range; - dirty_range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - dirty_range.pNext = nullptr; - dirty_range.memory = buffer_memory_; - dirty_range.offset = offset; - dirty_range.size = source_length; - dfn.vkFlushMappedMemoryRanges(device_, 1, &dirty_range); - return offset; - } - - private: - const VulkanDevice& device_; - - VkBuffer index_buffer_ = nullptr; - VkBuffer vertex_buffer_ = nullptr; - VkDeviceMemory buffer_memory_ = nullptr; - void* buffer_data_ = nullptr; - size_t buffer_capacity_ = 0; - size_t current_offset_ = 0; -}; - -class VulkanImmediateTexture : public ImmediateTexture { - public: - VulkanImmediateTexture(VulkanDevice* device, VkDescriptorPool descriptor_pool, - VkSampler sampler, uint32_t width, uint32_t height) - : ImmediateTexture(width, height), - device_(device), - descriptor_pool_(descriptor_pool), - sampler_(sampler) {} - - ~VulkanImmediateTexture() override { Shutdown(); } - - VkResult Initialize(VkDescriptorSetLayout descriptor_set_layout, - VkImageView image_view) { - image_view_ = image_view; - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - VkResult status; - - // Create descriptor set used just for this texture. - // It never changes, so we can reuse it and not worry with updates. - VkDescriptorSetAllocateInfo set_alloc_info; - set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - set_alloc_info.pNext = nullptr; - set_alloc_info.descriptorPool = descriptor_pool_; - set_alloc_info.descriptorSetCount = 1; - set_alloc_info.pSetLayouts = &descriptor_set_layout; - status = dfn.vkAllocateDescriptorSets(*device_, &set_alloc_info, - &descriptor_set_); - CheckResult(status, "vkAllocateDescriptorSets"); - if (status != VK_SUCCESS) { - return status; - } - - // Initialize descriptor with our texture. - VkDescriptorImageInfo texture_info; - texture_info.sampler = sampler_; - texture_info.imageView = image_view_; - texture_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - VkWriteDescriptorSet descriptor_write; - descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptor_write.pNext = nullptr; - descriptor_write.dstSet = descriptor_set_; - descriptor_write.dstBinding = 0; - descriptor_write.dstArrayElement = 0; - descriptor_write.descriptorCount = 1; - descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptor_write.pImageInfo = &texture_info; - dfn.vkUpdateDescriptorSets(*device_, 1, &descriptor_write, 0, nullptr); - - return VK_SUCCESS; - } - - VkResult Initialize(VkDescriptorSetLayout descriptor_set_layout) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - VkResult status; - - // Create image object. - VkImageCreateInfo image_info; - image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_info.pNext = nullptr; - image_info.flags = 0; - image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.format = VK_FORMAT_R8G8B8A8_UNORM; - image_info.extent = {width, height, 1}; - image_info.mipLevels = 1; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.tiling = VK_IMAGE_TILING_LINEAR; - image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_info.queueFamilyIndexCount = 0; - image_info.pQueueFamilyIndices = nullptr; - image_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; - status = dfn.vkCreateImage(*device_, &image_info, nullptr, &image_); - CheckResult(status, "vkCreateImage"); - if (status != VK_SUCCESS) { - return status; - } - - // Allocate memory for the image. - VkMemoryRequirements memory_requirements; - dfn.vkGetImageMemoryRequirements(*device_, image_, &memory_requirements); - device_memory_ = device_->AllocateMemory( - memory_requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - if (!device_memory_) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - // Bind memory and the image together. - status = dfn.vkBindImageMemory(*device_, image_, device_memory_, 0); - CheckResult(status, "vkBindImageMemory"); - if (status != VK_SUCCESS) { - return status; - } - - // Create image view used by the shader. - VkImageViewCreateInfo view_info; - view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_info.pNext = nullptr; - view_info.flags = 0; - view_info.image = image_; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = VK_FORMAT_R8G8B8A8_UNORM; - view_info.components = { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A, - }; - view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - status = dfn.vkCreateImageView(*device_, &view_info, nullptr, &image_view_); - CheckResult(status, "vkCreateImageView"); - if (status != VK_SUCCESS) { - return status; - } - - // Create descriptor set used just for this texture. - // It never changes, so we can reuse it and not worry with updates. - VkDescriptorSetAllocateInfo set_alloc_info; - set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - set_alloc_info.pNext = nullptr; - set_alloc_info.descriptorPool = descriptor_pool_; - set_alloc_info.descriptorSetCount = 1; - set_alloc_info.pSetLayouts = &descriptor_set_layout; - status = dfn.vkAllocateDescriptorSets(*device_, &set_alloc_info, - &descriptor_set_); - CheckResult(status, "vkAllocateDescriptorSets"); - if (status != VK_SUCCESS) { - return status; - } - - // Initialize descriptor with our texture. - VkDescriptorImageInfo texture_info; - texture_info.sampler = sampler_; - texture_info.imageView = image_view_; - texture_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - VkWriteDescriptorSet descriptor_write; - descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptor_write.pNext = nullptr; - descriptor_write.dstSet = descriptor_set_; - descriptor_write.dstBinding = 0; - descriptor_write.dstArrayElement = 0; - descriptor_write.descriptorCount = 1; - descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptor_write.pImageInfo = &texture_info; - dfn.vkUpdateDescriptorSets(*device_, 1, &descriptor_write, 0, nullptr); - - return VK_SUCCESS; - } - - void Shutdown() { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - - if (descriptor_set_) { - dfn.vkFreeDescriptorSets(*device_, descriptor_pool_, 1, &descriptor_set_); - descriptor_set_ = nullptr; - } - - DestroyAndNullHandle(dfn.vkDestroyImageView, *device_, image_view_); - DestroyAndNullHandle(dfn.vkDestroyImage, *device_, image_); - DestroyAndNullHandle(dfn.vkFreeMemory, *device_, device_memory_); - } - - VkResult Upload(const uint8_t* src_data) { - // TODO(benvanik): assert not in use? textures aren't dynamic right now. - - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - - // Get device image layout. - VkImageSubresource subresource; - subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - subresource.mipLevel = 0; - subresource.arrayLayer = 0; - VkSubresourceLayout layout; - dfn.vkGetImageSubresourceLayout(*device_, image_, &subresource, &layout); - - // Map memory for upload. - uint8_t* gpu_data = nullptr; - auto status = dfn.vkMapMemory(*device_, device_memory_, 0, layout.size, 0, - reinterpret_cast(&gpu_data)); - CheckResult(status, "vkMapMemory"); - - if (status == VK_SUCCESS) { - // Copy the entire texture, hoping its layout matches what we expect. - std::memcpy(gpu_data + layout.offset, src_data, layout.size); - - dfn.vkUnmapMemory(*device_, device_memory_); - } - - return status; - } - - // Queues a command to transition this texture to a new layout. This assumes - // the command buffer WILL be queued and executed by the device. - void TransitionLayout(VkCommandBuffer command_buffer, - VkImageLayout new_layout) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - - VkImageMemoryBarrier image_barrier; - image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_barrier.pNext = nullptr; - image_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_barrier.srcAccessMask = 0; - image_barrier.dstAccessMask = 0; - image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - image_barrier.newLayout = new_layout; - image_barrier.image = image_; - image_barrier.subresourceRange = {0, 0, 1, 0, 1}; - image_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - image_layout_ = new_layout; - - dfn.vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, - 0, nullptr, 1, &image_barrier); - } - - VkDescriptorSet descriptor_set() const { return descriptor_set_; } - VkImageLayout layout() const { return image_layout_; } - - private: - VulkanDevice* device_ = nullptr; - VkDescriptorPool descriptor_pool_ = nullptr; - VkSampler sampler_ = nullptr; // Not owned. - VkImage image_ = nullptr; - VkImageLayout image_layout_ = VK_IMAGE_LAYOUT_PREINITIALIZED; - VkDeviceMemory device_memory_ = nullptr; - VkImageView image_view_ = nullptr; - VkDescriptorSet descriptor_set_ = nullptr; -}; - -VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext* graphics_context) - : ImmediateDrawer(graphics_context), context_(graphics_context) {} - -VulkanImmediateDrawer::~VulkanImmediateDrawer() { Shutdown(); } - -VkResult VulkanImmediateDrawer::Initialize() { - const VulkanDevice* device = context_->device(); - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); - - // NEAREST + CLAMP - VkSamplerCreateInfo sampler_info; - sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - sampler_info.pNext = nullptr; - sampler_info.flags = 0; - sampler_info.magFilter = VK_FILTER_NEAREST; - sampler_info.minFilter = VK_FILTER_NEAREST; - sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; - sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler_info.mipLodBias = 0.0f; - sampler_info.anisotropyEnable = VK_FALSE; - sampler_info.maxAnisotropy = 1.0f; - sampler_info.compareEnable = VK_FALSE; - sampler_info.compareOp = VK_COMPARE_OP_NEVER; - sampler_info.minLod = 0.0f; - sampler_info.maxLod = 0.0f; - sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - sampler_info.unnormalizedCoordinates = VK_FALSE; - auto status = dfn.vkCreateSampler(*device, &sampler_info, nullptr, - &samplers_.nearest_clamp); - CheckResult(status, "vkCreateSampler"); - if (status != VK_SUCCESS) { - return status; - } - - // NEAREST + REPEAT - sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - status = dfn.vkCreateSampler(*device, &sampler_info, nullptr, - &samplers_.nearest_repeat); - CheckResult(status, "vkCreateSampler"); - if (status != VK_SUCCESS) { - return status; - } - - // LINEAR + CLAMP - sampler_info.magFilter = VK_FILTER_LINEAR; - sampler_info.minFilter = VK_FILTER_LINEAR; - sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - status = dfn.vkCreateSampler(*device, &sampler_info, nullptr, - &samplers_.linear_clamp); - CheckResult(status, "vkCreateSampler"); - if (status != VK_SUCCESS) { - return status; - } - - // LINEAR + REPEAT - sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - status = dfn.vkCreateSampler(*device, &sampler_info, nullptr, - &samplers_.linear_repeat); - CheckResult(status, "vkCreateSampler"); - if (status != VK_SUCCESS) { - return status; - } - - // Create the descriptor set layout used for our texture sampler. - // As it changes almost every draw we keep it separate from the uniform buffer - // and cache it on the textures. - VkDescriptorSetLayoutCreateInfo texture_set_layout_info; - texture_set_layout_info.sType = - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - texture_set_layout_info.pNext = nullptr; - texture_set_layout_info.flags = 0; - texture_set_layout_info.bindingCount = 1; - VkDescriptorSetLayoutBinding texture_binding; - texture_binding.binding = 0; - texture_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - texture_binding.descriptorCount = 1; - texture_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - texture_binding.pImmutableSamplers = nullptr; - texture_set_layout_info.pBindings = &texture_binding; - status = dfn.vkCreateDescriptorSetLayout(*device, &texture_set_layout_info, - nullptr, &texture_set_layout_); - CheckResult(status, "vkCreateDescriptorSetLayout"); - if (status != VK_SUCCESS) { - return status; - } - - // Descriptor pool used for all of our cached descriptors. - // In the steady state we don't allocate anything, so these are all manually - // managed. - VkDescriptorPoolCreateInfo descriptor_pool_info; - descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - descriptor_pool_info.pNext = nullptr; - descriptor_pool_info.flags = - VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - descriptor_pool_info.maxSets = 128; - VkDescriptorPoolSize pool_sizes[1]; - pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - pool_sizes[0].descriptorCount = 128; - descriptor_pool_info.poolSizeCount = 1; - descriptor_pool_info.pPoolSizes = pool_sizes; - status = dfn.vkCreateDescriptorPool(*device, &descriptor_pool_info, nullptr, - &descriptor_pool_); - CheckResult(status, "vkCreateDescriptorPool"); - if (status != VK_SUCCESS) { - return status; - } - - // Create the pipeline layout used for our pipeline. - // If we had multiple pipelines they would share this. - VkPipelineLayoutCreateInfo pipeline_layout_info; - pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipeline_layout_info.pNext = nullptr; - pipeline_layout_info.flags = 0; - VkDescriptorSetLayout set_layouts[] = {texture_set_layout_}; - pipeline_layout_info.setLayoutCount = - static_cast(xe::countof(set_layouts)); - pipeline_layout_info.pSetLayouts = set_layouts; - VkPushConstantRange push_constant_ranges[2]; - push_constant_ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - push_constant_ranges[0].offset = 0; - push_constant_ranges[0].size = sizeof(float) * 16; - push_constant_ranges[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - push_constant_ranges[1].offset = sizeof(float) * 16; - push_constant_ranges[1].size = sizeof(int); - pipeline_layout_info.pushConstantRangeCount = - static_cast(xe::countof(push_constant_ranges)); - pipeline_layout_info.pPushConstantRanges = push_constant_ranges; - status = dfn.vkCreatePipelineLayout(*device, &pipeline_layout_info, nullptr, - &pipeline_layout_); - CheckResult(status, "vkCreatePipelineLayout"); - if (status != VK_SUCCESS) { - return status; - } - - // Vertex and fragment shaders. - VkShaderModuleCreateInfo vertex_shader_info; - vertex_shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - vertex_shader_info.pNext = nullptr; - vertex_shader_info.flags = 0; - vertex_shader_info.codeSize = sizeof(immediate_vert); - vertex_shader_info.pCode = reinterpret_cast(immediate_vert); - VkShaderModule vertex_shader; - status = dfn.vkCreateShaderModule(*device, &vertex_shader_info, nullptr, - &vertex_shader); - CheckResult(status, "vkCreateShaderModule"); - VkShaderModuleCreateInfo fragment_shader_info; - fragment_shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - fragment_shader_info.pNext = nullptr; - fragment_shader_info.flags = 0; - fragment_shader_info.codeSize = sizeof(immediate_frag); - fragment_shader_info.pCode = - reinterpret_cast(immediate_frag); - VkShaderModule fragment_shader; - status = dfn.vkCreateShaderModule(*device, &fragment_shader_info, nullptr, - &fragment_shader); - CheckResult(status, "vkCreateShaderModule"); - - // Pipeline used when rendering triangles. - VkGraphicsPipelineCreateInfo pipeline_info; - pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipeline_info.pNext = nullptr; - pipeline_info.flags = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT; - VkPipelineShaderStageCreateInfo pipeline_stages[2]; - pipeline_stages[0].sType = - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - pipeline_stages[0].pNext = nullptr; - pipeline_stages[0].flags = 0; - pipeline_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - pipeline_stages[0].module = vertex_shader; - pipeline_stages[0].pName = "main"; - pipeline_stages[0].pSpecializationInfo = nullptr; - pipeline_stages[1].sType = - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - pipeline_stages[1].pNext = nullptr; - pipeline_stages[1].flags = 0; - pipeline_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - pipeline_stages[1].module = fragment_shader; - pipeline_stages[1].pName = "main"; - pipeline_stages[1].pSpecializationInfo = nullptr; - pipeline_info.stageCount = 2; - pipeline_info.pStages = pipeline_stages; - VkPipelineVertexInputStateCreateInfo vertex_state_info; - vertex_state_info.sType = - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertex_state_info.pNext = nullptr; - vertex_state_info.flags = 0; - VkVertexInputBindingDescription vertex_binding_descrs[1]; - vertex_binding_descrs[0].binding = 0; - vertex_binding_descrs[0].stride = sizeof(ImmediateVertex); - vertex_binding_descrs[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - vertex_state_info.vertexBindingDescriptionCount = - static_cast(xe::countof(vertex_binding_descrs)); - vertex_state_info.pVertexBindingDescriptions = vertex_binding_descrs; - VkVertexInputAttributeDescription vertex_attrib_descrs[3]; - vertex_attrib_descrs[0].location = 0; - vertex_attrib_descrs[0].binding = 0; - vertex_attrib_descrs[0].format = VK_FORMAT_R32G32_SFLOAT; - vertex_attrib_descrs[0].offset = offsetof(ImmediateVertex, x); - vertex_attrib_descrs[1].location = 1; - vertex_attrib_descrs[1].binding = 0; - vertex_attrib_descrs[1].format = VK_FORMAT_R32G32_SFLOAT; - vertex_attrib_descrs[1].offset = offsetof(ImmediateVertex, u); - vertex_attrib_descrs[2].location = 2; - vertex_attrib_descrs[2].binding = 0; - vertex_attrib_descrs[2].format = VK_FORMAT_R8G8B8A8_UNORM; - vertex_attrib_descrs[2].offset = offsetof(ImmediateVertex, color); - vertex_state_info.vertexAttributeDescriptionCount = - static_cast(xe::countof(vertex_attrib_descrs)); - vertex_state_info.pVertexAttributeDescriptions = vertex_attrib_descrs; - pipeline_info.pVertexInputState = &vertex_state_info; - VkPipelineInputAssemblyStateCreateInfo input_info; - input_info.sType = - VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - input_info.pNext = nullptr; - input_info.flags = 0; - input_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - input_info.primitiveRestartEnable = VK_FALSE; - pipeline_info.pInputAssemblyState = &input_info; - pipeline_info.pTessellationState = nullptr; - VkPipelineViewportStateCreateInfo viewport_state_info; - viewport_state_info.sType = - VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewport_state_info.pNext = nullptr; - viewport_state_info.flags = 0; - viewport_state_info.viewportCount = 1; - viewport_state_info.pViewports = nullptr; - viewport_state_info.scissorCount = 1; - viewport_state_info.pScissors = nullptr; - pipeline_info.pViewportState = &viewport_state_info; - VkPipelineRasterizationStateCreateInfo rasterization_info; - rasterization_info.sType = - VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterization_info.pNext = nullptr; - rasterization_info.flags = 0; - rasterization_info.depthClampEnable = VK_FALSE; - rasterization_info.rasterizerDiscardEnable = VK_FALSE; - rasterization_info.polygonMode = VK_POLYGON_MODE_FILL; - rasterization_info.cullMode = VK_CULL_MODE_NONE; - rasterization_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterization_info.depthBiasEnable = VK_FALSE; - rasterization_info.depthBiasConstantFactor = 0; - rasterization_info.depthBiasClamp = 0; - rasterization_info.depthBiasSlopeFactor = 0; - rasterization_info.lineWidth = 1.0f; - pipeline_info.pRasterizationState = &rasterization_info; - VkPipelineMultisampleStateCreateInfo multisample_info; - multisample_info.sType = - VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisample_info.pNext = nullptr; - multisample_info.flags = 0; - multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisample_info.sampleShadingEnable = VK_FALSE; - multisample_info.minSampleShading = 0; - multisample_info.pSampleMask = nullptr; - multisample_info.alphaToCoverageEnable = VK_FALSE; - multisample_info.alphaToOneEnable = VK_FALSE; - pipeline_info.pMultisampleState = &multisample_info; - pipeline_info.pDepthStencilState = nullptr; - VkPipelineColorBlendStateCreateInfo blend_info; - blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - blend_info.pNext = nullptr; - blend_info.flags = 0; - blend_info.logicOpEnable = VK_FALSE; - blend_info.logicOp = VK_LOGIC_OP_NO_OP; - VkPipelineColorBlendAttachmentState blend_attachments[1]; - blend_attachments[0].blendEnable = VK_TRUE; - blend_attachments[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - blend_attachments[0].dstColorBlendFactor = - VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - blend_attachments[0].colorBlendOp = VK_BLEND_OP_ADD; - blend_attachments[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - blend_attachments[0].dstAlphaBlendFactor = - VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - blend_attachments[0].alphaBlendOp = VK_BLEND_OP_ADD; - blend_attachments[0].colorWriteMask = 0xF; - blend_info.attachmentCount = - static_cast(xe::countof(blend_attachments)); - blend_info.pAttachments = blend_attachments; - std::memset(blend_info.blendConstants, 0, sizeof(blend_info.blendConstants)); - pipeline_info.pColorBlendState = &blend_info; - VkPipelineDynamicStateCreateInfo dynamic_state_info; - dynamic_state_info.sType = - VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_state_info.pNext = nullptr; - dynamic_state_info.flags = 0; - VkDynamicState dynamic_states[] = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, - }; - dynamic_state_info.dynamicStateCount = - static_cast(xe::countof(dynamic_states)); - dynamic_state_info.pDynamicStates = dynamic_states; - pipeline_info.pDynamicState = &dynamic_state_info; - pipeline_info.layout = pipeline_layout_; - pipeline_info.renderPass = context_->swap_chain()->render_pass(); - pipeline_info.subpass = 0; - pipeline_info.basePipelineHandle = nullptr; - pipeline_info.basePipelineIndex = -1; - if (status == VK_SUCCESS) { - status = dfn.vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info, - nullptr, &triangle_pipeline_); - CheckResult(status, "vkCreateGraphicsPipelines"); - } - - // Silly, but let's make a pipeline just for drawing lines. - pipeline_info.flags = VK_PIPELINE_CREATE_DERIVATIVE_BIT; - input_info.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - pipeline_info.basePipelineHandle = triangle_pipeline_; - pipeline_info.basePipelineIndex = -1; - if (status == VK_SUCCESS) { - status = dfn.vkCreateGraphicsPipelines(*device, nullptr, 1, &pipeline_info, - nullptr, &line_pipeline_); - CheckResult(status, "vkCreateGraphicsPipelines"); - } - - DestroyAndNullHandle(dfn.vkDestroyShaderModule, *device, vertex_shader); - DestroyAndNullHandle(dfn.vkDestroyShaderModule, *device, fragment_shader); - - // Allocate the buffer we'll use for our vertex and index data. - circular_buffer_ = std::make_unique(device); - - return status; } -void VulkanImmediateDrawer::Shutdown() { - const VulkanDevice* device = context_->device(); - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); +VulkanImmediateDrawer::~VulkanImmediateDrawer() { + // Await GPU usage completion of all draws and texture uploads (which happen + // before draws). + auto vulkan_presenter = static_cast(presenter()); + if (vulkan_presenter) { + vulkan_presenter->AwaitUISubmissionCompletionFromUIThread( + last_paint_submission_index_); + } - circular_buffer_.reset(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); - DestroyAndNullHandle(dfn.vkDestroyPipeline, *device, line_pipeline_); - DestroyAndNullHandle(dfn.vkDestroyPipeline, *device, triangle_pipeline_); - DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, *device, pipeline_layout_); + util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, pipeline_line_); + util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, pipeline_triangle_); - DestroyAndNullHandle(dfn.vkDestroyDescriptorPool, *device, descriptor_pool_); - DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, *device, - texture_set_layout_); + util::DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, device, + pipeline_layout_); - DestroyAndNullHandle(dfn.vkDestroySampler, *device, samplers_.nearest_clamp); - DestroyAndNullHandle(dfn.vkDestroySampler, *device, samplers_.nearest_repeat); - DestroyAndNullHandle(dfn.vkDestroySampler, *device, samplers_.linear_clamp); - DestroyAndNullHandle(dfn.vkDestroySampler, *device, samplers_.linear_repeat); + for (auto& deleted_texture : textures_deleted_) { + DestroyTextureResource(deleted_texture.first); + } + for (SubmittedTextureUploadBuffer& submitted_texture_upload_buffer : + texture_upload_buffers_submitted_) { + dfn.vkDestroyBuffer(device, submitted_texture_upload_buffer.buffer, + nullptr); + dfn.vkFreeMemory(device, submitted_texture_upload_buffer.buffer_memory, + nullptr); + } + for (PendingTextureUpload& pending_texture_upload : + texture_uploads_pending_) { + dfn.vkDestroyBuffer(device, pending_texture_upload.buffer, nullptr); + dfn.vkFreeMemory(device, pending_texture_upload.buffer_memory, nullptr); + } + for (VulkanImmediateTexture* texture : textures_) { + if (texture->immediate_drawer_ != this) { + continue; + } + texture->immediate_drawer_ = nullptr; + DestroyTextureResource(texture->resource_); + } + if (white_texture_.image != VK_NULL_HANDLE) { + DestroyTextureResource(white_texture_); + } + + for (TextureDescriptorPool* pool : texture_descriptor_pools_) { + dfn.vkDestroyDescriptorPool(device, pool->pool, nullptr); + delete pool; + } + util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, device, + texture_descriptor_set_layout_); +} + +bool VulkanImmediateDrawer::Initialize() { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkDescriptorSetLayoutBinding texture_descriptor_set_layout_binding; + texture_descriptor_set_layout_binding.binding = 0; + texture_descriptor_set_layout_binding.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + texture_descriptor_set_layout_binding.descriptorCount = 1; + texture_descriptor_set_layout_binding.stageFlags = + VK_SHADER_STAGE_FRAGMENT_BIT; + texture_descriptor_set_layout_binding.pImmutableSamplers = nullptr; + VkDescriptorSetLayoutCreateInfo texture_descriptor_set_layout_create_info; + texture_descriptor_set_layout_create_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + texture_descriptor_set_layout_create_info.pNext = nullptr; + texture_descriptor_set_layout_create_info.flags = 0; + texture_descriptor_set_layout_create_info.bindingCount = 1; + texture_descriptor_set_layout_create_info.pBindings = + &texture_descriptor_set_layout_binding; + if (dfn.vkCreateDescriptorSetLayout( + device, &texture_descriptor_set_layout_create_info, nullptr, + &texture_descriptor_set_layout_) != VK_SUCCESS) { + XELOGE( + "VulkanImmediateDrawer: Failed to create the combined image sampler " + "descriptor set layout"); + return false; + } + + // Create the (1, 1, 1, 1) texture as a replacement when drawing without a + // real texture. + size_t white_texture_pending_upload_index; + if (!CreateTextureResource(1, 1, ImmediateTextureFilter::kNearest, false, + nullptr, white_texture_, + white_texture_pending_upload_index)) { + XELOGE("VulkanImmediateDrawer: Failed to create a blank texture"); + return false; + } + + vertex_buffer_pool_ = std::make_unique( + provider_, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + + VkPushConstantRange push_constant_ranges[1]; + push_constant_ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constant_ranges[0].offset = offsetof(PushConstants, vertex); + push_constant_ranges[0].size = sizeof(PushConstants::Vertex); + VkPipelineLayoutCreateInfo pipeline_layout_create_info; + pipeline_layout_create_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_create_info.pNext = nullptr; + pipeline_layout_create_info.flags = 0; + pipeline_layout_create_info.setLayoutCount = 1; + pipeline_layout_create_info.pSetLayouts = &texture_descriptor_set_layout_; + pipeline_layout_create_info.pushConstantRangeCount = + uint32_t(xe::countof(push_constant_ranges)); + pipeline_layout_create_info.pPushConstantRanges = push_constant_ranges; + if (dfn.vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, + &pipeline_layout_) != VK_SUCCESS) { + XELOGE("VulkanImmediateDrawer: Failed to create the pipeline layout"); + return false; + } + + // Reset the current state. + batch_open_ = false; + + return true; } std::unique_ptr VulkanImmediateDrawer::CreateTexture( - uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat, - const uint8_t* data) { - auto device = context_->device(); - - VkResult status; - VkSampler sampler = GetSampler(filter, repeat); - - auto texture = std::make_unique( - device, descriptor_pool_, sampler, width, height); - status = texture->Initialize(texture_set_layout_); - if (status != VK_SUCCESS) { - texture->Shutdown(); - return nullptr; + uint32_t width, uint32_t height, ImmediateTextureFilter filter, + bool is_repeated, const uint8_t* data) { + assert_not_null(data); + auto texture = std::make_unique(width, height); + size_t pending_upload_index; + if (CreateTextureResource(width, height, filter, is_repeated, data, + texture->resource_, pending_upload_index)) { + // Manage by this immediate drawer. + texture->immediate_drawer_ = this; + texture->immediate_drawer_index_ = textures_.size(); + textures_.push_back(texture.get()); + texture->pending_upload_index_ = pending_upload_index; + texture_uploads_pending_[texture->pending_upload_index_].texture = + texture.get(); } - - if (data) { - texture->Upload(data); - } - return std::unique_ptr(texture.release()); + return std::move(texture); } -std::unique_ptr VulkanImmediateDrawer::WrapTexture( - VkImageView image_view, VkSampler sampler, uint32_t width, - uint32_t height) { - VkResult status; +void VulkanImmediateDrawer::Begin(UIDrawContext& ui_draw_context, + float coordinate_space_width, + float coordinate_space_height) { + ImmediateDrawer::Begin(ui_draw_context, coordinate_space_width, + coordinate_space_height); - auto texture = std::make_unique( - context_->device(), descriptor_pool_, sampler, width, height); - status = texture->Initialize(texture_set_layout_, image_view); - if (status != VK_SUCCESS) { - texture->Shutdown(); - return nullptr; + assert_false(batch_open_); + + const VulkanUIDrawContext& vulkan_ui_draw_context = + static_cast(ui_draw_context); + + // Update the submission index to be used throughout the current immediate + // drawer paint. + last_paint_submission_index_ = + vulkan_ui_draw_context.submission_index_current(); + last_completed_submission_index_ = + vulkan_ui_draw_context.submission_index_completed(); + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + // Destroy deleted textures. + for (auto it = textures_deleted_.begin(); it != textures_deleted_.end();) { + if (it->second > last_completed_submission_index_) { + ++it; + continue; + } + DestroyTextureResource(it->first); + if (std::next(it) != textures_deleted_.end()) { + *it = textures_deleted_.back(); + } + textures_deleted_.pop_back(); } - return texture; -} + // Release upload buffers for completed texture uploads. + auto erase_texture_uploads_end = texture_upload_buffers_submitted_.begin(); + while (erase_texture_uploads_end != texture_upload_buffers_submitted_.end()) { + if (erase_texture_uploads_end->submission_index > + last_completed_submission_index_) { + break; + } + dfn.vkDestroyBuffer(device, erase_texture_uploads_end->buffer, nullptr); + dfn.vkFreeMemory(device, erase_texture_uploads_end->buffer_memory, nullptr); + ++erase_texture_uploads_end; + } + texture_upload_buffers_submitted_.erase( + texture_upload_buffers_submitted_.begin(), erase_texture_uploads_end); -void VulkanImmediateDrawer::Begin(int render_target_width, - int render_target_height) { - const VulkanDevice* device = context_->device(); - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); - auto swap_chain = context_->swap_chain(); - assert_null(current_cmd_buffer_); - current_cmd_buffer_ = swap_chain->render_cmd_buffer(); - current_render_target_width_ = render_target_width; - current_render_target_height_ = render_target_height; + vertex_buffer_pool_->Reclaim(last_completed_submission_index_); + + // Begin drawing. + + // Update the framebuffer attachment format in the pipelines - may await + // submission completion if changing. + if (!EnsurePipelinesCreatedForCurrentRenderPass()) { + // Failed to create the pipelines - don't draw anything. + return; + } + + VkCommandBuffer draw_command_buffer = + vulkan_ui_draw_context.draw_command_buffer(); - // Viewport changes only once per batch. VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; - viewport.width = static_cast(render_target_width); - viewport.height = static_cast(render_target_height); + viewport.width = float(vulkan_ui_draw_context.render_target_width()); + viewport.height = float(vulkan_ui_draw_context.render_target_height()); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; - dfn.vkCmdSetViewport(current_cmd_buffer_, 0, 1, &viewport); + dfn.vkCmdSetViewport(draw_command_buffer, 0, 1, &viewport); + PushConstants::Vertex push_constants_vertex; + push_constants_vertex.coordinate_space_size_inv[0] = + 1.0f / coordinate_space_width; + push_constants_vertex.coordinate_space_size_inv[1] = + 1.0f / coordinate_space_height; + dfn.vkCmdPushConstants(draw_command_buffer, pipeline_layout_, + VK_SHADER_STAGE_VERTEX_BIT, + offsetof(PushConstants, vertex), + sizeof(PushConstants::Vertex), &push_constants_vertex); + current_scissor_.offset.x = 0; + current_scissor_.offset.y = 0; + current_scissor_.extent.width = 0; + current_scissor_.extent.height = 0; - // Update projection matrix. - const float ortho_projection[4][4] = { - {2.0f / render_target_width, 0.0f, 0.0f, 0.0f}, - {0.0f, 2.0f / -render_target_height, 0.0f, 0.0f}, - {0.0f, 0.0f, -1.0f, 0.0f}, - {-1.0f, 1.0f, 0.0f, 1.0f}, - }; - dfn.vkCmdPushConstants(current_cmd_buffer_, pipeline_layout_, - VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 16, - ortho_projection); + current_pipeline_ = VK_NULL_HANDLE; + current_texture_descriptor_index_ = UINT32_MAX; } void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) { - const VulkanDevice* device = context_->device(); - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); + assert_false(batch_open_); - // Upload vertices. - VkDeviceSize vertices_offset = circular_buffer_->Emplace( - batch.vertices, batch.vertex_count * sizeof(ImmediateVertex)); - if (vertices_offset == VK_WHOLE_SIZE) { - // TODO(benvanik): die? + const VulkanUIDrawContext& vulkan_ui_draw_context = + *static_cast(ui_draw_context()); + VkCommandBuffer draw_command_buffer = + vulkan_ui_draw_context.draw_command_buffer(); + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + + // Bind the vertices. + size_t vertex_buffer_size = sizeof(ImmediateVertex) * batch.vertex_count; + VkBuffer vertex_buffer; + VkDeviceSize vertex_buffer_offset; + void* vertex_buffer_mapping = vertex_buffer_pool_->Request( + last_paint_submission_index_, vertex_buffer_size, sizeof(float), + vertex_buffer, vertex_buffer_offset); + if (!vertex_buffer_mapping) { + XELOGE("VulkanImmediateDrawer: Failed to get a buffer for {} vertices", + batch.vertex_count); return; } - auto vertex_buffer = circular_buffer_->vertex_buffer(); - dfn.vkCmdBindVertexBuffers(current_cmd_buffer_, 0, 1, &vertex_buffer, - &vertices_offset); + std::memcpy(vertex_buffer_mapping, batch.vertices, vertex_buffer_size); + dfn.vkCmdBindVertexBuffers(draw_command_buffer, 0, 1, &vertex_buffer, + &vertex_buffer_offset); - // Upload indices. - if (batch.indices) { - VkDeviceSize indices_offset = circular_buffer_->Emplace( - batch.indices, batch.index_count * sizeof(uint16_t)); - if (indices_offset == VK_WHOLE_SIZE) { - // TODO(benvanik): die? + // Bind the indices. + batch_has_index_buffer_ = batch.indices != nullptr; + if (batch_has_index_buffer_) { + size_t index_buffer_size = sizeof(uint16_t) * batch.index_count; + VkBuffer index_buffer; + VkDeviceSize index_buffer_offset; + void* index_buffer_mapping = vertex_buffer_pool_->Request( + last_paint_submission_index_, index_buffer_size, sizeof(uint16_t), + index_buffer, index_buffer_offset); + if (!index_buffer_mapping) { + XELOGE("VulkanImmediateDrawer: Failed to get a buffer for {} indices", + batch.index_count); return; } - dfn.vkCmdBindIndexBuffer(current_cmd_buffer_, - circular_buffer_->index_buffer(), indices_offset, - VK_INDEX_TYPE_UINT16); + std::memcpy(index_buffer_mapping, batch.indices, index_buffer_size); + dfn.vkCmdBindIndexBuffer(draw_command_buffer, index_buffer, + index_buffer_offset, VK_INDEX_TYPE_UINT16); } - batch_has_index_buffer_ = !!batch.indices; + batch_open_ = true; } void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) { - const VulkanDevice* device = context_->device(); - const VulkanDevice::DeviceFunctions& dfn = device->dfn(); + if (!batch_open_) { + // Could have been an error while obtaining the vertex and index buffers, + // for instance. + return; + } + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + const VulkanUIDrawContext& vulkan_ui_draw_context = + *static_cast(ui_draw_context()); + VkCommandBuffer draw_command_buffer = + vulkan_ui_draw_context.draw_command_buffer(); + + // Get the pipeline for the current primitive type. + VkPipeline pipeline; switch (draw.primitive_type) { case ImmediatePrimitiveType::kLines: - dfn.vkCmdBindPipeline(current_cmd_buffer_, - VK_PIPELINE_BIND_POINT_GRAPHICS, line_pipeline_); + pipeline = pipeline_line_; break; case ImmediatePrimitiveType::kTriangles: - dfn.vkCmdBindPipeline(current_cmd_buffer_, - VK_PIPELINE_BIND_POINT_GRAPHICS, - triangle_pipeline_); + pipeline = pipeline_triangle_; break; + default: + assert_unhandled_case(draw.primitive_type); + return; + } + if (pipeline == VK_NULL_HANDLE) { + // Failed to create the pipeline. + return; } - // Setup texture binding. - auto texture = static_cast(draw.texture); - if (texture) { - if (texture->layout() != VK_IMAGE_LAYOUT_GENERAL) { - texture->TransitionLayout(current_cmd_buffer_, VK_IMAGE_LAYOUT_GENERAL); - } - - auto texture_set = texture->descriptor_set(); - if (!texture_set) { - XELOGW("Failed to acquire texture descriptor set for immediate drawer!"); - } - - dfn.vkCmdBindDescriptorSets( - current_cmd_buffer_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_, - 0, 1, &texture_set, 0, nullptr); - } - - // Use push constants for our per-draw changes. - // Here, the restrict_texture_samples uniform (was used before September 26, - // 2020, now deleted). - int restrict_texture_samples = 0; - dfn.vkCmdPushConstants(current_cmd_buffer_, pipeline_layout_, - VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 16, - sizeof(int), &restrict_texture_samples); - - // Scissor, if enabled. - // Scissor can be disabled by making it the full screen. + // Set the scissor rectangle if enabled. VkRect2D scissor; - if (draw.scissor) { - scissor.offset.x = draw.scissor_rect[0]; - scissor.offset.y = current_render_target_height_ - - (draw.scissor_rect[1] + draw.scissor_rect[3]); - scissor.extent.width = draw.scissor_rect[2]; - scissor.extent.height = draw.scissor_rect[3]; - } else { - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent.width = current_render_target_width_; - scissor.extent.height = current_render_target_height_; + uint32_t scissor_left, scissor_top; + if (!ScissorToRenderTarget(draw, scissor_left, scissor_top, + scissor.extent.width, scissor.extent.height)) { + // Nothing is visible (zero area is used as the default current_scissor_ + // value also). + return; + } + scissor.offset.x = int32_t(scissor_left); + scissor.offset.y = int32_t(scissor_top); + if (current_scissor_.offset.x != scissor.offset.x || + current_scissor_.offset.y != scissor.offset.y || + current_scissor_.extent.width != scissor.extent.width || + current_scissor_.extent.height != scissor.extent.height) { + current_scissor_ = scissor; + dfn.vkCmdSetScissor(draw_command_buffer, 0, 1, &scissor); } - dfn.vkCmdSetScissor(current_cmd_buffer_, 0, 1, &scissor); - // Issue draw. + // Bind the pipeline for the primitive type if the scissor is not empty. + if (current_pipeline_ != pipeline) { + current_pipeline_ = pipeline; + dfn.vkCmdBindPipeline(draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeline); + } + + // Bind the texture. + uint32_t texture_descriptor_index; + VulkanImmediateTexture* texture = + static_cast(draw.texture); + if (texture && texture->immediate_drawer_ == this) { + texture_descriptor_index = texture->resource_.descriptor_index; + texture->last_usage_submission_ = last_paint_submission_index_; + } else { + texture_descriptor_index = white_texture_.descriptor_index; + } + if (current_texture_descriptor_index_ != texture_descriptor_index) { + current_texture_descriptor_index_ = texture_descriptor_index; + VkDescriptorSet texture_descriptor_set = + GetTextureDescriptor(texture_descriptor_index); + dfn.vkCmdBindDescriptorSets( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_, + 0, 1, &texture_descriptor_set, 0, nullptr); + } + + // Draw. if (batch_has_index_buffer_) { - dfn.vkCmdDrawIndexed(current_cmd_buffer_, draw.count, 1, draw.index_offset, + dfn.vkCmdDrawIndexed(draw_command_buffer, draw.count, 1, draw.index_offset, draw.base_vertex, 0); } else { - dfn.vkCmdDraw(current_cmd_buffer_, draw.count, 1, draw.base_vertex, 0); + dfn.vkCmdDraw(draw_command_buffer, draw.count, 1, draw.base_vertex, 0); } } -void VulkanImmediateDrawer::EndDrawBatch() {} +void VulkanImmediateDrawer::EndDrawBatch() { batch_open_ = false; } -void VulkanImmediateDrawer::End() { current_cmd_buffer_ = nullptr; } +void VulkanImmediateDrawer::End() { + assert_false(batch_open_); -VkSampler VulkanImmediateDrawer::GetSampler(ImmediateTextureFilter filter, - bool repeat) { - VkSampler sampler = nullptr; - switch (filter) { - case ImmediateTextureFilter::kNearest: - sampler = repeat ? samplers_.nearest_repeat : samplers_.nearest_clamp; - break; - case ImmediateTextureFilter::kLinear: - sampler = repeat ? samplers_.linear_repeat : samplers_.linear_clamp; - break; - default: - assert_unhandled_case(filter); - sampler = samplers_.nearest_clamp; - break; + // Upload textures. + if (!texture_uploads_pending_.empty()) { + VulkanPresenter& vulkan_presenter = + *static_cast(presenter()); + VkCommandBuffer setup_command_buffer = + vulkan_presenter.AcquireUISetupCommandBufferFromUIThread(); + if (setup_command_buffer != VK_NULL_HANDLE) { + size_t texture_uploads_pending_count = texture_uploads_pending_.size(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + const VulkanUIDrawContext& vulkan_ui_draw_context = + *static_cast(ui_draw_context()); + + // Transition to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. + std::vector image_memory_barriers; + image_memory_barriers.reserve(texture_uploads_pending_count); + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = nullptr; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + util::InitializeSubresourceRange(image_memory_barrier.subresourceRange); + for (const PendingTextureUpload& pending_texture_upload : + texture_uploads_pending_) { + image_memory_barriers.emplace_back(image_memory_barrier).image = + pending_texture_upload.image; + } + dfn.vkCmdPipelineBarrier( + setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, + uint32_t(image_memory_barriers.size()), image_memory_barriers.data()); + + // Do transfer operations and transition to + // VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, and also mark as used. + for (size_t i = 0; i < texture_uploads_pending_count; ++i) { + const PendingTextureUpload& pending_texture_upload = + texture_uploads_pending_[i]; + if (pending_texture_upload.buffer != VK_NULL_HANDLE) { + // Copying. + VkBufferImageCopy copy_region; + copy_region.bufferOffset = 0; + copy_region.bufferRowLength = pending_texture_upload.width; + copy_region.bufferImageHeight = pending_texture_upload.height; + copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.imageSubresource.mipLevel = 0; + copy_region.imageSubresource.baseArrayLayer = 0; + copy_region.imageSubresource.layerCount = 1; + copy_region.imageOffset.x = 0; + copy_region.imageOffset.y = 0; + copy_region.imageOffset.z = 0; + copy_region.imageExtent.width = pending_texture_upload.width; + copy_region.imageExtent.height = pending_texture_upload.height; + copy_region.imageExtent.depth = 1; + dfn.vkCmdCopyBufferToImage( + setup_command_buffer, pending_texture_upload.buffer, + pending_texture_upload.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + + SubmittedTextureUploadBuffer& submitted_texture_upload_buffer = + texture_upload_buffers_submitted_.emplace_back(); + submitted_texture_upload_buffer.buffer = + pending_texture_upload.buffer; + submitted_texture_upload_buffer.buffer_memory = + pending_texture_upload.buffer_memory; + submitted_texture_upload_buffer.submission_index = + last_paint_submission_index_; + } else { + // Clearing (initializing the special empty image). + VkClearColorValue white_clear_value; + white_clear_value.float32[0] = 1.0f; + white_clear_value.float32[1] = 1.0f; + white_clear_value.float32[2] = 1.0f; + white_clear_value.float32[3] = 1.0f; + dfn.vkCmdClearColorImage( + setup_command_buffer, pending_texture_upload.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &white_clear_value, 1, + &image_memory_barrier.subresourceRange); + } + + VkImageMemoryBarrier& image_memory_barrier_current = + image_memory_barriers[i]; + image_memory_barrier_current.srcAccessMask = + VK_ACCESS_TRANSFER_WRITE_BIT; + image_memory_barrier_current.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier_current.oldLayout = + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier_current.newLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + if (pending_texture_upload.texture) { + pending_texture_upload.texture->last_usage_submission_ = + last_paint_submission_index_; + pending_texture_upload.texture->pending_upload_index_ = SIZE_MAX; + } + } + dfn.vkCmdPipelineBarrier( + setup_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, + uint32_t(image_memory_barriers.size()), image_memory_barriers.data()); + + texture_uploads_pending_.clear(); + } } - return sampler; + vertex_buffer_pool_->FlushWrites(); + + ImmediateDrawer::End(); +} + +void VulkanImmediateDrawer::OnLeavePresenter() { + // Leaving the presenter's submission timeline - await GPU usage completion of + // all draws and texture uploads (which happen before draws) and reset + // submission indices. + VulkanPresenter& vulkan_presenter = + *static_cast(presenter()); + vulkan_presenter.AwaitUISubmissionCompletionFromUIThread( + last_paint_submission_index_); + + for (VulkanImmediateTexture* texture : textures_) { + texture->last_usage_submission_ = 0; + } + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + for (SubmittedTextureUploadBuffer& submitted_texture_upload_buffer : + texture_upload_buffers_submitted_) { + dfn.vkDestroyBuffer(device, submitted_texture_upload_buffer.buffer, + nullptr); + dfn.vkFreeMemory(device, submitted_texture_upload_buffer.buffer_memory, + nullptr); + } + + vertex_buffer_pool_->ChangeSubmissionTimeline(); + + last_paint_submission_index_ = 0; + last_completed_submission_index_ = 0; +} + +bool VulkanImmediateDrawer::EnsurePipelinesCreatedForCurrentRenderPass() { + const VulkanUIDrawContext& vulkan_ui_draw_context = + *static_cast(ui_draw_context()); + VkFormat render_pass_format = vulkan_ui_draw_context.render_pass_format(); + + if (render_pass_format == pipeline_framebuffer_format_) { + // Either created, or failed to create once (don't try to create every + // frame). + return pipeline_triangle_ != VK_NULL_HANDLE && + pipeline_line_ != VK_NULL_HANDLE; + } + + if (last_paint_submission_index_ && pipeline_triangle_ != VK_NULL_HANDLE && + pipeline_line_ != VK_NULL_HANDLE) { + // Make sure it's safe to delete the pipelines. + VulkanPresenter& vulkan_presenter = + *static_cast(presenter()); + vulkan_presenter.AwaitUISubmissionCompletionFromUIThread( + last_paint_submission_index_); + } + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + // Safe to destroy the pipelines now - if the render pass was recreated, + // completion of its usage has already been awaited. + util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, pipeline_line_); + util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, pipeline_triangle_); + // If creation fails now, don't try to create every frame. + pipeline_framebuffer_format_ = render_pass_format; + + // Triangle pipeline. + + VkPipelineShaderStageCreateInfo stages[2] = {}; + stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stages[0].module = util::CreateShaderModule(provider_, immediate_vert, + sizeof(immediate_vert)); + if (stages[0].module == VK_NULL_HANDLE) { + XELOGE("VulkanImmediateDrawer: Failed to create the vertex shader module"); + return false; + } + stages[0].pName = "main"; + stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stages[1].module = util::CreateShaderModule(provider_, immediate_frag, + sizeof(immediate_frag)); + if (stages[1].module == VK_NULL_HANDLE) { + XELOGE( + "VulkanImmediateDrawer: Failed to create the fragment shader module"); + dfn.vkDestroyShaderModule(device, stages[0].module, nullptr); + return false; + } + stages[1].pName = "main"; + + VkVertexInputBindingDescription vertex_input_binding; + vertex_input_binding.binding = 0; + vertex_input_binding.stride = sizeof(ImmediateVertex); + vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + VkVertexInputAttributeDescription vertex_input_attributes[3]; + vertex_input_attributes[0].location = 0; + vertex_input_attributes[0].binding = 0; + vertex_input_attributes[0].format = VK_FORMAT_R32G32_SFLOAT; + vertex_input_attributes[0].offset = offsetof(ImmediateVertex, x); + vertex_input_attributes[1].location = 1; + vertex_input_attributes[1].binding = 0; + vertex_input_attributes[1].format = VK_FORMAT_R32G32_SFLOAT; + vertex_input_attributes[1].offset = offsetof(ImmediateVertex, u); + vertex_input_attributes[2].location = 2; + vertex_input_attributes[2].binding = 0; + vertex_input_attributes[2].format = VK_FORMAT_R8G8B8A8_UNORM; + vertex_input_attributes[2].offset = offsetof(ImmediateVertex, color); + VkPipelineVertexInputStateCreateInfo vertex_input_state; + vertex_input_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input_state.pNext = nullptr; + vertex_input_state.flags = 0; + vertex_input_state.vertexBindingDescriptionCount = 1; + vertex_input_state.pVertexBindingDescriptions = &vertex_input_binding; + vertex_input_state.vertexAttributeDescriptionCount = + uint32_t(xe::countof(vertex_input_attributes)); + vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes; + + VkPipelineInputAssemblyStateCreateInfo input_assembly_state; + input_assembly_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly_state.pNext = nullptr; + input_assembly_state.flags = 0; + input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly_state.primitiveRestartEnable = VK_FALSE; + + VkPipelineViewportStateCreateInfo viewport_state; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.pNext = nullptr; + viewport_state.flags = 0; + viewport_state.viewportCount = 1; + viewport_state.pViewports = nullptr; + viewport_state.scissorCount = 1; + viewport_state.pScissors = nullptr; + + VkPipelineRasterizationStateCreateInfo rasterization_state = {}; + rasterization_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterization_state.polygonMode = VK_POLYGON_MODE_FILL; + rasterization_state.cullMode = VK_CULL_MODE_NONE; + rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterization_state.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo multisample_state = {}; + multisample_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState color_blend_attachment_state; + color_blend_attachment_state.blendEnable = VK_TRUE; + color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment_state.dstColorBlendFactor = + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment_state.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + VkPipelineColorBlendStateCreateInfo color_blend_state = {}; + color_blend_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blend_state.attachmentCount = 1; + color_blend_state.pAttachments = &color_blend_attachment_state; + + static const VkDynamicState dynamic_states[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + VkPipelineDynamicStateCreateInfo dynamic_state; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.pNext = nullptr; + dynamic_state.flags = 0; + dynamic_state.dynamicStateCount = uint32_t(xe::countof(dynamic_states)); + dynamic_state.pDynamicStates = dynamic_states; + + VkGraphicsPipelineCreateInfo pipeline_create_info; + pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_create_info.pNext = nullptr; + pipeline_create_info.flags = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT; + pipeline_create_info.stageCount = uint32_t(xe::countof(stages)); + pipeline_create_info.pStages = stages; + pipeline_create_info.pVertexInputState = &vertex_input_state; + pipeline_create_info.pInputAssemblyState = &input_assembly_state; + pipeline_create_info.pTessellationState = nullptr; + pipeline_create_info.pViewportState = &viewport_state; + pipeline_create_info.pRasterizationState = &rasterization_state; + pipeline_create_info.pMultisampleState = &multisample_state; + pipeline_create_info.pDepthStencilState = nullptr; + pipeline_create_info.pColorBlendState = &color_blend_state; + pipeline_create_info.pDynamicState = &dynamic_state; + pipeline_create_info.layout = pipeline_layout_; + pipeline_create_info.renderPass = vulkan_ui_draw_context.render_pass(); + pipeline_create_info.subpass = 0; + pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_create_info.basePipelineIndex = UINT32_MAX; + if (dfn.vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, + &pipeline_create_info, nullptr, + &pipeline_triangle_) != VK_SUCCESS) { + XELOGE( + "VulkanImmediateDrawer: Failed to create the triangle list pipeline"); + dfn.vkDestroyShaderModule(device, stages[1].module, nullptr); + dfn.vkDestroyShaderModule(device, stages[0].module, nullptr); + return false; + } + + // Line pipeline. + + input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + pipeline_create_info.flags = + (pipeline_create_info.flags & ~VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT) | + VK_PIPELINE_CREATE_DERIVATIVE_BIT; + pipeline_create_info.basePipelineHandle = pipeline_triangle_; + VkResult pipeline_line_create_result = dfn.vkCreateGraphicsPipelines( + device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr, + &pipeline_line_); + dfn.vkDestroyShaderModule(device, stages[1].module, nullptr); + dfn.vkDestroyShaderModule(device, stages[0].module, nullptr); + if (pipeline_line_create_result != VK_SUCCESS) { + XELOGE("VulkanImmediateDrawer: Failed to create the line list pipeline"); + dfn.vkDestroyPipeline(device, pipeline_triangle_, nullptr); + pipeline_triangle_ = VK_NULL_HANDLE; + return false; + } + + return true; +} + +uint32_t VulkanImmediateDrawer::AllocateTextureDescriptor() { + // Try to reuse a recycled descriptor first. + if (texture_descriptor_pool_recycled_first_) { + TextureDescriptorPool* pool = texture_descriptor_pool_recycled_first_; + assert_not_zero(pool->recycled_bits); + uint32_t local_index; + xe::bit_scan_forward(pool->recycled_bits, &local_index); + pool->recycled_bits &= ~(uint64_t(1) << local_index); + if (!pool->recycled_bits) { + texture_descriptor_pool_recycled_first_ = pool->recycled_next; + } + return (pool->index << 6) | local_index; + } + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkDescriptorSetAllocateInfo allocate_info; + allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocate_info.pNext = nullptr; + allocate_info.descriptorSetCount = 1; + allocate_info.pSetLayouts = &texture_descriptor_set_layout_; + + // If no recycled, try to create a new allocation within an existing pool with + // unallocated descriptors left. + while (texture_descriptor_pool_unallocated_first_) { + TextureDescriptorPool* pool = texture_descriptor_pool_unallocated_first_; + assert_not_zero(pool->unallocated_count); + allocate_info.descriptorPool = pool->pool; + uint32_t local_index = + TextureDescriptorPool::kDescriptorCount - pool->unallocated_count; + VkResult allocate_result = dfn.vkAllocateDescriptorSets( + device, &allocate_info, &pool->sets[local_index]); + if (allocate_result == VK_SUCCESS) { + --pool->unallocated_count; + } else { + // Failed to allocate for some reason, don't try again for this pool. + pool->unallocated_count = 0; + } + if (!pool->unallocated_count) { + texture_descriptor_pool_unallocated_first_ = pool->unallocated_next; + } + if (allocate_result == VK_SUCCESS) { + return (pool->index << 6) | local_index; + } + } + + // Create a new pool and allocate the descriptor from it. + VkDescriptorPoolSize descriptor_pool_size; + descriptor_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_pool_size.descriptorCount = + TextureDescriptorPool::kDescriptorCount; + VkDescriptorPoolCreateInfo descriptor_pool_create_info; + descriptor_pool_create_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptor_pool_create_info.pNext = nullptr; + descriptor_pool_create_info.flags = 0; + descriptor_pool_create_info.maxSets = TextureDescriptorPool::kDescriptorCount; + descriptor_pool_create_info.poolSizeCount = 1; + descriptor_pool_create_info.pPoolSizes = &descriptor_pool_size; + VkDescriptorPool descriptor_pool; + if (dfn.vkCreateDescriptorPool(device, &descriptor_pool_create_info, nullptr, + &descriptor_pool) != VK_SUCCESS) { + XELOGE( + "VulkanImmediateDrawer: Failed to create a combined image sampler " + "descriptor pool with {} descriptors", + TextureDescriptorPool::kDescriptorCount); + return UINT32_MAX; + } + allocate_info.descriptorPool = descriptor_pool; + VkDescriptorSet descriptor_set; + if (dfn.vkAllocateDescriptorSets(device, &allocate_info, &descriptor_set) != + VK_SUCCESS) { + XELOGE( + "VulkanImmediateDrawer: Failed to allocate a combined image sampler " + "descriptor"); + dfn.vkDestroyDescriptorPool(device, descriptor_pool, nullptr); + return UINT32_MAX; + } + TextureDescriptorPool* new_pool = new TextureDescriptorPool; + new_pool->pool = descriptor_pool; + new_pool->sets[0] = descriptor_set; + uint32_t new_pool_index = uint32_t(texture_descriptor_pools_.size()); + new_pool->index = new_pool_index; + new_pool->unallocated_count = TextureDescriptorPool::kDescriptorCount - 1; + new_pool->recycled_bits = 0; + new_pool->unallocated_next = texture_descriptor_pool_unallocated_first_; + texture_descriptor_pool_unallocated_first_ = new_pool; + new_pool->recycled_next = nullptr; + texture_descriptor_pools_.push_back(new_pool); + return new_pool_index << 6; +} + +VkDescriptorSet VulkanImmediateDrawer::GetTextureDescriptor( + uint32_t descriptor_index) const { + uint32_t pool_index = descriptor_index >> 6; + assert_true(pool_index < texture_descriptor_pools_.size()); + const TextureDescriptorPool* pool = texture_descriptor_pools_[pool_index]; + uint32_t allocation_index = descriptor_index & 63; + assert_true(allocation_index < TextureDescriptorPool::kDescriptorCount - + pool->unallocated_count); + return pool->sets[allocation_index]; +} + +void VulkanImmediateDrawer::FreeTextureDescriptor(uint32_t descriptor_index) { + uint32_t pool_index = descriptor_index >> 6; + assert_true(pool_index < texture_descriptor_pools_.size()); + TextureDescriptorPool* pool = texture_descriptor_pools_[pool_index]; + uint32_t allocation_index = descriptor_index & 63; + assert_true(allocation_index < TextureDescriptorPool::kDescriptorCount - + pool->unallocated_count); + assert_zero(pool->recycled_bits & (uint64_t(1) << allocation_index)); + if (!pool->recycled_bits) { + // Add to the free list if not already in it. + pool->recycled_next = texture_descriptor_pool_recycled_first_; + texture_descriptor_pool_recycled_first_ = pool; + } + pool->recycled_bits |= uint64_t(1) << allocation_index; +} + +bool VulkanImmediateDrawer::CreateTextureResource( + uint32_t width, uint32_t height, ImmediateTextureFilter filter, + bool is_repeated, const uint8_t* data, + VulkanImmediateTexture::Resource& resource_out, + size_t& pending_upload_index_out) { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + // Create the image and the descriptor. + + VkImageCreateInfo image_create_info; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = nullptr; + image_create_info.flags = 0; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; + image_create_info.extent.width = width; + image_create_info.extent.height = height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_create_info.usage = + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_create_info.queueFamilyIndexCount = 0; + image_create_info.pQueueFamilyIndices = nullptr; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImage image; + VkDeviceMemory image_memory; + if (!util::CreateDedicatedAllocationImage(provider_, image_create_info, + util::MemoryPurpose::kDeviceLocal, + image, image_memory)) { + XELOGE( + "VulkanImmediateDrawer: Failed to create an image with dedicated " + "memory for a {}x{} texture", + width, height); + return false; + } + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = nullptr; + image_view_create_info.flags = 0; + image_view_create_info.image = image; + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; + // data == nullptr is a special case for (1, 1, 1, 1). + VkComponentSwizzle swizzle = + data ? VK_COMPONENT_SWIZZLE_IDENTITY : VK_COMPONENT_SWIZZLE_ONE; + image_view_create_info.components.r = swizzle; + image_view_create_info.components.g = swizzle; + image_view_create_info.components.b = swizzle; + image_view_create_info.components.a = swizzle; + util::InitializeSubresourceRange(image_view_create_info.subresourceRange); + VkImageView image_view; + if (dfn.vkCreateImageView(device, &image_view_create_info, nullptr, + &image_view) != VK_SUCCESS) { + XELOGE( + "VulkanImmediateDrawer: Failed to create an image view for a {}x{} " + "image", + width, height); + dfn.vkDestroyImage(device, image, nullptr); + dfn.vkFreeMemory(device, image_memory, nullptr); + return false; + } + + uint32_t descriptor_index = AllocateTextureDescriptor(); + if (descriptor_index == UINT32_MAX) { + XELOGE( + "VulkanImmediateDrawer: Failed to allocate a descriptor for an image"); + dfn.vkDestroyImageView(device, image_view, nullptr); + dfn.vkDestroyImage(device, image, nullptr); + dfn.vkFreeMemory(device, image_memory, nullptr); + return false; + } + VkDescriptorImageInfo descriptor_image_info; + VulkanProvider::HostSampler host_sampler; + if (filter == ImmediateTextureFilter::kLinear) { + host_sampler = is_repeated ? VulkanProvider::HostSampler::kLinearRepeat + : VulkanProvider::HostSampler::kLinearClamp; + } else { + host_sampler = is_repeated ? VulkanProvider::HostSampler::kNearestRepeat + : VulkanProvider::HostSampler::kNearestClamp; + } + descriptor_image_info.sampler = provider_.GetHostSampler(host_sampler); + descriptor_image_info.imageView = image_view; + descriptor_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkWriteDescriptorSet descriptor_write; + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.pNext = nullptr; + descriptor_write.dstSet = GetTextureDescriptor(descriptor_index); + descriptor_write.dstBinding = 0; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorCount = 1; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_write.pImageInfo = &descriptor_image_info; + descriptor_write.pBufferInfo = nullptr; + descriptor_write.pTexelBufferView = nullptr; + dfn.vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, nullptr); + + // Create and fill the upload buffer. + + // data == nullptr is a special case for (1, 1, 1, 1), clearing rather than + // uploading in this case. + VkBuffer upload_buffer = VK_NULL_HANDLE; + VkDeviceMemory upload_buffer_memory = VK_NULL_HANDLE; + if (data) { + size_t data_size = sizeof(uint32_t) * width * height; + uint32_t upload_buffer_memory_type; + if (!util::CreateDedicatedAllocationBuffer( + provider_, VkDeviceSize(data_size), + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, util::MemoryPurpose::kUpload, + upload_buffer, upload_buffer_memory, &upload_buffer_memory_type)) { + XELOGE( + "VulkanImmediateDrawer: Failed to create an upload buffer for a " + "{}x{} image", + width, height); + FreeTextureDescriptor(descriptor_index); + dfn.vkDestroyImageView(device, image_view, nullptr); + dfn.vkDestroyImage(device, image, nullptr); + dfn.vkFreeMemory(device, image_memory, nullptr); + return false; + } + void* upload_buffer_mapping; + if (dfn.vkMapMemory(device, upload_buffer_memory, 0, VK_WHOLE_SIZE, 0, + &upload_buffer_mapping) != VK_SUCCESS) { + XELOGE( + "VulkanImmediateDrawer: Failed to map upload buffer memory for a " + "{}x{} image", + width, height); + dfn.vkDestroyBuffer(device, upload_buffer, nullptr); + dfn.vkFreeMemory(device, upload_buffer_memory, nullptr); + FreeTextureDescriptor(descriptor_index); + dfn.vkDestroyImageView(device, image_view, nullptr); + dfn.vkDestroyImage(device, image, nullptr); + dfn.vkFreeMemory(device, image_memory, nullptr); + return false; + } + std::memcpy(upload_buffer_mapping, data, data_size); + util::FlushMappedMemoryRange(provider_, upload_buffer_memory, + upload_buffer_memory_type); + dfn.vkUnmapMemory(device, upload_buffer_memory); + } + + resource_out.image = image; + resource_out.memory = image_memory; + resource_out.image_view = image_view; + resource_out.descriptor_index = descriptor_index; + + pending_upload_index_out = texture_uploads_pending_.size(); + PendingTextureUpload& pending_upload = + texture_uploads_pending_.emplace_back(); + // The caller will set the ImmedateTexture pointer if needed. + pending_upload.texture = nullptr; + pending_upload.buffer = upload_buffer; + pending_upload.buffer_memory = upload_buffer_memory; + pending_upload.image = image; + pending_upload.width = width; + pending_upload.height = height; + + return true; +} + +void VulkanImmediateDrawer::DestroyTextureResource( + VulkanImmediateTexture::Resource& resource) { + FreeTextureDescriptor(resource.descriptor_index); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkDestroyImageView(device, resource.image_view, nullptr); + dfn.vkDestroyImage(device, resource.image, nullptr); + dfn.vkFreeMemory(device, resource.memory, nullptr); +} + +void VulkanImmediateDrawer::OnImmediateTextureDestroyed( + VulkanImmediateTexture& texture) { + // Remove from the pending uploads. + size_t pending_upload_index = texture.pending_upload_index_; + if (pending_upload_index != SIZE_MAX) { + if (pending_upload_index + 1 < texture_uploads_pending_.size()) { + PendingTextureUpload& pending_upload = + texture_uploads_pending_[pending_upload_index]; + pending_upload = texture_uploads_pending_.back(); + if (pending_upload.texture) { + pending_upload.texture->pending_upload_index_ = pending_upload_index; + } + } + texture_uploads_pending_.pop_back(); + } + + // Remove from the texture list. + VulkanImmediateTexture*& texture_at_index = + textures_[texture.immediate_drawer_index_]; + texture_at_index = textures_.back(); + texture_at_index->immediate_drawer_index_ = texture.immediate_drawer_index_; + textures_.pop_back(); + + // Destroy immediately or queue for destruction if in use. + if (texture.last_usage_submission_ > last_completed_submission_index_) { + textures_deleted_.emplace_back( + std::make_pair(texture.resource_, texture.last_usage_submission_)); + } else { + DestroyTextureResource(texture.resource_); + } } } // namespace vulkan diff --git a/src/xenia/ui/vulkan/vulkan_immediate_drawer.h b/src/xenia/ui/vulkan/vulkan_immediate_drawer.h index 5cc66fd26..ca087e1cb 100644 --- a/src/xenia/ui/vulkan/vulkan_immediate_drawer.h +++ b/src/xenia/ui/vulkan/vulkan_immediate_drawer.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2020 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,66 +10,166 @@ #ifndef XENIA_UI_VULKAN_VULKAN_IMMEDIATE_DRAWER_H_ #define XENIA_UI_VULKAN_VULKAN_IMMEDIATE_DRAWER_H_ +#include +#include #include +#include +#include #include "xenia/ui/immediate_drawer.h" -#include "xenia/ui/vulkan/vulkan.h" +#include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h" namespace xe { namespace ui { namespace vulkan { -class LightweightCircularBuffer; -class VulkanContext; - class VulkanImmediateDrawer : public ImmediateDrawer { public: - VulkanImmediateDrawer(VulkanContext* graphics_context); - ~VulkanImmediateDrawer() override; + static std::unique_ptr Create( + const VulkanProvider& provider) { + auto immediate_drawer = std::unique_ptr( + new VulkanImmediateDrawer(provider)); + if (!immediate_drawer->Initialize()) { + return nullptr; + } + return std::move(immediate_drawer); + } - VkResult Initialize(); - void Shutdown(); + ~VulkanImmediateDrawer(); std::unique_ptr CreateTexture(uint32_t width, uint32_t height, ImmediateTextureFilter filter, - bool repeat, + bool is_repeated, const uint8_t* data) override; - std::unique_ptr WrapTexture(VkImageView image_view, - VkSampler sampler, - uint32_t width, - uint32_t height); - void Begin(int render_target_width, int render_target_height) override; + void Begin(UIDrawContext& ui_draw_context, float coordinate_space_width, + float coordinate_space_height) override; void BeginDrawBatch(const ImmediateDrawBatch& batch) override; void Draw(const ImmediateDraw& draw) override; void EndDrawBatch() override; void End() override; - VkSampler GetSampler(ImmediateTextureFilter filter, bool repeat); + protected: + void OnLeavePresenter() override; private: - VulkanContext* context_ = nullptr; + struct PushConstants { + struct Vertex { + float coordinate_space_size_inv[2]; + } vertex; + }; - struct { - VkSampler nearest_clamp = nullptr; - VkSampler nearest_repeat = nullptr; - VkSampler linear_clamp = nullptr; - VkSampler linear_repeat = nullptr; - } samplers_; + class VulkanImmediateTexture : public ImmediateTexture { + public: + struct Resource { + VkImage image; + VkDeviceMemory memory; + VkImageView image_view; + uint32_t descriptor_index; + }; - VkDescriptorSetLayout texture_set_layout_ = nullptr; - VkDescriptorPool descriptor_pool_ = nullptr; - VkPipelineLayout pipeline_layout_ = nullptr; - VkPipeline triangle_pipeline_ = nullptr; - VkPipeline line_pipeline_ = nullptr; + VulkanImmediateTexture(uint32_t width, uint32_t height) + : ImmediateTexture(width, height), immediate_drawer_(nullptr) {} + ~VulkanImmediateTexture() override; - std::unique_ptr circular_buffer_; + // If null, this is either a blank texture, or the immediate drawer has been + // destroyed. + VulkanImmediateDrawer* immediate_drawer_; + size_t immediate_drawer_index_; + // Invalid if immediate_drawer_ is null, since it's managed by the immediate + // drawer. + Resource resource_; + size_t pending_upload_index_; + uint64_t last_usage_submission_ = 0; + }; - bool batch_has_index_buffer_ = false; - VkCommandBuffer current_cmd_buffer_ = nullptr; - int current_render_target_width_ = 0; - int current_render_target_height_ = 0; + struct TextureDescriptorPool { + // Using uint64_t for recycled bits. + static constexpr uint32_t kDescriptorCount = 64; + VkDescriptorPool pool; + VkDescriptorSet sets[kDescriptorCount]; + uint32_t index; + uint32_t unallocated_count; + uint64_t recycled_bits; + TextureDescriptorPool* unallocated_next; + TextureDescriptorPool* recycled_next; + }; + + VulkanImmediateDrawer(const VulkanProvider& provider) : provider_(provider) {} + bool Initialize(); + + bool EnsurePipelinesCreatedForCurrentRenderPass(); + + // Allocates a combined image sampler in a pool and returns its index, or + // UINT32_MAX in case of failure. + uint32_t AllocateTextureDescriptor(); + VkDescriptorSet GetTextureDescriptor(uint32_t descriptor_index) const; + void FreeTextureDescriptor(uint32_t descriptor_index); + + // If data is null, a (1, 1, 1, 1) image will be created, which can be used as + // a replacement when drawing without a real texture. + bool CreateTextureResource(uint32_t width, uint32_t height, + ImmediateTextureFilter filter, bool is_repeated, + const uint8_t* data, + VulkanImmediateTexture::Resource& resource_out, + size_t& pending_upload_index_out); + void DestroyTextureResource(VulkanImmediateTexture::Resource& resource); + void OnImmediateTextureDestroyed(VulkanImmediateTexture& texture); + + const VulkanProvider& provider_; + + // Combined image sampler pools for textures. + VkDescriptorSetLayout texture_descriptor_set_layout_; + std::vector texture_descriptor_pools_; + TextureDescriptorPool* texture_descriptor_pool_unallocated_first_ = nullptr; + TextureDescriptorPool* texture_descriptor_pool_recycled_first_ = nullptr; + + VulkanImmediateTexture::Resource white_texture_ = {}; + std::vector textures_; + struct PendingTextureUpload { + // Null for internal resources such as the white texture. + VulkanImmediateTexture* texture; + // VK_NULL_HANDLE if need to clear rather than to copy. + VkBuffer buffer; + VkDeviceMemory buffer_memory; + VkImage image; + uint32_t width; + uint32_t height; + }; + std::vector texture_uploads_pending_; + struct SubmittedTextureUploadBuffer { + VkBuffer buffer; + VkDeviceMemory buffer_memory; + uint64_t submission_index; + }; + std::deque texture_upload_buffers_submitted_; + // Resource and last usage submission pairs. + std::vector> + textures_deleted_; + + std::unique_ptr vertex_buffer_pool_; + + VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE; + + VkFormat pipeline_framebuffer_format_ = VK_FORMAT_UNDEFINED; + VkPipeline pipeline_triangle_ = VK_NULL_HANDLE; + VkPipeline pipeline_line_ = VK_NULL_HANDLE; + + // The submission index within the current Begin (or the last, if outside + // one). + uint64_t last_paint_submission_index_ = 0; + // Completed submission index as of the latest Begin, to coarsely skip delayed + // texture deletion. + uint64_t last_completed_submission_index_ = 0; + + VkCommandBuffer current_command_buffer_ = VK_NULL_HANDLE; + VkExtent2D current_render_target_extent_; + VkRect2D current_scissor_; + VkPipeline current_pipeline_; + uint32_t current_texture_descriptor_index_; + bool batch_open_ = false; + bool batch_has_index_buffer_; }; } // namespace vulkan diff --git a/src/xenia/ui/vulkan/vulkan_instance.cc b/src/xenia/ui/vulkan/vulkan_instance.cc deleted file mode 100644 index 0ccbba7fa..000000000 --- a/src/xenia/ui/vulkan/vulkan_instance.cc +++ /dev/null @@ -1,634 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/vulkan/vulkan_instance.h" - -#include -#include -#include -#include - -#include "third_party/renderdoc/renderdoc_app.h" - -#include "xenia/base/assert.h" -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/platform.h" -#include "xenia/base/profiling.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_immediate_drawer.h" -#include "xenia/ui/vulkan/vulkan_util.h" -#include "xenia/ui/window.h" - -#if XE_PLATFORM_LINUX -#include -#elif XE_PLATFORM_WIN32 -#include "xenia/base/platform_win.h" -#endif - -#if XE_PLATFORM_GNU_LINUX -#include "xenia/ui/window_gtk.h" -#endif - -#define VK_API_VERSION VK_API_VERSION_1_1 - -namespace xe { -namespace ui { -namespace vulkan { - -VulkanInstance::VulkanInstance() { - if (cvars::vulkan_validation) { - DeclareRequiredLayer("VK_LAYER_LUNARG_standard_validation", - Version::Make(0, 0, 0), true); - // DeclareRequiredLayer("VK_LAYER_GOOGLE_unique_objects", Version::Make(0, - // 0, 0), true); - /* - DeclareRequiredLayer("VK_LAYER_GOOGLE_threading", Version::Make(0, 0, 0), - true); - DeclareRequiredLayer("VK_LAYER_LUNARG_core_validation", - Version::Make(0, 0, 0), true); - DeclareRequiredLayer("VK_LAYER_LUNARG_object_tracker", - Version::Make(0, 0, 0), true); - DeclareRequiredLayer("VK_LAYER_LUNARG_draw_state", Version::Make(0, 0, 0), - true); - DeclareRequiredLayer("VK_LAYER_LUNARG_parameter_validation", - Version::Make(0, 0, 0), true); - DeclareRequiredLayer("VK_LAYER_LUNARG_swapchain", Version::Make(0, 0, 0), - true); - DeclareRequiredLayer("VK_LAYER_LUNARG_device_limits", - Version::Make(0, 0, 0), true); - DeclareRequiredLayer("VK_LAYER_LUNARG_image", Version::Make(0, 0, 0), true); - */ - DeclareRequiredExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, - Version::Make(0, 0, 0), true); - } - - DeclareRequiredExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME, - Version::Make(0, 0, 0), true); -} - -VulkanInstance::~VulkanInstance() { DestroyInstance(); } - -bool VulkanInstance::Initialize() { - auto version = Version::Parse(VK_API_VERSION); - XELOGVK("Initializing Vulkan {}...", version.pretty_string); - - // Load the library. - bool library_functions_loaded = true; -#if XE_PLATFORM_LINUX -#if XE_PLATFORM_ANDROID - const char* libvulkan_name = "libvulkan.so"; -#else - const char* libvulkan_name = "libvulkan.so.1"; -#endif - // http://developer.download.nvidia.com/mobile/shield/assets/Vulkan/UsingtheVulkanAPI.pdf - library_ = dlopen(libvulkan_name, RTLD_NOW | RTLD_LOCAL); - if (!library_) { - XELOGE("Failed to load {}", libvulkan_name); - return false; - } -#define XE_VULKAN_LOAD_MODULE_LFN(name) \ - library_functions_loaded &= \ - (lfn_.name = PFN_##name(dlsym(library_, #name))) != nullptr; -#elif XE_PLATFORM_WIN32 - library_ = LoadLibraryA("vulkan-1.dll"); - if (!library_) { - XELOGE("Failed to load vulkan-1.dll"); - return false; - } -#define XE_VULKAN_LOAD_MODULE_LFN(name) \ - library_functions_loaded &= \ - (lfn_.name = PFN_##name(GetProcAddress(library_, #name))) != nullptr; -#else -#error No Vulkan library loading provided for the target platform. -#endif - XE_VULKAN_LOAD_MODULE_LFN(vkGetInstanceProcAddr); - XE_VULKAN_LOAD_MODULE_LFN(vkDestroyInstance); -#undef XE_VULKAN_LOAD_MODULE_LFN - if (!library_functions_loaded) { - XELOGE("Failed to get Vulkan library function pointers"); - return false; - } - library_functions_loaded &= - (lfn_.vkCreateInstance = PFN_vkCreateInstance(lfn_.vkGetInstanceProcAddr( - VK_NULL_HANDLE, "vkCreateInstance"))) != nullptr; - library_functions_loaded &= - (lfn_.vkEnumerateInstanceExtensionProperties = - PFN_vkEnumerateInstanceExtensionProperties( - lfn_.vkGetInstanceProcAddr( - VK_NULL_HANDLE, - "vkEnumerateInstanceExtensionProperties"))) != nullptr; - library_functions_loaded &= - (lfn_.vkEnumerateInstanceLayerProperties = - PFN_vkEnumerateInstanceLayerProperties(lfn_.vkGetInstanceProcAddr( - VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"))) != - nullptr; - if (!library_functions_loaded) { - XELOGE( - "Failed to get Vulkan library function pointers via " - "vkGetInstanceProcAddr"); - return false; - } - - // Get all of the global layers and extensions provided by the system. - if (!QueryGlobals()) { - XELOGE("Failed to query instance globals"); - return false; - } - - // Create the vulkan instance used by the application with our required - // extensions and layers. - if (!CreateInstance()) { - XELOGE("Failed to create instance"); - return false; - } - - // Query available devices so that we can pick one. - if (!QueryDevices()) { - XELOGE("Failed to query devices"); - return false; - } - - // Hook into renderdoc, if it's available. - EnableRenderDoc(); - - XELOGVK("Instance initialized successfully!"); - return true; -} - -bool VulkanInstance::EnableRenderDoc() { - // RenderDoc injects itself into our process, so we should be able to get it. - pRENDERDOC_GetAPI get_api = nullptr; -#if XE_PLATFORM_WIN32 - auto module_handle = GetModuleHandleW(L"renderdoc.dll"); - if (!module_handle) { - XELOGI("RenderDoc support requested but it is not attached"); - return false; - } - get_api = reinterpret_cast( - GetProcAddress(module_handle, "RENDERDOC_GetAPI")); -#else -// TODO(benvanik): dlsym/etc - abstracted in base/. -#endif // XE_PLATFORM_32 - if (!get_api) { - XELOGI("RenderDoc support requested but it is not attached"); - return false; - } - - // Request all API function pointers. - if (!get_api(eRENDERDOC_API_Version_1_0_1, - reinterpret_cast(&renderdoc_api_))) { - XELOGE("RenderDoc found but was unable to get API - version mismatch?"); - return false; - } - auto api = reinterpret_cast(renderdoc_api_); - - // Query version. - int major; - int minor; - int patch; - api->GetAPIVersion(&major, &minor, &patch); - XELOGI("RenderDoc attached; {}.{}.{}", major, minor, patch); - - is_renderdoc_attached_ = true; - - return true; -} - -bool VulkanInstance::QueryGlobals() { - // Scan global layers and accumulate properties. - // We do this in a loop so that we can allocate the required amount of - // memory and handle race conditions while querying. - uint32_t count = 0; - std::vector global_layer_properties; - VkResult err; - do { - err = lfn_.vkEnumerateInstanceLayerProperties(&count, nullptr); - CheckResult(err, "vkEnumerateInstanceLayerProperties"); - global_layer_properties.resize(count); - err = lfn_.vkEnumerateInstanceLayerProperties( - &count, global_layer_properties.data()); - } while (err == VK_INCOMPLETE); - CheckResult(err, "vkEnumerateInstanceLayerProperties"); - global_layers_.resize(count); - for (size_t i = 0; i < global_layers_.size(); ++i) { - auto& global_layer = global_layers_[i]; - global_layer.properties = global_layer_properties[i]; - - // Get all extensions available for the layer. - do { - err = lfn_.vkEnumerateInstanceExtensionProperties( - global_layer.properties.layerName, &count, nullptr); - CheckResult(err, "vkEnumerateInstanceExtensionProperties"); - global_layer.extensions.resize(count); - err = lfn_.vkEnumerateInstanceExtensionProperties( - global_layer.properties.layerName, &count, - global_layer.extensions.data()); - } while (err == VK_INCOMPLETE); - CheckResult(err, "vkEnumerateInstanceExtensionProperties"); - } - XELOGVK("Found {} global layers:", global_layers_.size()); - for (size_t i = 0; i < global_layers_.size(); ++i) { - auto& global_layer = global_layers_[i]; - auto spec_version = Version::Parse(global_layer.properties.specVersion); - auto impl_version = - Version::Parse(global_layer.properties.implementationVersion); - XELOGVK("- {} (spec: {}, impl: {})", global_layer.properties.layerName, - spec_version.pretty_string, impl_version.pretty_string); - XELOGVK(" {}", global_layer.properties.description); - if (!global_layer.extensions.empty()) { - XELOGVK(" {} extensions:", global_layer.extensions.size()); - DumpExtensions(global_layer.extensions, " "); - } - } - - // Scan global extensions. - do { - err = lfn_.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr); - CheckResult(err, "vkEnumerateInstanceExtensionProperties"); - global_extensions_.resize(count); - err = lfn_.vkEnumerateInstanceExtensionProperties( - nullptr, &count, global_extensions_.data()); - } while (err == VK_INCOMPLETE); - CheckResult(err, "vkEnumerateInstanceExtensionProperties"); - XELOGVK("Found {} global extensions:", global_extensions_.size()); - DumpExtensions(global_extensions_, ""); - - return true; -} - -bool VulkanInstance::CreateInstance() { - XELOGVK("Verifying layers and extensions..."); - - // Gather list of enabled layer names. - auto layers_result = CheckRequirements(required_layers_, global_layers_); - auto& enabled_layers = layers_result.second; - - // Gather list of enabled extension names. - auto extensions_result = - CheckRequirements(required_extensions_, global_extensions_); - auto& enabled_extensions = extensions_result.second; - - // We wait until both extensions and layers are checked before failing out so - // that the user gets a complete list of what they have/don't. - if (!extensions_result.first || !layers_result.first) { - XELOGE("Layer and extension verification failed; aborting initialization"); - return false; - } - - XELOGVK("Initializing application instance..."); - - // TODO(benvanik): use GetEntryInfo? - VkApplicationInfo application_info; - application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - application_info.pNext = nullptr; - application_info.pApplicationName = "xenia"; - application_info.applicationVersion = 1; - application_info.pEngineName = "xenia"; - application_info.engineVersion = 1; - application_info.apiVersion = VK_API_VERSION; - - VkInstanceCreateInfo instance_info; - instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instance_info.pNext = nullptr; - instance_info.flags = 0; - instance_info.pApplicationInfo = &application_info; - instance_info.enabledLayerCount = - static_cast(enabled_layers.size()); - instance_info.ppEnabledLayerNames = enabled_layers.data(); - instance_info.enabledExtensionCount = - static_cast(enabled_extensions.size()); - instance_info.ppEnabledExtensionNames = enabled_extensions.data(); - - auto err = lfn_.vkCreateInstance(&instance_info, nullptr, &handle); - if (err != VK_SUCCESS) { - XELOGE("vkCreateInstance returned {}", to_string(err)); - } - switch (err) { - case VK_SUCCESS: - // Ok! - break; - case VK_ERROR_INITIALIZATION_FAILED: - XELOGE("Instance initialization failed; generic"); - return false; - case VK_ERROR_INCOMPATIBLE_DRIVER: - XELOGE( - "Instance initialization failed; cannot find a compatible Vulkan " - "installable client driver (ICD)"); - return false; - case VK_ERROR_EXTENSION_NOT_PRESENT: - XELOGE("Instance initialization failed; requested extension not present"); - return false; - case VK_ERROR_LAYER_NOT_PRESENT: - XELOGE("Instance initialization failed; requested layer not present"); - return false; - default: - XELOGE("Instance initialization failed; unknown: {}", to_string(err)); - return false; - } - - // Check if extensions are enabled. - dbg_report_ena_ = false; - for (const char* enabled_extension : enabled_extensions) { - if (!dbg_report_ena_ && - !std::strcmp(enabled_extension, VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { - dbg_report_ena_ = true; - } - } - - // Get instance functions. - std::memset(&ifn_, 0, sizeof(ifn_)); - bool instance_functions_loaded = true; -#define XE_UI_VULKAN_FUNCTION(name) \ - instance_functions_loaded &= \ - (ifn_.name = PFN_##name(lfn_.vkGetInstanceProcAddr(handle, #name))) != \ - nullptr; -#include "xenia/ui/vulkan/functions/instance_1_0.inc" -#include "xenia/ui/vulkan/functions/instance_khr_surface.inc" -#if XE_PLATFORM_ANDROID -#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc" -#elif XE_PLATFORM_GNU_LINUX -#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc" -#elif XE_PLATFORM_WIN32 -#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc" -#endif - if (dbg_report_ena_) { -#include "xenia/ui/vulkan/functions/instance_ext_debug_report.inc" - } -#undef XE_VULKAN_LOAD_IFN - if (!instance_functions_loaded) { - XELOGE("Failed to get Vulkan instance function pointers"); - return false; - } - - // Enable debug validation, if needed. - EnableDebugValidation(); - - return true; -} - -void VulkanInstance::DestroyInstance() { - if (handle) { - DisableDebugValidation(); - lfn_.vkDestroyInstance(handle, nullptr); - handle = nullptr; - } - -#if XE_PLATFORM_LINUX - if (library_) { - dlclose(library_); - library_ = nullptr; - } -#elif XE_PLATFORM_WIN32 - if (library_) { - FreeLibrary(library_); - library_ = nullptr; - } -#endif -} - -VkBool32 VKAPI_PTR DebugMessageCallback(VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, size_t location, - int32_t messageCode, - const char* pLayerPrefix, - const char* pMessage, void* pUserData) { - if (strcmp(pLayerPrefix, "Validation") == 0) { - const char* blacklist[] = { - "bound but it was never updated. You may want to either update it or " - "not bind it.", - "is being used in draw but has not been updated.", - }; - for (uint32_t i = 0; i < xe::countof(blacklist); ++i) { - if (strstr(pMessage, blacklist[i]) != nullptr) { - return false; - } - } - } - - auto instance = reinterpret_cast(pUserData); - const char* message_type = "UNKNOWN"; - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { - message_type = "ERROR"; - } else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { - message_type = "WARN"; - } else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) { - message_type = "PERF WARN"; - } else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) { - message_type = "INFO"; - } else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { - message_type = "DEBUG"; - } - - XELOGVK("[{}/{}:{}] {}", pLayerPrefix, message_type, messageCode, pMessage); - return false; -} - -void VulkanInstance::EnableDebugValidation() { - if (!dbg_report_ena_) { - XELOGVK("Debug validation layer not installed; ignoring"); - return; - } - if (dbg_report_callback_) { - DisableDebugValidation(); - } - VkDebugReportCallbackCreateInfoEXT create_info; - create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - create_info.pNext = nullptr; - // TODO(benvanik): flags to set these. - create_info.flags = - VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | - VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | - VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT; - create_info.pfnCallback = &DebugMessageCallback; - create_info.pUserData = this; - auto status = ifn_.vkCreateDebugReportCallbackEXT( - handle, &create_info, nullptr, &dbg_report_callback_); - if (status == VK_SUCCESS) { - XELOGVK("Debug validation layer enabled"); - } else { - XELOGVK("Debug validation layer failed to install; error {}", - to_string(status)); - } -} - -void VulkanInstance::DisableDebugValidation() { - if (!dbg_report_ena_ || !dbg_report_callback_) { - return; - } - ifn_.vkDestroyDebugReportCallbackEXT(handle, dbg_report_callback_, nullptr); - dbg_report_callback_ = nullptr; -} - -bool VulkanInstance::QueryDevices() { - // Get handles to all devices. - uint32_t count = 0; - std::vector device_handles; - auto err = ifn_.vkEnumeratePhysicalDevices(handle, &count, nullptr); - CheckResult(err, "vkEnumeratePhysicalDevices"); - - device_handles.resize(count); - err = ifn_.vkEnumeratePhysicalDevices(handle, &count, device_handles.data()); - CheckResult(err, "vkEnumeratePhysicalDevices"); - - // Query device info. - for (size_t i = 0; i < device_handles.size(); ++i) { - auto device_handle = device_handles[i]; - DeviceInfo device_info; - device_info.handle = device_handle; - - // Query general attributes. - ifn_.vkGetPhysicalDeviceProperties(device_handle, &device_info.properties); - ifn_.vkGetPhysicalDeviceFeatures(device_handle, &device_info.features); - ifn_.vkGetPhysicalDeviceMemoryProperties(device_handle, - &device_info.memory_properties); - - // Gather queue family properties. - ifn_.vkGetPhysicalDeviceQueueFamilyProperties(device_handle, &count, - nullptr); - device_info.queue_family_properties.resize(count); - ifn_.vkGetPhysicalDeviceQueueFamilyProperties( - device_handle, &count, device_info.queue_family_properties.data()); - - // Gather layers. - std::vector layer_properties; - err = ifn_.vkEnumerateDeviceLayerProperties(device_handle, &count, nullptr); - CheckResult(err, "vkEnumerateDeviceLayerProperties"); - layer_properties.resize(count); - err = ifn_.vkEnumerateDeviceLayerProperties(device_handle, &count, - layer_properties.data()); - CheckResult(err, "vkEnumerateDeviceLayerProperties"); - for (size_t j = 0; j < layer_properties.size(); ++j) { - LayerInfo layer_info; - layer_info.properties = layer_properties[j]; - err = ifn_.vkEnumerateDeviceExtensionProperties( - device_handle, layer_info.properties.layerName, &count, nullptr); - CheckResult(err, "vkEnumerateDeviceExtensionProperties"); - layer_info.extensions.resize(count); - err = ifn_.vkEnumerateDeviceExtensionProperties( - device_handle, layer_info.properties.layerName, &count, - layer_info.extensions.data()); - CheckResult(err, "vkEnumerateDeviceExtensionProperties"); - device_info.layers.push_back(std::move(layer_info)); - } - - // Gather extensions. - err = ifn_.vkEnumerateDeviceExtensionProperties(device_handle, nullptr, - &count, nullptr); - CheckResult(err, "vkEnumerateDeviceExtensionProperties"); - device_info.extensions.resize(count); - err = ifn_.vkEnumerateDeviceExtensionProperties( - device_handle, nullptr, &count, device_info.extensions.data()); - CheckResult(err, "vkEnumerateDeviceExtensionProperties"); - - available_devices_.push_back(std::move(device_info)); - } - - XELOGVK("Found {} physical devices:", available_devices_.size()); - for (size_t i = 0; i < available_devices_.size(); ++i) { - auto& device_info = available_devices_[i]; - XELOGVK("- Device {}:", i); - DumpDeviceInfo(device_info); - } - - return true; -} - -void VulkanInstance::DumpLayers(const std::vector& layers, - const char* indent) { - for (size_t i = 0; i < layers.size(); ++i) { - auto& layer = layers[i]; - auto spec_version = Version::Parse(layer.properties.specVersion); - auto impl_version = Version::Parse(layer.properties.implementationVersion); - XELOGVK("{}- {} (spec: {}, impl: {})", indent, layer.properties.layerName, - spec_version.pretty_string, impl_version.pretty_string); - XELOGVK("{} {}", indent, layer.properties.description); - if (!layer.extensions.empty()) { - XELOGVK("{} {} extensions:", indent, layer.extensions.size()); - DumpExtensions(layer.extensions, std::strlen(indent) ? " " : " "); - } - } -} - -void VulkanInstance::DumpExtensions( - const std::vector& extensions, const char* indent) { - for (size_t i = 0; i < extensions.size(); ++i) { - auto& extension = extensions[i]; - auto version = Version::Parse(extension.specVersion); - XELOGVK("{}- {} ({})", indent, extension.extensionName, - version.pretty_string); - } -} - -void VulkanInstance::DumpDeviceInfo(const DeviceInfo& device_info) { - auto& properties = device_info.properties; - auto api_version = Version::Parse(properties.apiVersion); - auto driver_version = Version::Parse(properties.driverVersion); - XELOGVK(" apiVersion = {}", api_version.pretty_string); - XELOGVK(" driverVersion = {}", driver_version.pretty_string); - XELOGVK(" vendorId = {:#04x}", properties.vendorID); - XELOGVK(" deviceId = {:#04x}", properties.deviceID); - XELOGVK(" deviceType = {}", to_string(properties.deviceType)); - XELOGVK(" deviceName = {}", properties.deviceName); - - auto& memory_props = device_info.memory_properties; - XELOGVK(" Memory Heaps:"); - for (size_t j = 0; j < memory_props.memoryHeapCount; ++j) { - XELOGVK(" - Heap {}: {} bytes", j, memory_props.memoryHeaps[j].size); - for (size_t k = 0; k < memory_props.memoryTypeCount; ++k) { - if (memory_props.memoryTypes[k].heapIndex == j) { - XELOGVK(" - Type {}:", k); - auto type_flags = memory_props.memoryTypes[k].propertyFlags; - if (!type_flags) { - XELOGVK(" VK_MEMORY_PROPERTY_DEVICE_ONLY"); - } - if (type_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { - XELOGVK(" VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT"); - } - if (type_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { - XELOGVK(" VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT"); - } - if (type_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { - XELOGVK(" VK_MEMORY_PROPERTY_HOST_COHERENT_BIT"); - } - if (type_flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { - XELOGVK(" VK_MEMORY_PROPERTY_HOST_CACHED_BIT"); - } - if (type_flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { - XELOGVK(" VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT"); - } - } - } - } - - XELOGVK(" Queue Families:"); - for (size_t j = 0; j < device_info.queue_family_properties.size(); ++j) { - auto& queue_props = device_info.queue_family_properties[j]; - XELOGVK(" - Queue {}:", j); - XELOGVK( - " queueFlags = {}{}{}{}", - (queue_props.queueFlags & VK_QUEUE_GRAPHICS_BIT) ? "graphics, " : "", - (queue_props.queueFlags & VK_QUEUE_COMPUTE_BIT) ? "compute, " : "", - (queue_props.queueFlags & VK_QUEUE_TRANSFER_BIT) ? "transfer, " : "", - (queue_props.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) ? "sparse, " - : ""); - XELOGVK(" queueCount = {}", queue_props.queueCount); - XELOGVK(" timestampValidBits = {}", queue_props.timestampValidBits); - } - - XELOGVK(" Layers:"); - DumpLayers(device_info.layers, " "); - - XELOGVK(" Extensions:"); - DumpExtensions(device_info.extensions, " "); -} - -} // namespace vulkan -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_instance.h b/src/xenia/ui/vulkan/vulkan_instance.h deleted file mode 100644 index 52a92f53e..000000000 --- a/src/xenia/ui/vulkan/vulkan_instance.h +++ /dev/null @@ -1,145 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_VULKAN_VULKAN_INSTANCE_H_ -#define XENIA_UI_VULKAN_VULKAN_INSTANCE_H_ - -#include -#include -#include - -#include "xenia/base/platform.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_util.h" -#include "xenia/ui/window.h" - -namespace xe { -namespace ui { -namespace vulkan { - -// Wrappers and utilities for VkInstance. -class VulkanInstance { - public: - VulkanInstance(); - ~VulkanInstance(); - - struct LibraryFunctions { - // From the module. - PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; - PFN_vkDestroyInstance vkDestroyInstance; - // From vkGetInstanceProcAddr. - PFN_vkCreateInstance vkCreateInstance; - PFN_vkEnumerateInstanceExtensionProperties - vkEnumerateInstanceExtensionProperties; - PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; - }; - const LibraryFunctions& lfn() const { return lfn_; } - - VkInstance handle = nullptr; - - operator VkInstance() const { return handle; } - - struct InstanceFunctions { -#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name; -#include "xenia/ui/vulkan/functions/instance_1_0.inc" -#include "xenia/ui/vulkan/functions/instance_ext_debug_report.inc" -#include "xenia/ui/vulkan/functions/instance_khr_surface.inc" -#if XE_PLATFORM_ANDROID -#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc" -#elif XE_PLATFORM_GNU_LINUX -#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc" -#elif XE_PLATFORM_WIN32 -#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc" -#endif -#undef XE_UI_VULKAN_FUNCTION - }; - const InstanceFunctions& ifn() const { return ifn_; } - - // Declares a layer to verify and enable upon initialization. - // Must be called before Initialize. - void DeclareRequiredLayer(std::string name, uint32_t min_version, - bool is_optional) { - required_layers_.push_back({name, min_version, is_optional}); - } - - // Declares an extension to verify and enable upon initialization. - // Must be called before Initialize. - void DeclareRequiredExtension(std::string name, uint32_t min_version, - bool is_optional) { - required_extensions_.push_back({name, min_version, is_optional}); - } - - // Initializes the instance, querying and enabling extensions and layers and - // preparing the instance for general use. - // If initialization succeeds it's likely that no more failures beyond runtime - // issues will occur. - bool Initialize(); - - // Returns a list of all available devices as detected during initialization. - const std::vector& available_devices() const { - return available_devices_; - } - - // True if RenderDoc is attached and available for use. - bool is_renderdoc_attached() const { return is_renderdoc_attached_; } - // RenderDoc API handle, if attached. - void* renderdoc_api() const { return renderdoc_api_; } - - private: - // Attempts to enable RenderDoc via the API, if it is attached. - bool EnableRenderDoc(); - - // Queries the system to find global extensions and layers. - bool QueryGlobals(); - - // Creates the instance, enabling required extensions and layers. - bool CreateInstance(); - void DestroyInstance(); - - // Enables debugging info and callbacks for supported layers. - void EnableDebugValidation(); - void DisableDebugValidation(); - - // Queries all available physical devices. - bool QueryDevices(); - - void DumpLayers(const std::vector& layers, const char* indent); - void DumpExtensions(const std::vector& extensions, - const char* indent); - void DumpDeviceInfo(const DeviceInfo& device_info); - -#if XE_PLATFORM_LINUX - void* library_ = nullptr; -#elif XE_PLATFORM_WIN32 - HMODULE library_ = nullptr; -#endif - - LibraryFunctions lfn_ = {}; - - std::vector required_layers_; - std::vector required_extensions_; - - InstanceFunctions ifn_ = {}; - - std::vector global_layers_; - std::vector global_extensions_; - std::vector available_devices_; - - bool dbg_report_ena_ = false; - VkDebugReportCallbackEXT dbg_report_callback_ = nullptr; - - void* renderdoc_api_ = nullptr; - bool is_renderdoc_attached_ = false; -}; - -} // namespace vulkan -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_VULKAN_VULKAN_INSTANCE_H_ diff --git a/src/xenia/ui/vulkan/vulkan_mem_alloc.h b/src/xenia/ui/vulkan/vulkan_mem_alloc.h index 31a9b2c40..133a1f269 100644 --- a/src/xenia/ui/vulkan/vulkan_mem_alloc.h +++ b/src/xenia/ui/vulkan/vulkan_mem_alloc.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2018 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,20 +10,32 @@ #ifndef XENIA_UI_VULKAN_VULKAN_MEM_ALLOC_H_ #define XENIA_UI_VULKAN_VULKAN_MEM_ALLOC_H_ -#define VMA_STATIC_VULKAN_FUNCTIONS 0 -#include "third_party/vulkan/vk_mem_alloc.h" +// Make sure vulkan.h is included from third_party (rather than from the system +// include directory) before vk_mem_alloc.h. -#include "xenia/ui/vulkan/vulkan_device.h" -#include "xenia/ui/vulkan/vulkan_instance.h" +#include "xenia/ui/vulkan/vulkan_provider.h" + +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +// Work around the pointer nullability completeness warnings on Clang. +#ifndef VMA_NULLABLE +#define VMA_NULLABLE +#endif +#ifndef VMA_NOT_NULL +#define VMA_NOT_NULL +#endif +#include "third_party/vulkan/vk_mem_alloc.h" namespace xe { namespace ui { namespace vulkan { inline void FillVMAVulkanFunctions(VmaVulkanFunctions* vma_funcs, - const VulkanDevice& device) { - const VulkanInstance::InstanceFunctions& ifn = device.instance()->ifn(); - const VulkanDevice::DeviceFunctions& dfn = device.dfn(); + const VulkanProvider& provider) { + const VulkanProvider::LibraryFunctions& lfn = provider.lfn(); + const VulkanProvider::InstanceFunctions& ifn = provider.ifn(); + const VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + vma_funcs->vkGetInstanceProcAddr = lfn.vkGetInstanceProcAddr; + vma_funcs->vkGetDeviceProcAddr = ifn.vkGetDeviceProcAddr; vma_funcs->vkGetPhysicalDeviceProperties = ifn.vkGetPhysicalDeviceProperties; vma_funcs->vkGetPhysicalDeviceMemoryProperties = ifn.vkGetPhysicalDeviceMemoryProperties; @@ -31,6 +43,9 @@ inline void FillVMAVulkanFunctions(VmaVulkanFunctions* vma_funcs, vma_funcs->vkFreeMemory = dfn.vkFreeMemory; vma_funcs->vkMapMemory = dfn.vkMapMemory; vma_funcs->vkUnmapMemory = dfn.vkUnmapMemory; + vma_funcs->vkFlushMappedMemoryRanges = dfn.vkFlushMappedMemoryRanges; + vma_funcs->vkInvalidateMappedMemoryRanges = + dfn.vkInvalidateMappedMemoryRanges; vma_funcs->vkBindBufferMemory = dfn.vkBindBufferMemory; vma_funcs->vkBindImageMemory = dfn.vkBindImageMemory; vma_funcs->vkGetBufferMemoryRequirements = dfn.vkGetBufferMemoryRequirements; @@ -39,6 +54,7 @@ inline void FillVMAVulkanFunctions(VmaVulkanFunctions* vma_funcs, vma_funcs->vkDestroyBuffer = dfn.vkDestroyBuffer; vma_funcs->vkCreateImage = dfn.vkCreateImage; vma_funcs->vkDestroyImage = dfn.vkDestroyImage; + vma_funcs->vkCmdCopyBuffer = dfn.vkCmdCopyBuffer; } } // namespace vulkan diff --git a/src/xenia/ui/vulkan/vulkan_presenter.cc b/src/xenia/ui/vulkan/vulkan_presenter.cc new file mode 100644 index 000000000..64a665c48 --- /dev/null +++ b/src/xenia/ui/vulkan/vulkan_presenter.cc @@ -0,0 +1,2548 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/vulkan/vulkan_presenter.h" + +#include +#include +#include +#include +#include +#include + +#include "xenia/base/assert.h" +#include "xenia/base/cvar.h" +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/base/platform.h" +#include "xenia/ui/vulkan/vulkan_util.h" + +#if XE_PLATFORM_ANDROID +#include "xenia/ui/surface_android.h" +#endif +#if XE_PLATFORM_GNU_LINUX +#include "xenia/ui/surface_gnulinux.h" +#endif +#if XE_PLATFORM_WIN32 +#include "xenia/ui/surface_win.h" +#endif + +// Note: If the priorities in the description are changed, update the actual +// present mode selection logic. +DEFINE_bool( + vulkan_allow_present_mode_immediate, true, + "When available, allow the immediate presentation mode (1st priority), " + "offering the lowest latency with the possibility of tearing in certain " + "cases, and, depending on the configuration, variable refresh rate.", + "Vulkan"); +DEFINE_bool( + vulkan_allow_present_mode_mailbox, true, + "When available, allow the mailbox presentation mode (2nd priority), " + "offering low latency without the possibility of tearing.", + "Vulkan"); +DEFINE_bool( + vulkan_allow_present_mode_fifo_relaxed, true, + "When available, allow the relaxed first-in-first-out presentation mode " + "(3rd priority), which causes waiting for host display vertical sync, but " + "may present with tearing if frames don't meet the host display refresh " + "rate.", + "Vulkan"); + +namespace xe { +namespace ui { +namespace vulkan { + +// Generated with `xb buildshaders`. +namespace shaders { +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_dither_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_bilinear_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_dither_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_resample_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_dither_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_cas_sharpen_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_easu_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_dither_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_ffx_fsr_rcas_frag.h" +#include "xenia/ui/shaders/bytecode/vulkan_spirv/guest_output_triangle_strip_rect_vert.h" +} // namespace shaders + +VulkanPresenter::PaintContext::Submission::~Submission() { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + if (draw_command_pool_ != VK_NULL_HANDLE) { + dfn.vkDestroyCommandPool(device, draw_command_pool_, nullptr); + } + + if (present_semaphore_ != VK_NULL_HANDLE) { + dfn.vkDestroySemaphore(device, present_semaphore_, nullptr); + } + if (acquire_semaphore_ != VK_NULL_HANDLE) { + dfn.vkDestroySemaphore(device, acquire_semaphore_, nullptr); + } +} + +bool VulkanPresenter::PaintContext::Submission::Initialize() { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkSemaphoreCreateInfo semaphore_create_info; + semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_create_info.pNext = nullptr; + semaphore_create_info.flags = 0; + if (dfn.vkCreateSemaphore(device, &semaphore_create_info, nullptr, + &acquire_semaphore_) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create a swapchain image acquisition " + "semaphore"); + return false; + } + if (dfn.vkCreateSemaphore(device, &semaphore_create_info, nullptr, + &present_semaphore_) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create a swapchain image presentation " + "semaphore"); + return false; + } + + VkCommandPoolCreateInfo command_pool_create_info; + command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + command_pool_create_info.pNext = nullptr; + command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + command_pool_create_info.queueFamilyIndex = + provider_.queue_family_graphics_compute(); + if (dfn.vkCreateCommandPool(device, &command_pool_create_info, nullptr, + &draw_command_pool_) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create a command pool for drawing to a " + "swapchain"); + return false; + } + VkCommandBufferAllocateInfo command_buffer_allocate_info; + command_buffer_allocate_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buffer_allocate_info.pNext = nullptr; + command_buffer_allocate_info.commandPool = draw_command_pool_; + command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buffer_allocate_info.commandBufferCount = 1; + if (dfn.vkAllocateCommandBuffers(device, &command_buffer_allocate_info, + &draw_command_buffer_) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to allocate a command buffer for drawing to a " + "swapchain"); + return false; + } + + return true; +} + +VulkanPresenter::~VulkanPresenter() { + // Destroy the swapchain after its images are not used for drawing anymore. + // This is a confusing part in Vulkan, as vkQueuePresentKHR doesn't signal a + // fence clearly indicating when it's safe to destroy a swapchain, so we + // assume that its lifetime is tracked internally in the WSI. This is also + // done before destroying the semaphore awaited by vkQueuePresentKHR, hoping + // that it will prevent the destruction during the semaphore wait in + // vkQueuePresentKHR execution (or between the vkQueueSubmit semaphore signal + // and the vkQueuePresentKHR semaphore wait). + // This will await completion of all paint submissions also. + paint_context_.DestroySwapchainAndVulkanSurface(); + + // Await completion of the usage of everything before destroying anything + // (paint submission completion already awaited). + // From most likely the latest to most likely the earliest to be signaled, so + // just one sleep will likely be needed. + ui_submission_tracker_.Shutdown(); + guest_output_image_refresher_submission_tracker_.Shutdown(); + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + if (paint_context_.swapchain_render_pass != VK_NULL_HANDLE) { + dfn.vkDestroyRenderPass(device, paint_context_.swapchain_render_pass, + nullptr); + } + + for (const PaintContext::UISetupCommandBuffer& ui_setup_command_buffer : + paint_context_.ui_setup_command_buffers) { + dfn.vkDestroyCommandPool(device, ui_setup_command_buffer.command_pool, + nullptr); + } + + for (VkFramebuffer& framebuffer : + paint_context_.guest_output_intermediate_framebuffers) { + util::DestroyAndNullHandle(dfn.vkDestroyFramebuffer, device, framebuffer); + } + util::DestroyAndNullHandle(dfn.vkDestroyDescriptorPool, device, + paint_context_.guest_output_descriptor_pool); + for (PaintContext::GuestOutputPaintPipeline& guest_output_paint_pipeline : + paint_context_.guest_output_paint_pipelines) { + util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, + guest_output_paint_pipeline.swapchain_pipeline); + util::DestroyAndNullHandle( + dfn.vkDestroyPipeline, device, + guest_output_paint_pipeline.intermediate_pipeline); + } + + util::DestroyAndNullHandle(dfn.vkDestroyRenderPass, device, + guest_output_intermediate_render_pass_); + for (VkShaderModule& shader_module : guest_output_paint_fs_) { + util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device, + shader_module); + } + util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device, + guest_output_paint_vs_); + for (VkPipelineLayout& pipeline_layout : + guest_output_paint_pipeline_layouts_) { + util::DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, device, + pipeline_layout); + } + util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout, device, + guest_output_paint_image_descriptor_set_layout_); +} + +Surface::TypeFlags VulkanPresenter::GetSupportedSurfaceTypes() const { + if (!provider_.device_extensions().khr_swapchain) { + return 0; + } + return GetSurfaceTypesSupportedByInstance(provider_.instance_extensions()); +} + +bool VulkanPresenter::CaptureGuestOutput(RawImage& image_out) { + std::shared_ptr guest_output_image; + { + uint32_t guest_output_mailbox_index; + std::unique_lock guest_output_consumer_lock( + ConsumeGuestOutput(guest_output_mailbox_index, nullptr, nullptr)); + if (guest_output_mailbox_index != UINT32_MAX) { + assert_true(guest_output_images_[guest_output_mailbox_index] + .ever_successfully_refreshed); + guest_output_image = + guest_output_images_[guest_output_mailbox_index].image; + } + // Incremented the reference count of the guest output image - safe to leave + // the consumer critical section now. + } + if (!guest_output_image) { + return false; + } + + VkExtent2D image_extent = guest_output_image->extent(); + size_t pixel_count = size_t(image_extent.width) * image_extent.height; + VkDeviceSize buffer_size = VkDeviceSize(sizeof(uint32_t) * pixel_count); + VkBuffer buffer; + VkDeviceMemory buffer_memory; + if (!util::CreateDedicatedAllocationBuffer( + provider_, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, + util::MemoryPurpose::kReadback, buffer, buffer_memory)) { + XELOGE("VulkanPresenter: Failed to create the guest output capture buffer"); + return false; + } + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + { + VkCommandPoolCreateInfo command_pool_create_info; + command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + command_pool_create_info.pNext = nullptr; + command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + command_pool_create_info.queueFamilyIndex = + provider_.queue_family_graphics_compute(); + VkCommandPool command_pool; + if (dfn.vkCreateCommandPool(device, &command_pool_create_info, nullptr, + &command_pool) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create the guest output capturing " + "command pool"); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, buffer_memory, nullptr); + return false; + } + + VkCommandBufferAllocateInfo command_buffer_allocate_info; + command_buffer_allocate_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buffer_allocate_info.pNext = nullptr; + command_buffer_allocate_info.commandPool = command_pool; + command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buffer_allocate_info.commandBufferCount = 1; + VkCommandBuffer command_buffer; + if (dfn.vkAllocateCommandBuffers(device, &command_buffer_allocate_info, + &command_buffer) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to allocate the guest output capturing " + "command buffer"); + dfn.vkDestroyCommandPool(device, command_pool, nullptr); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, buffer_memory, nullptr); + return false; + } + + VkCommandBufferBeginInfo command_buffer_begin_info; + command_buffer_begin_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + command_buffer_begin_info.pNext = nullptr; + command_buffer_begin_info.flags = + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + command_buffer_begin_info.pInheritanceInfo = nullptr; + if (dfn.vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info) != + VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to begin recording the guest output " + "capturing command buffer"); + dfn.vkDestroyCommandPool(device, command_pool, nullptr); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, buffer_memory, nullptr); + return false; + } + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = nullptr; + image_memory_barrier.srcAccessMask = kGuestOutputInternalAccessMask; + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + image_memory_barrier.oldLayout = kGuestOutputInternalLayout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = guest_output_image->image(); + util::InitializeSubresourceRange(image_memory_barrier.subresourceRange); + dfn.vkCmdPipelineBarrier(command_buffer, kGuestOutputInternalStageMask, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, + nullptr, 1, &image_memory_barrier); + + VkBufferImageCopy buffer_image_copy = {}; + buffer_image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + buffer_image_copy.imageSubresource.layerCount = 1; + buffer_image_copy.imageExtent.width = image_extent.width; + buffer_image_copy.imageExtent.height = image_extent.height; + buffer_image_copy.imageExtent.depth = 1; + dfn.vkCmdCopyImageToBuffer(command_buffer, guest_output_image->image(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, 1, + &buffer_image_copy); + + // A fence doesn't guarantee host visibility and availability. + VkBufferMemoryBarrier buffer_memory_barrier; + buffer_memory_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + buffer_memory_barrier.pNext = nullptr; + buffer_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + buffer_memory_barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT; + buffer_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + buffer_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + buffer_memory_barrier.buffer = buffer; + buffer_memory_barrier.offset = 0; + buffer_memory_barrier.size = VK_WHOLE_SIZE; + std::swap(image_memory_barrier.srcAccessMask, + image_memory_barrier.dstAccessMask); + std::swap(image_memory_barrier.oldLayout, image_memory_barrier.newLayout); + dfn.vkCmdPipelineBarrier( + command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_HOST_BIT | kGuestOutputInternalStageMask, 0, 0, + nullptr, 1, &buffer_memory_barrier, 1, &image_memory_barrier); + + if (dfn.vkEndCommandBuffer(command_buffer) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to end recording the guest output capturing " + "command buffer"); + dfn.vkDestroyCommandPool(device, command_pool, nullptr); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, buffer_memory, nullptr); + return false; + } + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + VulkanSubmissionTracker submission_tracker(provider_); + { + VulkanSubmissionTracker::FenceAcquisition fence_acqusition( + submission_tracker.AcquireFenceToAdvanceSubmission()); + if (!fence_acqusition.fence()) { + XELOGE( + "VulkanPresenter: Failed to acquire a fence for guest output " + "capturing"); + fence_acqusition.SubmissionFailedOrDropped(); + dfn.vkDestroyCommandPool(device, command_pool, nullptr); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, buffer_memory, nullptr); + return false; + } + VkResult submit_result; + { + VulkanProvider::QueueAcquisition queue_acquisition( + provider_.AcquireQueue(provider_.queue_family_graphics_compute(), + 0)); + submit_result = dfn.vkQueueSubmit( + queue_acquisition.queue, 1, &submit_info, fence_acqusition.fence()); + } + if (submit_result != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to submit the guest output capturing " + "command buffer"); + fence_acqusition.SubmissionFailedOrDropped(); + dfn.vkDestroyCommandPool(device, command_pool, nullptr); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, buffer_memory, nullptr); + return false; + } + } + if (!submission_tracker.AwaitAllSubmissionsCompletion()) { + XELOGE( + "VulkanPresenter: Failed to await the guest output capturing fence"); + dfn.vkDestroyCommandPool(device, command_pool, nullptr); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, buffer_memory, nullptr); + return false; + } + + dfn.vkDestroyCommandPool(device, command_pool, nullptr); + } + + // Don't need the buffer anymore, just its memory. + dfn.vkDestroyBuffer(device, buffer, nullptr); + + void* mapping; + if (dfn.vkMapMemory(device, buffer_memory, 0, VK_WHOLE_SIZE, 0, &mapping) != + VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to map the guest output capture memory"); + dfn.vkFreeMemory(device, buffer_memory, nullptr); + return false; + } + + image_out.width = image_extent.width; + image_out.height = image_extent.height; + image_out.stride = sizeof(uint32_t) * image_extent.width; + image_out.data.resize(size_t(buffer_size)); + uint32_t* image_out_pixels = + reinterpret_cast(image_out.data.data()); + for (size_t i = 0; i < pixel_count; ++i) { + image_out_pixels[i] = Packed10bpcRGBTo8bpcBytes( + reinterpret_cast(mapping)[i]); + } + + // Unmapping will be done by freeing. + dfn.vkFreeMemory(device, buffer_memory, nullptr); + + return true; +} + +VkCommandBuffer VulkanPresenter::AcquireUISetupCommandBufferFromUIThread() { + if (paint_context_.ui_setup_command_buffer_current_index != SIZE_MAX) { + return paint_context_ + .ui_setup_command_buffers[paint_context_ + .ui_setup_command_buffer_current_index] + .command_buffer; + } + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkCommandBufferBeginInfo command_buffer_begin_info; + command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + command_buffer_begin_info.pNext = nullptr; + command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + command_buffer_begin_info.pInheritanceInfo = nullptr; + + // Try to reuse an existing command buffer. + if (!paint_context_.ui_setup_command_buffers.empty()) { + uint64_t submission_index_completed = + ui_submission_tracker_.UpdateAndGetCompletedSubmission(); + for (size_t i = 0; i < paint_context_.ui_setup_command_buffers.size(); + ++i) { + PaintContext::UISetupCommandBuffer& ui_setup_command_buffer = + paint_context_.ui_setup_command_buffers[i]; + if (ui_setup_command_buffer.last_usage_submission_index > + submission_index_completed) { + continue; + } + if (dfn.vkResetCommandPool(device, ui_setup_command_buffer.command_pool, + 0) != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to reset a UI setup command pool"); + return VK_NULL_HANDLE; + } + if (dfn.vkBeginCommandBuffer(ui_setup_command_buffer.command_buffer, + &command_buffer_begin_info) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to begin UI setup command buffer " + "recording"); + return VK_NULL_HANDLE; + } + paint_context_.ui_setup_command_buffer_current_index = i; + ui_setup_command_buffer.last_usage_submission_index = + ui_submission_tracker_.GetCurrentSubmission(); + return ui_setup_command_buffer.command_buffer; + } + } + + // Create a new command buffer. + VkCommandPoolCreateInfo command_pool_create_info; + command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + command_pool_create_info.pNext = nullptr; + command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + command_pool_create_info.queueFamilyIndex = + provider_.queue_family_graphics_compute(); + VkCommandPool new_command_pool; + if (dfn.vkCreateCommandPool(device, &command_pool_create_info, nullptr, + &new_command_pool) != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to create a UI setup command pool"); + return VK_NULL_HANDLE; + } + VkCommandBufferAllocateInfo command_buffer_allocate_info; + command_buffer_allocate_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buffer_allocate_info.pNext = nullptr; + command_buffer_allocate_info.commandPool = new_command_pool; + command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buffer_allocate_info.commandBufferCount = 1; + VkCommandBuffer new_command_buffer; + if (dfn.vkAllocateCommandBuffers(device, &command_buffer_allocate_info, + &new_command_buffer) != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to allocate a UI setup command buffer"); + dfn.vkDestroyCommandPool(device, new_command_pool, nullptr); + return VK_NULL_HANDLE; + } + if (dfn.vkBeginCommandBuffer(new_command_buffer, + &command_buffer_begin_info) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to begin UI setup command buffer recording"); + dfn.vkDestroyCommandPool(device, new_command_pool, nullptr); + return VK_NULL_HANDLE; + } + paint_context_.ui_setup_command_buffer_current_index = + paint_context_.ui_setup_command_buffers.size(); + paint_context_.ui_setup_command_buffers.emplace_back( + new_command_pool, new_command_buffer, + ui_submission_tracker_.GetCurrentSubmission()); + return new_command_buffer; +} + +Presenter::SurfacePaintConnectResult +VulkanPresenter::ConnectOrReconnectPaintingToSurfaceFromUIThread( + Surface& new_surface, uint32_t new_surface_width, + uint32_t new_surface_height, bool was_paintable, + bool& is_vsync_implicit_out) { + const VulkanProvider::InstanceFunctions& ifn = provider_.ifn(); + VkInstance instance = provider_.instance(); + VkPhysicalDevice physical_device = provider_.physical_device(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkFormat new_swapchain_format; + + // ConnectOrReconnectToSurfaceFromUIThread may be called only for the + // ui::Surface of the current swapchain or when the old swapchain and + // VkSurface have, if the ui::Surface is the same, try using the existing + // VkSurface and creating the swapchain smoothly from the existing one - if + // this doesn't succeed, start from scratch. + // The retirement or destruction of the swapchain here will also cause + // awaiting completion of the usage of the swapchain and the surface on the + // GPU. + if (paint_context_.vulkan_surface != VK_NULL_HANDLE) { + VkSwapchainKHR old_swapchain = + paint_context_.PrepareForSwapchainRetirement(); + bool surface_unusable; + paint_context_.swapchain = PaintContext::CreateSwapchainForVulkanSurface( + provider_, paint_context_.vulkan_surface, new_surface_width, + new_surface_height, old_swapchain, paint_context_.present_queue_family, + new_swapchain_format, paint_context_.swapchain_extent, + paint_context_.swapchain_is_fifo, surface_unusable); + // Destroy the old swapchain that may be retired now. + if (old_swapchain != VK_NULL_HANDLE) { + dfn.vkDestroySwapchainKHR(device, old_swapchain, nullptr); + } + if (paint_context_.swapchain == VK_NULL_HANDLE) { + // Couldn't create the swapchain for the existing surface - start over. + paint_context_.DestroySwapchainAndVulkanSurface(); + } + } + + // If failed to create the swapchain for the previous surface, recreate the + // surface and create the new swapchain. + if (paint_context_.swapchain == VK_NULL_HANDLE) { + // DestroySwapchainAndVulkanSurface should have been called previously. + assert_true(paint_context_.vulkan_surface == VK_NULL_HANDLE); + Surface::TypeIndex surface_type = new_surface.GetType(); + // Check if the surface type is supported according to the Vulkan + // extensions. + if (!(GetSupportedSurfaceTypes() & + (Surface::TypeFlags(1) << surface_type))) { + XELOGE( + "VulkanPresenter: Tried to create a Vulkan surface for an " + "unsupported Xenia surface type"); + return SurfacePaintConnectResult::kFailureSurfaceUnusable; + } + VkResult vulkan_surface_create_result = VK_ERROR_UNKNOWN; + switch (surface_type) { +#if XE_PLATFORM_ANDROID + case Surface::kTypeIndex_AndroidNativeWindow: { + auto& android_native_window_surface = + static_cast(new_surface); + VkAndroidSurfaceCreateInfoKHR surface_create_info; + surface_create_info.sType = + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + surface_create_info.pNext = nullptr; + surface_create_info.flags = 0; + surface_create_info.window = android_native_window_surface.window(); + vulkan_surface_create_result = ifn.vkCreateAndroidSurfaceKHR( + instance, &surface_create_info, nullptr, + &paint_context_.vulkan_surface); + } break; +#endif +#if XE_PLATFORM_GNU_LINUX + case Surface::kTypeIndex_XcbWindow: { + auto& xcb_window_surface = + static_cast(new_surface); + VkXcbSurfaceCreateInfoKHR surface_create_info; + surface_create_info.sType = + VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + surface_create_info.pNext = nullptr; + surface_create_info.flags = 0; + surface_create_info.connection = xcb_window_surface.connection(); + surface_create_info.window = xcb_window_surface.window(); + vulkan_surface_create_result = + ifn.vkCreateXcbSurfaceKHR(instance, &surface_create_info, nullptr, + &paint_context_.vulkan_surface); + } break; +#endif +#if XE_PLATFORM_WIN32 + case Surface::kTypeIndex_Win32Hwnd: { + auto& win32_hwnd_surface = + static_cast(new_surface); + VkWin32SurfaceCreateInfoKHR surface_create_info; + surface_create_info.sType = + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surface_create_info.pNext = nullptr; + surface_create_info.flags = 0; + surface_create_info.hinstance = win32_hwnd_surface.hinstance(); + surface_create_info.hwnd = win32_hwnd_surface.hwnd(); + vulkan_surface_create_result = + ifn.vkCreateWin32SurfaceKHR(instance, &surface_create_info, nullptr, + &paint_context_.vulkan_surface); + } break; +#endif + default: + assert_unhandled_case(surface_type); + XELOGE( + "VulkanPresenter: Tried to create a Vulkan surface for an " + "unknown Xenia surface type"); + return SurfacePaintConnectResult::kFailureSurfaceUnusable; + } + if (vulkan_surface_create_result != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to create a Vulkan surface"); + return SurfacePaintConnectResult::kFailure; + } + bool surface_unusable; + paint_context_.swapchain = PaintContext::CreateSwapchainForVulkanSurface( + provider_, paint_context_.vulkan_surface, new_surface_width, + new_surface_height, VK_NULL_HANDLE, paint_context_.present_queue_family, + new_swapchain_format, paint_context_.swapchain_extent, + paint_context_.swapchain_is_fifo, surface_unusable); + if (paint_context_.swapchain == VK_NULL_HANDLE) { + // Failed to create the swapchain for the new Vulkan surface - destroy the + // Vulkan surface. + ifn.vkDestroySurfaceKHR(instance, paint_context_.vulkan_surface, nullptr); + paint_context_.vulkan_surface = VK_NULL_HANDLE; + return surface_unusable + ? SurfacePaintConnectResult::kFailureSurfaceUnusable + : SurfacePaintConnectResult::kFailure; + } + // Successfully attached (at least for now). + } + + // From now on, in case of failure, + // paint_context_.DestroySwapchainAndVulkanSurface must be called before + // returning. + + // Update the render pass to the new format. + if (paint_context_.swapchain_render_pass_format != new_swapchain_format) { + util::DestroyAndNullHandle(dfn.vkDestroyRenderPass, device, + paint_context_.swapchain_render_pass); + paint_context_.swapchain_render_pass_format = new_swapchain_format; + } + if (paint_context_.swapchain_render_pass == VK_NULL_HANDLE) { + VkAttachmentDescription render_pass_attachment; + render_pass_attachment.flags = 0; + render_pass_attachment.format = new_swapchain_format; + render_pass_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + render_pass_attachment.loadOp = cvars::present_render_pass_clear + ? VK_ATTACHMENT_LOAD_OP_CLEAR + : VK_ATTACHMENT_LOAD_OP_DONT_CARE; + render_pass_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + render_pass_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + render_pass_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + render_pass_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + render_pass_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference render_pass_color_attachment; + render_pass_color_attachment.attachment = 0; + render_pass_color_attachment.layout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription render_pass_subpass = {}; + render_pass_subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + render_pass_subpass.colorAttachmentCount = 1; + render_pass_subpass.pColorAttachments = &render_pass_color_attachment; + VkSubpassDependency render_pass_dependencies[2]; + render_pass_dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + render_pass_dependencies[0].dstSubpass = 0; + // srcStageMask is the semaphore wait stage. + render_pass_dependencies[0].srcStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + render_pass_dependencies[0].dstStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + render_pass_dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + // The main target can be used for UI drawing at any moment, which requires + // blending, so VK_ACCESS_COLOR_ATTACHMENT_READ_BIT is also included. + render_pass_dependencies[0].dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + render_pass_dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + render_pass_dependencies[1].srcSubpass = 0; + render_pass_dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + render_pass_dependencies[1].srcStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + // Semaphores are signaled at VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT. + render_pass_dependencies[1].dstStageMask = + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + render_pass_dependencies[1].srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + render_pass_dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + render_pass_dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + VkRenderPassCreateInfo render_pass_create_info; + render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_create_info.pNext = nullptr; + render_pass_create_info.flags = 0; + render_pass_create_info.attachmentCount = 1; + render_pass_create_info.pAttachments = &render_pass_attachment; + render_pass_create_info.subpassCount = 1; + render_pass_create_info.pSubpasses = &render_pass_subpass; + render_pass_create_info.dependencyCount = + uint32_t(xe::countof(render_pass_dependencies)); + render_pass_create_info.pDependencies = render_pass_dependencies; + VkRenderPass new_render_pass; + if (dfn.vkCreateRenderPass(device, &render_pass_create_info, nullptr, + &new_render_pass) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create the render pass for drawing to " + "swapchain images"); + paint_context_.DestroySwapchainAndVulkanSurface(); + return SurfacePaintConnectResult::kFailure; + } + paint_context_.swapchain_render_pass = new_render_pass; + paint_context_.swapchain_render_pass_format = new_swapchain_format; + paint_context_.swapchain_render_pass_clear_load_op = + render_pass_attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR; + } + + // Get the swapchain images. + paint_context_.swapchain_images.clear(); + VkResult swapchain_images_get_result; + for (;;) { + uint32_t swapchain_image_count = + uint32_t(paint_context_.swapchain_images.size()); + bool swapchain_images_were_empty = !swapchain_image_count; + swapchain_images_get_result = dfn.vkGetSwapchainImagesKHR( + device, paint_context_.swapchain, &swapchain_image_count, + swapchain_images_were_empty ? nullptr + : paint_context_.swapchain_images.data()); + // If the original swapchain image count was 0 (first call), SUCCESS is + // returned, not INCOMPLETE. + if (swapchain_images_get_result == VK_SUCCESS || + swapchain_images_get_result == VK_INCOMPLETE) { + paint_context_.swapchain_images.resize(swapchain_image_count); + if (swapchain_images_get_result == VK_SUCCESS && + (!swapchain_images_were_empty || !swapchain_image_count)) { + break; + } + } else { + break; + } + } + if (swapchain_images_get_result != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to get swapchain images"); + paint_context_.DestroySwapchainAndVulkanSurface(); + return SurfacePaintConnectResult::kFailure; + } + + // Create the image views and the framebuffers. + assert_true(paint_context_.swapchain_framebuffers.empty()); + paint_context_.swapchain_framebuffers.reserve( + paint_context_.swapchain_images.size()); + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = nullptr; + image_view_create_info.flags = 0; + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_create_info.format = new_swapchain_format; + image_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_create_info.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = 1; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = 1; + VkImageView image_view; + VkFramebufferCreateInfo framebuffer_create_info; + framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_create_info.pNext = nullptr; + framebuffer_create_info.flags = 0; + framebuffer_create_info.renderPass = paint_context_.swapchain_render_pass; + framebuffer_create_info.attachmentCount = 1; + framebuffer_create_info.pAttachments = &image_view; + framebuffer_create_info.width = paint_context_.swapchain_extent.width; + framebuffer_create_info.height = paint_context_.swapchain_extent.height; + framebuffer_create_info.layers = 1; + for (VkImage image : paint_context_.swapchain_images) { + image_view_create_info.image = image; + if (dfn.vkCreateImageView(device, &image_view_create_info, nullptr, + &image_view) != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to create a swapchain image view"); + paint_context_.DestroySwapchainAndVulkanSurface(); + return SurfacePaintConnectResult::kFailure; + } + VkFramebuffer framebuffer; + if (dfn.vkCreateFramebuffer(device, &framebuffer_create_info, nullptr, + &framebuffer) != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to create a swapchain framebuffer"); + dfn.vkDestroyImageView(device, image_view, nullptr); + paint_context_.DestroySwapchainAndVulkanSurface(); + return SurfacePaintConnectResult::kFailure; + } + paint_context_.swapchain_framebuffers.emplace_back(image_view, framebuffer); + } + + is_vsync_implicit_out = paint_context_.swapchain_is_fifo; + return SurfacePaintConnectResult::kSuccess; +} + +void VulkanPresenter::DisconnectPaintingFromSurfaceFromUIThreadImpl() { + paint_context_.DestroySwapchainAndVulkanSurface(); +} + +bool VulkanPresenter::RefreshGuestOutputImpl( + uint32_t mailbox_index, uint32_t frontbuffer_width, + uint32_t frontbuffer_height, + std::function refresher, + bool& is_8bpc_out_ref) { + assert_not_zero(frontbuffer_width); + assert_not_zero(frontbuffer_height); + VkExtent2D max_framebuffer_extent = + util::GetMax2DFramebufferExtent(provider_); + if (frontbuffer_width > max_framebuffer_extent.width || + frontbuffer_height > max_framebuffer_extent.height) { + // Writing the guest output isn't supposed to rescale, and a guest texture + // exceeding the maximum size won't be loadable anyway. + return false; + } + + GuestOutputImageInstance& image_instance = + guest_output_images_[mailbox_index]; + if (image_instance.image && + (image_instance.image->extent().width != frontbuffer_width || + image_instance.image->extent().height != frontbuffer_height)) { + guest_output_image_refresher_submission_tracker_.AwaitSubmissionCompletion( + image_instance.last_refresher_submission); + image_instance.image.reset(); + } + if (!image_instance.image) { + std::unique_ptr new_image = GuestOutputImage::Create( + provider_, frontbuffer_width, frontbuffer_height); + if (!new_image) { + return false; + } + image_instance.SetToNewImage(std::move(new_image), + guest_output_image_next_version_++); + } + + VulkanGuestOutputRefreshContext context( + is_8bpc_out_ref, image_instance.image->image(), + image_instance.image->view(), image_instance.version, + image_instance.ever_successfully_refreshed); + bool refresher_succeeded = refresher(context); + if (refresher_succeeded) { + image_instance.ever_successfully_refreshed = true; + } + // Even if the refresher has returned false, it still might have submitted + // some commands referencing the image. It's better to put an excessive + // signal and wait slightly longer, for nothing important, while shutting down + // than to destroy the image while it's still in use. + image_instance.last_refresher_submission = + guest_output_image_refresher_submission_tracker_.GetCurrentSubmission(); + // No need to make the refresher signal the fence by itself - signal it here + // instead to have more control: + // "Fence signal operations that are defined by vkQueueSubmit additionally + // include in the first synchronization scope all commands that occur earlier + // in submission order." + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + { + VulkanSubmissionTracker::FenceAcquisition fence_acqusition( + guest_output_image_refresher_submission_tracker_ + .AcquireFenceToAdvanceSubmission()); + VulkanProvider::QueueAcquisition queue_acquisition( + provider_.AcquireQueue(provider_.queue_family_graphics_compute(), 0)); + if (dfn.vkQueueSubmit(queue_acquisition.queue, 0, nullptr, + fence_acqusition.fence()) != VK_SUCCESS) { + fence_acqusition.SubmissionSucceededSignalFailed(); + } + } + + return refresher_succeeded; +} + +VkSwapchainKHR VulkanPresenter::PaintContext::CreateSwapchainForVulkanSurface( + const VulkanProvider& provider, VkSurfaceKHR surface, uint32_t width, + uint32_t height, VkSwapchainKHR old_swapchain, + uint32_t& present_queue_family_out, VkFormat& image_format_out, + VkExtent2D& image_extent_out, bool& is_fifo_out, + bool& ui_surface_unusable_out) { + ui_surface_unusable_out = false; + + const VulkanProvider::InstanceFunctions& ifn = provider.ifn(); + VkInstance instance = provider.instance(); + VkPhysicalDevice physical_device = provider.physical_device(); + const VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); + + // Get surface capabilities. + VkSurfaceCapabilitiesKHR surface_capabilities; + if (ifn.vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + physical_device, surface, &surface_capabilities) != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to get Vulkan surface capabilities"); + // Some strange error, try again later. + return VK_NULL_HANDLE; + } + + // First, check if the surface is not zero-area because in this case, the rest + // of the fields in theory may not be informative as the surface doesn't need + // to go to presentation anyway, thus there's no need to return real + // information, and ui_surface_unusable_out (from which it may not be possible + // to recover on certain platforms at all) may be set to true spuriously if + // any checks set it to true. VkSurfaceKHR may have zero-area bounds in some + // window state cases on Windows, for example. Also clamp the requested size + // to the maximum supported by the physical device as long as minImageExtent + // in the instance's WSI allows that (if not, there's no way to satisfy both + // requirements - the maximum 2D framebuffer size on the specific physical + // device, and the minimum swap chain size on the whole instance - fail to + // create until the surface becomes smaller). + VkExtent2D max_framebuffer_extent = util::GetMax2DFramebufferExtent(provider); + VkExtent2D image_extent; + image_extent.width = + std::min(std::max(std::min(width, max_framebuffer_extent.width), + surface_capabilities.minImageExtent.width), + surface_capabilities.maxImageExtent.width); + image_extent.height = + std::min(std::max(std::min(height, max_framebuffer_extent.height), + surface_capabilities.minImageExtent.height), + surface_capabilities.maxImageExtent.height); + if (!image_extent.width || !image_extent.height || + image_extent.width > max_framebuffer_extent.width || + image_extent.height > max_framebuffer_extent.height) { + return VK_NULL_HANDLE; + } + + // Get the queue family for presentation. + uint32_t queue_family_index_present = UINT32_MAX; + const std::vector& queue_families = + provider.queue_families(); + VkBool32 queue_family_present_supported; + // First try the graphics and compute queue, prefer it to avoid the concurrent + // image sharing mode. + uint32_t queue_family_index_graphics_compute = + provider.queue_family_graphics_compute(); + const VulkanProvider::QueueFamily& queue_family_graphics_compute = + queue_families[queue_family_index_graphics_compute]; + if (queue_family_graphics_compute.potentially_supports_present && + queue_family_graphics_compute.queue_count && + ifn.vkGetPhysicalDeviceSurfaceSupportKHR( + physical_device, queue_family_index_graphics_compute, surface, + &queue_family_present_supported) == VK_SUCCESS && + queue_family_present_supported) { + queue_family_index_present = queue_family_index_graphics_compute; + } else { + for (uint32_t i = 0; i < uint32_t(queue_families.size()); ++i) { + const VulkanProvider::QueueFamily& queue_family = queue_families[i]; + if (queue_family.potentially_supports_present && + queue_family.queue_count && + ifn.vkGetPhysicalDeviceSurfaceSupportKHR( + physical_device, i, surface, &queue_family_present_supported) == + VK_SUCCESS && + queue_family_present_supported) { + queue_family_index_present = i; + break; + } + } + } + if (queue_family_index_present == UINT32_MAX) { + // Not unusable though - may become presentable if the window (with the same + // surface) moved to a different display, for instance. + return VK_NULL_HANDLE; + } + + // TODO(Triang3l): Support transforms. + if (!(surface_capabilities.supportedTransforms & + (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | + VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR))) { + XELOGE( + "VulkanPresenter: The surface doesn't support identity or " + "window-system-controlled transform"); + return VK_NULL_HANDLE; + } + + // Get the surface format. + std::vector surface_formats; + VkResult surface_formats_get_result; + for (;;) { + uint32_t surface_format_count = uint32_t(surface_formats.size()); + bool surface_formats_were_empty = !surface_format_count; + surface_formats_get_result = ifn.vkGetPhysicalDeviceSurfaceFormatsKHR( + physical_device, surface, &surface_format_count, + surface_formats_were_empty ? nullptr : surface_formats.data()); + // If the original presentation mode count was 0 (first call), SUCCESS is + // returned, not INCOMPLETE. + if (surface_formats_get_result == VK_SUCCESS || + surface_formats_get_result == VK_INCOMPLETE) { + surface_formats.resize(surface_format_count); + if (surface_formats_get_result == VK_SUCCESS && + (!surface_formats_were_empty || !surface_format_count)) { + break; + } + } else { + break; + } + } + if (surface_formats_get_result != VK_SUCCESS) { + // Assuming any format in case of an error (or as some fallback in case of + // specification violation). + surface_formats.clear(); + } +#if XE_PLATFORM_ANDROID + // Android uses R8G8B8A8. + static const VkFormat kFormat8888Primary = VK_FORMAT_R8G8B8A8_UNORM; + static const VkFormat kFormat8888Secondary = VK_FORMAT_B8G8R8A8_UNORM; +#else + // GNU/Linux X11 and Windows DWM use B8G8R8A8. + static const VkFormat kFormat8888Primary = VK_FORMAT_B8G8R8A8_UNORM; + static const VkFormat kFormat8888Secondary = VK_FORMAT_R8G8B8A8_UNORM; +#endif + VkSurfaceFormatKHR image_format; + if (surface_formats.empty() || + (surface_formats.size() == 1 || + surface_formats[0].format == VK_FORMAT_UNDEFINED)) { + // Can choose any format if the implementation specifies only UNDEFINED. + image_format.format = kFormat8888Primary; + image_format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + } else { + // Pick the sRGB 8888 format preferred by the OS, fall back to any sRGB + // 8888, then to 8888 with an unknown color space, and then to the first + // sRGB available, and then to the first. + auto format_8888_primary_it = surface_formats.cend(); + auto format_8888_secondary_it = surface_formats.cend(); + auto any_non_8888_srgb_it = surface_formats.cend(); + for (auto it = surface_formats.cbegin(); it != surface_formats.cend(); + ++it) { + if (it->format != kFormat8888Primary && + it->format != kFormat8888Secondary) { + if (it->colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR && + any_non_8888_srgb_it == surface_formats.cend()) { + any_non_8888_srgb_it = it; + } + continue; + } + auto& preferred_8888_it = it->format == kFormat8888Primary + ? format_8888_primary_it + : format_8888_secondary_it; + if (preferred_8888_it == surface_formats.cend()) { + // First primary or secondary 8888 encounter. + preferred_8888_it = it; + continue; + } + // Is this a better primary or secondary 8888, that is, this is sRGB, + // while the previous encounter was not? + if (it->colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR && + preferred_8888_it->colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + preferred_8888_it = it; + } + } + if (format_8888_primary_it != surface_formats.cend() && + format_8888_secondary_it != surface_formats.cend()) { + // Both the primary and the secondary 8888 formats are available - prefer + // sRGB, if both are sRGB or not, prefer the primary format. + if (format_8888_primary_it->colorSpace == + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR || + format_8888_secondary_it->colorSpace != + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + image_format = *format_8888_primary_it; + } else { + image_format = *format_8888_secondary_it; + } + } else if (format_8888_primary_it != surface_formats.cend()) { + // Only primary 8888. + image_format = *format_8888_primary_it; + } else if (format_8888_secondary_it != surface_formats.cend()) { + // Only secondary 8888. + image_format = *format_8888_secondary_it; + } else if (any_non_8888_srgb_it != surface_formats.cend()) { + // No 8888, but some sRGB format is available. + image_format = *any_non_8888_srgb_it; + } else { + // Just pick any format. + image_format = surface_formats.front(); + } + } + + // Get presentation modes. + std::vector present_modes; + VkResult present_modes_get_result; + for (;;) { + uint32_t present_mode_count = uint32_t(present_modes.size()); + bool present_modes_were_empty = !present_mode_count; + present_modes_get_result = ifn.vkGetPhysicalDeviceSurfacePresentModesKHR( + physical_device, surface, &present_mode_count, + present_modes_were_empty ? nullptr : present_modes.data()); + // If the original presentation mode count was 0 (first call), SUCCESS is + // returned, not INCOMPLETE. + if (present_modes_get_result == VK_SUCCESS || + present_modes_get_result == VK_INCOMPLETE) { + present_modes.resize(present_mode_count); + if (present_modes_get_result == VK_SUCCESS && + (!present_modes_were_empty || !present_mode_count)) { + break; + } + } else { + break; + } + } + if (present_modes_get_result != VK_SUCCESS) { + // Assuming FIFO only (required everywhere) in case of an error. + present_modes.clear(); + } + + // Create the swapchain. + VkSwapchainCreateInfoKHR swapchain_create_info; + swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_create_info.pNext = nullptr; + swapchain_create_info.flags = 0; + swapchain_create_info.surface = surface; + swapchain_create_info.minImageCount = + std::max(kSubmissionCount, surface_capabilities.minImageCount); + if (surface_capabilities.maxImageCount) { + swapchain_create_info.minImageCount = + std::min(swapchain_create_info.minImageCount, + surface_capabilities.maxImageCount); + } + swapchain_create_info.imageFormat = image_format.format; + swapchain_create_info.imageColorSpace = image_format.colorSpace; + swapchain_create_info.imageExtent = image_extent; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + uint32_t swapchain_queue_family_indices[2]; + if (queue_family_index_graphics_compute != queue_family_index_present) { + // Using concurrent sharing mode to avoid an explicit ownership transfer + // before presenting, which would require an additional command buffer + // submission with the acquire barrier and a semaphore between the graphics + // queue and the present queue. Different queues are an extremely rare case, + // and Xenia only uses the swapchain for the final guest output and the + // internal UI, so keeping framebuffer compression is not worth the + // additional submission complexity and possibly latency. + swapchain_queue_family_indices[0] = queue_family_index_graphics_compute; + swapchain_queue_family_indices[1] = queue_family_index_present; + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + swapchain_create_info.queueFamilyIndexCount = 2; + swapchain_create_info.pQueueFamilyIndices = swapchain_queue_family_indices; + } else { + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_create_info.queueFamilyIndexCount = 0; + swapchain_create_info.pQueueFamilyIndices = nullptr; + } + swapchain_create_info.preTransform = + (surface_capabilities.supportedTransforms & + VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR + : VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; + // Prefer opaque to avoid blending in the window system, or let that be + // specified via the window system if it can't be forced. As a last resort, + // just pick any - guest output will write alpha of 1 anyway. + if (surface_capabilities.supportedCompositeAlpha & + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) { + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + } else if (surface_capabilities.supportedCompositeAlpha & + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) { + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + } else { + uint32_t composite_alpha_shift; + if (!xe::bit_scan_forward( + uint32_t(surface_capabilities.supportedCompositeAlpha), + &composite_alpha_shift)) { + // Against the Vulkan specification, but breaks the logic here. + XELOGE( + "VulkanPresenter: The surface doesn't support any composite alpha " + "mode"); + return VK_NULL_HANDLE; + } + swapchain_create_info.compositeAlpha = + VkCompositeAlphaFlagBitsKHR(uint32_t(1) << composite_alpha_shift); + } + // As presentation is usually controlled by the GPU command processor, it's + // better to use modes that allow as quick acquisition as possible to avoid + // interfering with GPU command processing, and also to allow tearing so + // variable refresh rate may be used where it's available. + // Note: If the priorities here are changes, update the cvar descriptions. + if (cvars::vulkan_allow_present_mode_immediate && + std::find(present_modes.cbegin(), present_modes.cend(), + VK_PRESENT_MODE_IMMEDIATE_KHR) != present_modes.cend()) { + // Allowing tearing to reduce latency, and possibly variable refresh rate + // (though on Windows with borderless fullscreen, GDI copying is used + // instead of independent flip, so it's not supported there). + swapchain_create_info.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + } else if (cvars::vulkan_allow_present_mode_mailbox && + std::find(present_modes.cbegin(), present_modes.cend(), + VK_PRESENT_MODE_MAILBOX_KHR) != present_modes.cend()) { + // Allowing dropping frames to reduce latency, but no tearing. + swapchain_create_info.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + } else if (cvars::vulkan_allow_present_mode_fifo_relaxed && + std::find(present_modes.cbegin(), present_modes.cend(), + VK_PRESENT_MODE_FIFO_RELAXED_KHR) != + present_modes.cend()) { + // Limiting the frame rate, but lets too long frames cause tearing not to + // make the latency even worse. + swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR; + } else { + // Highest latency (but always guaranteed to be available). + swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; + } + swapchain_create_info.clipped = VK_TRUE; + swapchain_create_info.oldSwapchain = old_swapchain; + VkSwapchainKHR swapchain; + if (dfn.vkCreateSwapchainKHR(device, &swapchain_create_info, nullptr, + &swapchain) != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to create a swapchain"); + return VK_NULL_HANDLE; + } + XELOGVK( + "VulkanPresenter: Created {}x{} swapchain with format {}, color space " + "{}, presentation mode {}", + swapchain_create_info.imageExtent.width, + swapchain_create_info.imageExtent.height, + uint32_t(swapchain_create_info.imageFormat), + uint32_t(swapchain_create_info.imageColorSpace), + uint32_t(swapchain_create_info.presentMode)); + + present_queue_family_out = queue_family_index_present; + image_format_out = swapchain_create_info.imageFormat; + image_extent_out = swapchain_create_info.imageExtent; + is_fifo_out = + swapchain_create_info.presentMode == VK_PRESENT_MODE_FIFO_KHR || + swapchain_create_info.presentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR; + return swapchain; +} + +VkSwapchainKHR VulkanPresenter::PaintContext::PrepareForSwapchainRetirement() { + if (swapchain != VK_NULL_HANDLE) { + submission_tracker.AwaitAllSubmissionsCompletion(); + } + const VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); + for (const SwapchainFramebuffer& framebuffer : swapchain_framebuffers) { + dfn.vkDestroyFramebuffer(device, framebuffer.framebuffer, nullptr); + dfn.vkDestroyImageView(device, framebuffer.image_view, nullptr); + } + swapchain_framebuffers.clear(); + swapchain_images.clear(); + swapchain_extent.width = 0; + swapchain_extent.height = 0; + // The old swapchain must be destroyed externally. + VkSwapchainKHR old_swapchain = swapchain; + swapchain = nullptr; + return old_swapchain; +} + +void VulkanPresenter::PaintContext::DestroySwapchainAndVulkanSurface() { + VkSwapchainKHR old_swapchain = PrepareForSwapchainRetirement(); + if (old_swapchain != VK_NULL_HANDLE) { + const VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); + dfn.vkDestroySwapchainKHR(device, old_swapchain, nullptr); + } + present_queue_family = UINT32_MAX; + if (vulkan_surface != VK_NULL_HANDLE) { + const VulkanProvider::InstanceFunctions& ifn = provider.ifn(); + VkInstance instance = provider.instance(); + ifn.vkDestroySurfaceKHR(instance, vulkan_surface, nullptr); + vulkan_surface = VK_NULL_HANDLE; + } +} + +VulkanPresenter::GuestOutputImage::~GuestOutputImage() { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + if (view_ != VK_NULL_HANDLE) { + dfn.vkDestroyImageView(device, view_, nullptr); + } + if (image_ != VK_NULL_HANDLE) { + dfn.vkDestroyImage(device, image_, nullptr); + } + if (memory_ != VK_NULL_HANDLE) { + dfn.vkFreeMemory(device, memory_, nullptr); + } +} + +bool VulkanPresenter::GuestOutputImage::Initialize() { + VkImageCreateInfo image_create_info; + VkImageCreateInfo* image_create_info_last = &image_create_info; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = nullptr; + image_create_info.flags = 0; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = kGuestOutputFormat; + image_create_info.extent.width = extent_.width; + image_create_info.extent.height = extent_.height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_create_info.queueFamilyIndexCount = 0; + image_create_info.pQueueFamilyIndices = nullptr; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + if (!ui::vulkan::util::CreateDedicatedAllocationImage( + provider_, image_create_info, + ui::vulkan::util::MemoryPurpose::kDeviceLocal, image_, memory_)) { + XELOGE("VulkanPresenter: Failed to create a guest output image"); + return false; + } + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = nullptr; + image_view_create_info.flags = 0; + image_view_create_info.image = image_; + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_create_info.format = kGuestOutputFormat; + image_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_create_info.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = 1; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = 1; + if (dfn.vkCreateImageView(device, &image_view_create_info, nullptr, &view_) != + VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to create a guest output image view"); + return false; + } + + return true; +} + +Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl( + bool execute_ui_drawers) { + // Begin the submission in place of the one not currently potentially used on + // the GPU. + uint64_t current_paint_submission_index = + paint_context_.submission_tracker.GetCurrentSubmission(); + uint64_t paint_submission_count = uint64_t(paint_context_.submissions.size()); + if (current_paint_submission_index >= paint_submission_count) { + paint_context_.submission_tracker.AwaitSubmissionCompletion( + current_paint_submission_index - paint_submission_count); + } + const PaintContext::Submission& paint_submission = + *paint_context_.submissions[current_paint_submission_index % + paint_submission_count]; + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkCommandPool draw_command_pool = paint_submission.draw_command_pool(); + if (dfn.vkResetCommandPool(device, draw_command_pool, 0) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to reset a command buffer for drawing to the " + "swapchain"); + return PaintResult::kNotPresented; + } + + VkCommandBuffer draw_command_buffer = paint_submission.draw_command_buffer(); + VkCommandBufferBeginInfo command_buffer_begin_info; + command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + command_buffer_begin_info.pNext = nullptr; + command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + command_buffer_begin_info.pInheritanceInfo = nullptr; + if (dfn.vkBeginCommandBuffer(draw_command_buffer, + &command_buffer_begin_info) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to being recording the command buffer for " + "drawing to the swapchain"); + return PaintResult::kNotPresented; + } + // vkResetCommandPool resets from both initial and recording states, still + // safe to return early from this function in case of an error. + + VkSemaphore acquire_semaphore = paint_submission.acquire_semaphore(); + uint32_t swapchain_image_index; + VkResult acquire_result = dfn.vkAcquireNextImageKHR( + device, paint_context_.swapchain, UINT64_MAX, acquire_semaphore, + VK_NULL_HANDLE, &swapchain_image_index); + switch (acquire_result) { + case VK_SUCCESS: + case VK_SUBOPTIMAL_KHR: + break; + case VK_ERROR_DEVICE_LOST: + XELOGE( + "VulkanPresenter: Failed to acquire the swapchain image as the " + "device has been lost"); + return PaintResult::kGpuLostResponsible; + case VK_ERROR_OUT_OF_DATE_KHR: + case VK_ERROR_SURFACE_LOST_KHR: + case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: + // Not an error, reporting just as info (may normally occur while resizing + // on some platforms). + XELOGVK( + "VulkanPresenter: Presentation to the swapchain image has been " + "dropped as the swapchain or the surface has become outdated"); + return PaintResult::kNotPresentedConnectionOutdated; + default: + XELOGE("VulkanPresenter: Failed to acquire the swapchain image"); + return PaintResult::kNotPresented; + } + + // Non-zero extents needed for both the viewport (width must not be zero) and + // the guest output rectangle. + assert_not_zero(paint_context_.swapchain_extent.width); + assert_not_zero(paint_context_.swapchain_extent.height); + + bool swapchain_image_clear_needed = true; + VkClearAttachment swapchain_image_clear_attachment; + swapchain_image_clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + swapchain_image_clear_attachment.colorAttachment = 0; + swapchain_image_clear_attachment.clearValue.color.float32[0] = 0.0f; + swapchain_image_clear_attachment.clearValue.color.float32[1] = 0.0f; + swapchain_image_clear_attachment.clearValue.color.float32[2] = 0.0f; + swapchain_image_clear_attachment.clearValue.color.float32[3] = 1.0f; + + VkRenderPassBeginInfo swapchain_render_pass_begin_info; + swapchain_render_pass_begin_info.sType = + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + swapchain_render_pass_begin_info.pNext = nullptr; + swapchain_render_pass_begin_info.renderPass = + paint_context_.swapchain_render_pass; + swapchain_render_pass_begin_info.framebuffer = + paint_context_.swapchain_framebuffers[swapchain_image_index].framebuffer; + swapchain_render_pass_begin_info.renderArea.offset.x = 0; + swapchain_render_pass_begin_info.renderArea.offset.y = 0; + swapchain_render_pass_begin_info.renderArea.extent = + paint_context_.swapchain_extent; + swapchain_render_pass_begin_info.clearValueCount = 0; + swapchain_render_pass_begin_info.pClearValues = nullptr; + if (paint_context_.swapchain_render_pass_clear_load_op) { + swapchain_render_pass_begin_info.clearValueCount = 1; + swapchain_render_pass_begin_info.pClearValues = + &swapchain_image_clear_attachment.clearValue; + swapchain_image_clear_needed = false; + } + + bool swapchain_image_pass_begun = false; + + GuestOutputProperties guest_output_properties; + GuestOutputPaintConfig guest_output_paint_config; + std::shared_ptr guest_output_image; + { + uint32_t guest_output_mailbox_index; + std::unique_lock guest_output_consumer_lock( + ConsumeGuestOutput(guest_output_mailbox_index, &guest_output_properties, + &guest_output_paint_config)); + if (guest_output_mailbox_index != UINT32_MAX) { + assert_true(guest_output_images_[guest_output_mailbox_index] + .ever_successfully_refreshed); + guest_output_image = + guest_output_images_[guest_output_mailbox_index].image; + } + // Incremented the reference count of the guest output image - safe to leave + // the consumer critical section now as everything here either will be using + // the new reference or is exclusively owned by main target painting (and + // multiple threads can't paint the main target at the same time). + } + + if (guest_output_image) { + VkExtent2D max_framebuffer_extent = + util::GetMax2DFramebufferExtent(provider_); + GuestOutputPaintFlow guest_output_flow = GetGuestOutputPaintFlow( + guest_output_properties, paint_context_.swapchain_extent.width, + paint_context_.swapchain_extent.height, max_framebuffer_extent.width, + max_framebuffer_extent.height, guest_output_paint_config); + if (guest_output_flow.effect_count) { + // Store the main target reference to the guest output image so it's not + // destroyed while it's still potentially in use by main target painting + // queued on the GPU. + size_t guest_output_image_paint_ref_index = SIZE_MAX; + size_t guest_output_image_paint_ref_new_index = SIZE_MAX; + // Try to find the existing reference to the same image, or an already + // released (or a taken, but never actually used) slot. + for (size_t i = 0; + i < paint_context_.guest_output_image_paint_refs.size(); ++i) { + const std::pair>& + guest_output_image_paint_ref = + paint_context_.guest_output_image_paint_refs[i]; + if (guest_output_image_paint_ref.second == guest_output_image) { + guest_output_image_paint_ref_index = i; + break; + } + if (guest_output_image_paint_ref_new_index == SIZE_MAX && + (!guest_output_image_paint_ref.second || + !guest_output_image_paint_ref.first)) { + guest_output_image_paint_ref_new_index = i; + } + } + if (guest_output_image_paint_ref_index == SIZE_MAX) { + // New image - store the reference and create the descriptors. + if (guest_output_image_paint_ref_new_index == SIZE_MAX) { + // Replace the earliest used reference. + guest_output_image_paint_ref_new_index = 0; + for (size_t i = 1; + i < paint_context_.guest_output_image_paint_refs.size(); ++i) { + if (paint_context_.guest_output_image_paint_refs[i].first < + paint_context_ + .guest_output_image_paint_refs + [guest_output_image_paint_ref_new_index] + .first) { + guest_output_image_paint_ref_new_index = i; + } + } + // Await the completion of the usage of the old guest output image and + // its descriptors. + paint_context_.submission_tracker.AwaitSubmissionCompletion( + paint_context_ + .guest_output_image_paint_refs + [guest_output_image_paint_ref_new_index] + .first); + } + guest_output_image_paint_ref_index = + guest_output_image_paint_ref_new_index; + // The actual submission index will be set if the image is actually + // used, not dropped due to some error. + paint_context_ + .guest_output_image_paint_refs[guest_output_image_paint_ref_index] = + std::make_pair(uint64_t(0), guest_output_image); + // Create the descriptors of the new image. + VkDescriptorImageInfo guest_output_image_descriptor_image_info; + guest_output_image_descriptor_image_info.sampler = + provider_.GetHostSampler(VulkanProvider::HostSampler::kLinearClamp); + guest_output_image_descriptor_image_info.imageView = + guest_output_image->view(); + guest_output_image_descriptor_image_info.imageLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkWriteDescriptorSet guest_output_image_descriptor_write; + guest_output_image_descriptor_write.sType = + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + guest_output_image_descriptor_write.pNext = nullptr; + guest_output_image_descriptor_write.dstSet = + paint_context_.guest_output_descriptor_sets + [PaintContext::kGuestOutputDescriptorSetGuestOutput0Sampled + + guest_output_image_paint_ref_index]; + guest_output_image_descriptor_write.dstBinding = 0; + guest_output_image_descriptor_write.dstArrayElement = 0; + guest_output_image_descriptor_write.descriptorCount = 1; + guest_output_image_descriptor_write.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + guest_output_image_descriptor_write.pImageInfo = + &guest_output_image_descriptor_image_info; + guest_output_image_descriptor_write.pBufferInfo = nullptr; + guest_output_image_descriptor_write.pTexelBufferView = nullptr; + dfn.vkUpdateDescriptorSets( + device, 1, &guest_output_image_descriptor_write, 0, nullptr); + } + + // Make sure intermediate textures of the needed size are available, and + // unneeded intermediate textures are destroyed. + for (size_t i = 0; i < kMaxGuestOutputPaintEffects - 1; ++i) { + std::pair intermediate_needed_size(0, 0); + if (i + 1 < guest_output_flow.effect_count) { + intermediate_needed_size = guest_output_flow.effect_output_sizes[i]; + } + std::unique_ptr& intermediate_image_ptr_ref = + paint_context_.guest_output_intermediate_images[i]; + VkExtent2D intermediate_current_extent( + intermediate_image_ptr_ref ? intermediate_image_ptr_ref->extent() + : VkExtent2D{}); + if (intermediate_current_extent.width != + intermediate_needed_size.first || + intermediate_current_extent.height != + intermediate_needed_size.second) { + if (intermediate_needed_size.first && + intermediate_needed_size.second) { + // Need to replace immediately as a new image with the requested + // size is needed. + if (intermediate_image_ptr_ref) { + paint_context_.submission_tracker.AwaitSubmissionCompletion( + paint_context_ + .guest_output_intermediate_image_last_submission); + intermediate_image_ptr_ref.reset(); + util::DestroyAndNullHandle( + dfn.vkDestroyFramebuffer, device, + paint_context_.guest_output_intermediate_framebuffers[i]); + } + // Image. + intermediate_image_ptr_ref = GuestOutputImage::Create( + provider_, intermediate_needed_size.first, + intermediate_needed_size.second); + if (!intermediate_image_ptr_ref) { + // Don't display the guest output, and don't try to create more + // intermediate textures (only destroy them). + guest_output_flow.effect_count = 0; + continue; + } + // Framebuffer. + VkImageView intermediate_framebuffer_attachment = + intermediate_image_ptr_ref->view(); + VkFramebufferCreateInfo intermediate_framebuffer_create_info; + intermediate_framebuffer_create_info.sType = + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + intermediate_framebuffer_create_info.pNext = nullptr; + intermediate_framebuffer_create_info.flags = 0; + intermediate_framebuffer_create_info.renderPass = + guest_output_intermediate_render_pass_; + intermediate_framebuffer_create_info.attachmentCount = 1; + intermediate_framebuffer_create_info.pAttachments = + &intermediate_framebuffer_attachment; + intermediate_framebuffer_create_info.width = + intermediate_needed_size.first; + intermediate_framebuffer_create_info.height = + intermediate_needed_size.second; + intermediate_framebuffer_create_info.layers = 1; + if (dfn.vkCreateFramebuffer( + device, &intermediate_framebuffer_create_info, nullptr, + &paint_context_ + .guest_output_intermediate_framebuffers[i]) != + VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create a guest output " + "intermediate framebuffer"); + // Don't display the guest output, and don't try to create more + // intermediate textures (only destroy them). + guest_output_flow.effect_count = 0; + continue; + } + // Descriptors. + VkDescriptorImageInfo intermediate_descriptor_image_info; + intermediate_descriptor_image_info.sampler = + provider_.GetHostSampler( + VulkanProvider::HostSampler::kLinearClamp); + intermediate_descriptor_image_info.imageView = + intermediate_image_ptr_ref->view(); + intermediate_descriptor_image_info.imageLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkWriteDescriptorSet intermediate_descriptor_write; + intermediate_descriptor_write.sType = + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + intermediate_descriptor_write.pNext = nullptr; + intermediate_descriptor_write.dstSet = + paint_context_.guest_output_descriptor_sets + [PaintContext :: + kGuestOutputDescriptorSetIntermediate0Sampled + + i]; + intermediate_descriptor_write.dstBinding = 0; + intermediate_descriptor_write.dstArrayElement = 0; + intermediate_descriptor_write.descriptorCount = 1; + intermediate_descriptor_write.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + intermediate_descriptor_write.pImageInfo = + &intermediate_descriptor_image_info; + intermediate_descriptor_write.pBufferInfo = nullptr; + intermediate_descriptor_write.pTexelBufferView = nullptr; + dfn.vkUpdateDescriptorSets( + device, 1, &intermediate_descriptor_write, 0, nullptr); + } else { + // Was previously needed, but not anymore - destroy when possible. + if (intermediate_image_ptr_ref && + paint_context_.submission_tracker + .UpdateAndGetCompletedSubmission() >= + paint_context_ + .guest_output_intermediate_image_last_submission) { + intermediate_image_ptr_ref.reset(); + util::DestroyAndNullHandle( + dfn.vkDestroyFramebuffer, device, + paint_context_.guest_output_intermediate_framebuffers[i]); + } + } + } + } + + if (guest_output_flow.effect_count) { + // Check if all the intermediate effects are supported by the + // implementation. + for (size_t i = 0; i + 1 < guest_output_flow.effect_count; ++i) { + if (paint_context_ + .guest_output_paint_pipelines[size_t( + guest_output_flow.effects[i])] + .intermediate_pipeline == VK_NULL_HANDLE) { + guest_output_flow.effect_count = 0; + break; + } + } + // Ensure the pipeline exists for the final effect drawing to the + // swapchain, for the render pass with the up-to-date image format. + GuestOutputPaintEffect swapchain_effect = + guest_output_flow.effects[guest_output_flow.effect_count - 1]; + PaintContext::GuestOutputPaintPipeline& swapchain_effect_pipeline = + paint_context_ + .guest_output_paint_pipelines[size_t(swapchain_effect)]; + if (swapchain_effect_pipeline.swapchain_pipeline != VK_NULL_HANDLE && + swapchain_effect_pipeline.swapchain_format != + paint_context_.swapchain_render_pass_format) { + paint_context_.submission_tracker.AwaitSubmissionCompletion( + paint_context_.guest_output_image_paint_last_submission); + util::DestroyAndNullHandle( + dfn.vkDestroyPipeline, device, + swapchain_effect_pipeline.swapchain_pipeline); + } + if (swapchain_effect_pipeline.swapchain_pipeline == VK_NULL_HANDLE) { + assert_true(CanGuestOutputPaintEffectBeFinal(swapchain_effect)); + assert_true(guest_output_paint_fs_[size_t(swapchain_effect)] != + VK_NULL_HANDLE); + swapchain_effect_pipeline.swapchain_pipeline = + CreateGuestOutputPaintPipeline( + swapchain_effect, paint_context_.swapchain_render_pass); + if (swapchain_effect_pipeline.swapchain_pipeline == VK_NULL_HANDLE) { + guest_output_flow.effect_count = 0; + } + } + } + + if (guest_output_flow.effect_count) { + // Actually draw the guest output. + paint_context_ + .guest_output_image_paint_refs[guest_output_image_paint_ref_index] + .first = current_paint_submission_index; + paint_context_.guest_output_image_paint_last_submission = + current_paint_submission_index; + VkViewport guest_output_viewport; + guest_output_viewport.x = 0.0f; + guest_output_viewport.y = 0.0f; + guest_output_viewport.minDepth = 0.0f; + guest_output_viewport.maxDepth = 1.0f; + VkRect2D guest_output_scissor; + guest_output_scissor.offset.x = 0; + guest_output_scissor.offset.y = 0; + if (guest_output_flow.effect_count > 1) { + paint_context_.guest_output_intermediate_image_last_submission = + current_paint_submission_index; + } + for (size_t i = 0; i < guest_output_flow.effect_count; ++i) { + bool is_final_effect = i + 1 >= guest_output_flow.effect_count; + + int32_t effect_rect_x, effect_rect_y; + std::pair effect_rect_size = + guest_output_flow.effect_output_sizes[i]; + if (is_final_effect) { + effect_rect_x = guest_output_flow.output_x; + effect_rect_y = guest_output_flow.output_y; + dfn.vkCmdBeginRenderPass(draw_command_buffer, + &swapchain_render_pass_begin_info, + VK_SUBPASS_CONTENTS_INLINE); + swapchain_image_pass_begun = true; + guest_output_viewport.width = + float(paint_context_.swapchain_extent.width); + guest_output_viewport.height = + float(paint_context_.swapchain_extent.height); + guest_output_scissor.extent.width = + paint_context_.swapchain_extent.width; + guest_output_scissor.extent.height = + paint_context_.swapchain_extent.height; + } else { + effect_rect_x = 0; + effect_rect_y = 0; + VkRenderPassBeginInfo intermediate_render_pass_begin_info; + intermediate_render_pass_begin_info.sType = + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + intermediate_render_pass_begin_info.pNext = nullptr; + intermediate_render_pass_begin_info.renderPass = + guest_output_intermediate_render_pass_; + intermediate_render_pass_begin_info.framebuffer = + paint_context_.guest_output_intermediate_framebuffers[i]; + intermediate_render_pass_begin_info.renderArea.offset.x = 0; + intermediate_render_pass_begin_info.renderArea.offset.y = 0; + intermediate_render_pass_begin_info.renderArea.extent.width = + effect_rect_size.first; + intermediate_render_pass_begin_info.renderArea.extent.height = + effect_rect_size.second; + intermediate_render_pass_begin_info.clearValueCount = 0; + intermediate_render_pass_begin_info.pClearValues = nullptr; + dfn.vkCmdBeginRenderPass(draw_command_buffer, + &intermediate_render_pass_begin_info, + VK_SUBPASS_CONTENTS_INLINE); + guest_output_viewport.width = float(effect_rect_size.first); + guest_output_viewport.height = float(effect_rect_size.second); + guest_output_scissor.extent.width = effect_rect_size.first; + guest_output_scissor.extent.height = effect_rect_size.second; + } + dfn.vkCmdSetViewport(draw_command_buffer, 0, 1, + &guest_output_viewport); + dfn.vkCmdSetScissor(draw_command_buffer, 0, 1, &guest_output_scissor); + + GuestOutputPaintEffect effect = guest_output_flow.effects[i]; + + const PaintContext::GuestOutputPaintPipeline& effect_pipeline = + paint_context_.guest_output_paint_pipelines[size_t(effect)]; + VkPipeline effect_vulkan_pipeline = + is_final_effect ? effect_pipeline.swapchain_pipeline + : effect_pipeline.intermediate_pipeline; + assert_true(effect_vulkan_pipeline != VK_NULL_HANDLE); + dfn.vkCmdBindPipeline(draw_command_buffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + effect_vulkan_pipeline); + + GuestOutputPaintPipelineLayoutIndex + guest_output_paint_pipeline_layout_index = + GetGuestOutputPaintPipelineLayoutIndex(effect); + VkPipelineLayout guest_output_paint_pipeline_layout = + guest_output_paint_pipeline_layouts_ + [guest_output_paint_pipeline_layout_index]; + + PaintContext::GuestOutputDescriptorSet effect_src_descriptor_set; + if (i) { + effect_src_descriptor_set = PaintContext::GuestOutputDescriptorSet( + PaintContext::kGuestOutputDescriptorSetIntermediate0Sampled + + (i - 1)); + } else { + effect_src_descriptor_set = PaintContext::GuestOutputDescriptorSet( + PaintContext::kGuestOutputDescriptorSetGuestOutput0Sampled + + guest_output_image_paint_ref_index); + } + dfn.vkCmdBindDescriptorSets( + draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + guest_output_paint_pipeline_layout, 0, 1, + &paint_context_ + .guest_output_descriptor_sets[effect_src_descriptor_set], + 0, nullptr); + + GuestOutputPaintRectangleConstants effect_rect_constants; + float effect_x_to_ndc = 2.0f / guest_output_viewport.width; + float effect_y_to_ndc = 2.0f / guest_output_viewport.height; + effect_rect_constants.x = + -1.0f + float(effect_rect_x) * effect_x_to_ndc; + effect_rect_constants.y = + -1.0f + float(effect_rect_y) * effect_y_to_ndc; + effect_rect_constants.width = + float(effect_rect_size.first) * effect_x_to_ndc; + effect_rect_constants.height = + float(effect_rect_size.second) * effect_y_to_ndc; + dfn.vkCmdPushConstants( + draw_command_buffer, guest_output_paint_pipeline_layout, + VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(effect_rect_constants), + &effect_rect_constants); + + uint32_t effect_constants_size = 0; + union { + BilinearConstants bilinear; + CasSharpenConstants cas_sharpen; + CasResampleConstants cas_resample; + FsrEasuConstants fsr_easu; + FsrRcasConstants fsr_rcas; + } effect_constants; + switch (guest_output_paint_pipeline_layout_index) { + case kGuestOutputPaintPipelineLayoutIndexBilinear: { + effect_constants_size = sizeof(effect_constants.bilinear); + effect_constants.bilinear.Initialize(guest_output_flow, i); + } break; + case kGuestOutputPaintPipelineLayoutIndexCasSharpen: { + effect_constants_size = sizeof(effect_constants.cas_sharpen); + effect_constants.cas_sharpen.Initialize( + guest_output_flow, i, guest_output_paint_config); + } break; + case kGuestOutputPaintPipelineLayoutIndexCasResample: { + effect_constants_size = sizeof(effect_constants.cas_resample); + effect_constants.cas_resample.Initialize( + guest_output_flow, i, guest_output_paint_config); + } break; + case kGuestOutputPaintPipelineLayoutIndexFsrEasu: { + effect_constants_size = sizeof(effect_constants.fsr_easu); + effect_constants.fsr_easu.Initialize(guest_output_flow, i); + } break; + case kGuestOutputPaintPipelineLayoutIndexFsrRcas: { + effect_constants_size = sizeof(effect_constants.fsr_rcas); + effect_constants.fsr_rcas.Initialize(guest_output_flow, i, + guest_output_paint_config); + } break; + default: + break; + } + if (effect_constants_size) { + dfn.vkCmdPushConstants( + draw_command_buffer, guest_output_paint_pipeline_layout, + VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(effect_rect_constants), + effect_constants_size, &effect_constants); + } + + dfn.vkCmdDraw(draw_command_buffer, 4, 1, 0, 0); + + if (is_final_effect) { + // Clear the letterbox around the guest output if the guest output + // doesn't cover the entire swapchain image. + if (swapchain_image_clear_needed && + guest_output_flow.letterbox_clear_rectangle_count) { + VkClearRect letterbox_clear_vulkan_rectangles + [GuestOutputPaintFlow::kMaxClearRectangles]; + for (size_t i = 0; + i < guest_output_flow.letterbox_clear_rectangle_count; ++i) { + VkClearRect& letterbox_clear_vulkan_rectangle = + letterbox_clear_vulkan_rectangles[i]; + const GuestOutputPaintFlow::ClearRectangle& + letterbox_clear_rectangle = + guest_output_flow.letterbox_clear_rectangles[i]; + letterbox_clear_vulkan_rectangle.rect.offset.x = + int32_t(letterbox_clear_rectangle.x); + letterbox_clear_vulkan_rectangle.rect.offset.y = + int32_t(letterbox_clear_rectangle.y); + letterbox_clear_vulkan_rectangle.rect.extent.width = + letterbox_clear_rectangle.width; + letterbox_clear_vulkan_rectangle.rect.extent.height = + letterbox_clear_rectangle.height; + letterbox_clear_vulkan_rectangle.baseArrayLayer = 0; + letterbox_clear_vulkan_rectangle.layerCount = 1; + } + dfn.vkCmdClearAttachments( + draw_command_buffer, 1, &swapchain_image_clear_attachment, + uint32_t(guest_output_flow.letterbox_clear_rectangle_count), + letterbox_clear_vulkan_rectangles); + } + swapchain_image_clear_needed = false; + } else { + // Still need the swapchain pass to be open for UI drawing. + dfn.vkCmdEndRenderPass(draw_command_buffer); + } + } + } + } + } + + // Release main target guest output image references that aren't needed + // anymore (this is done after various potential guest-output-related main + // target submission tracker waits so the completed submission value is the + // most actual). + uint64_t completed_paint_submission = + paint_context_.submission_tracker.UpdateAndGetCompletedSubmission(); + for (std::pair>& + guest_output_image_paint_ref : + paint_context_.guest_output_image_paint_refs) { + if (!guest_output_image_paint_ref.second || + guest_output_image_paint_ref.second == guest_output_image) { + continue; + } + if (completed_paint_submission >= guest_output_image_paint_ref.first) { + guest_output_image_paint_ref.second.reset(); + } + } + + // If hasn't presented the guest output, begin the pass to clear and, if + // needed, to draw the UI. + if (!swapchain_image_pass_begun) { + dfn.vkCmdBeginRenderPass(draw_command_buffer, + &swapchain_render_pass_begin_info, + VK_SUBPASS_CONTENTS_INLINE); + } + if (swapchain_image_clear_needed) { + VkClearRect swapchain_image_clear_rectangle; + swapchain_image_clear_rectangle.rect.offset.x = 0; + swapchain_image_clear_rectangle.rect.offset.y = 0; + swapchain_image_clear_rectangle.rect.extent = + paint_context_.swapchain_extent; + swapchain_image_clear_rectangle.baseArrayLayer = 0; + swapchain_image_clear_rectangle.layerCount = 1; + dfn.vkCmdClearAttachments(draw_command_buffer, 1, + &swapchain_image_clear_attachment, 1, + &swapchain_image_clear_rectangle); + swapchain_image_clear_needed = false; + } + + if (execute_ui_drawers) { + // Draw the UI. + VulkanUIDrawContext ui_draw_context( + *this, paint_context_.swapchain_extent.width, + paint_context_.swapchain_extent.height, draw_command_buffer, + ui_submission_tracker_.GetCurrentSubmission(), + ui_submission_tracker_.UpdateAndGetCompletedSubmission(), + paint_context_.swapchain_render_pass, + paint_context_.swapchain_render_pass_format); + ExecuteUIDrawersFromUIThread(ui_draw_context); + } + + dfn.vkCmdEndRenderPass(draw_command_buffer); + + dfn.vkEndCommandBuffer(draw_command_buffer); + + VkPipelineStageFlags acquire_semaphore_wait_stage = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkCommandBuffer command_buffers[2]; + uint32_t command_buffer_count = 0; + // UI setup command buffers must be accessed only if execute_ui_drawers is not + // null, to identify the UI thread. + size_t ui_setup_command_buffer_index = + execute_ui_drawers ? paint_context_.ui_setup_command_buffer_current_index + : SIZE_MAX; + if (ui_setup_command_buffer_index != SIZE_MAX) { + PaintContext::UISetupCommandBuffer& ui_setup_command_buffer = + paint_context_.ui_setup_command_buffers[ui_setup_command_buffer_index]; + dfn.vkEndCommandBuffer(ui_setup_command_buffer.command_buffer); + command_buffers[command_buffer_count++] = + ui_setup_command_buffer.command_buffer; + // Release the current UI setup command buffer regardless of submission + // result. Failed submissions (if the UI submission index wasn't incremented + // since the previous draw) should be handled by UI drawers themselves by + // retrying all the failed work if needed. + paint_context_.ui_setup_command_buffer_current_index = SIZE_MAX; + } + command_buffers[command_buffer_count++] = draw_command_buffer; + VkSemaphore present_semaphore = paint_submission.present_semaphore(); + VkSubmitInfo submit_info; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &acquire_semaphore; + submit_info.pWaitDstStageMask = &acquire_semaphore_wait_stage; + submit_info.commandBufferCount = command_buffer_count; + submit_info.pCommandBuffers = command_buffers; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &present_semaphore; + { + VulkanSubmissionTracker::FenceAcquisition fence_acqusition( + paint_context_.submission_tracker.AcquireFenceToAdvanceSubmission()); + // Also update the submission tracker giving submission indices to UI draw + // callbacks if submission is successful. + VulkanSubmissionTracker::FenceAcquisition ui_fence_acquisition; + if (execute_ui_drawers) { + ui_fence_acquisition = + ui_submission_tracker_.AcquireFenceToAdvanceSubmission(); + } + VkResult submit_result; + { + VulkanProvider::QueueAcquisition queue_acquisition( + provider_.AcquireQueue(provider_.queue_family_graphics_compute(), 0)); + submit_result = dfn.vkQueueSubmit(queue_acquisition.queue, 1, + &submit_info, fence_acqusition.fence()); + if (ui_fence_acquisition.fence() != VK_NULL_HANDLE && + submit_result == VK_SUCCESS) { + if (dfn.vkQueueSubmit(queue_acquisition.queue, 0, nullptr, + ui_fence_acquisition.fence()) != VK_SUCCESS) { + ui_fence_acquisition.SubmissionSucceededSignalFailed(); + } + } + } + if (submit_result != VK_SUCCESS) { + XELOGE("VulkanPresenter: Failed to submit command buffers"); + fence_acqusition.SubmissionFailedOrDropped(); + ui_fence_acquisition.SubmissionFailedOrDropped(); + if (ui_setup_command_buffer_index != SIZE_MAX) { + // If failed to submit, make the UI setup command buffer available for + // immediate reuse, as the completed submission index won't be updated + // to the current index, and failing submissions with setup command + // buffer over and over will result in never reusing the setup command + // buffers. + paint_context_.ui_setup_command_buffers[ui_setup_command_buffer_index] + .last_usage_submission_index = 0; + } + // The image is in an acquired state - but now, it will be in it forever. + // To avoid that, recreate the swapchain - don't return just + // kNotPresented. + return PaintResult::kNotPresentedConnectionOutdated; + } + } + + VkPresentInfoKHR present_info; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.pNext = nullptr; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &present_semaphore; + present_info.swapchainCount = 1; + present_info.pSwapchains = &paint_context_.swapchain; + present_info.pImageIndices = &swapchain_image_index; + present_info.pResults = nullptr; + VkResult present_result; + { + VulkanProvider::QueueAcquisition queue_acquisition( + provider_.AcquireQueue(paint_context_.present_queue_family, 0)); + present_result = + dfn.vkQueuePresentKHR(queue_acquisition.queue, &present_info); + } + switch (present_result) { + case VK_SUCCESS: + return PaintResult::kPresented; + case VK_SUBOPTIMAL_KHR: + return PaintResult::kPresentedSuboptimal; + case VK_ERROR_DEVICE_LOST: + XELOGE( + "VulkanPresenter: Failed to present the swapchain image as the " + "device has been lost"); + return PaintResult::kGpuLostResponsible; + case VK_ERROR_OUT_OF_DATE_KHR: + case VK_ERROR_SURFACE_LOST_KHR: + case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: + // Not an error, reporting just as info (may normally occur while resizing + // on some platforms). + XELOGVK( + "VulkanPresenter: Presentation to the swapchain image has been " + "dropped as the swapchain or the surface has become outdated"); + // Note that the semaphore wait (followed by reset) has been enqueued, + // however, this should have no effect on anything here likely. + return PaintResult::kNotPresentedConnectionOutdated; + default: + XELOGE("VulkanPresenter: Failed to present the swapchain image"); + // The image is in an acquired state - but now, it will be in it forever. + // To avoid that, recreate the swapchain - don't return just + // kNotPresented. + return PaintResult::kNotPresentedConnectionOutdated; + } +} + +bool VulkanPresenter::InitializeSurfaceIndependent() { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkSampler sampler_linear_clamp = + provider_.GetHostSampler(VulkanProvider::HostSampler::kLinearClamp); + VkDescriptorSetLayoutBinding guest_output_image_binding; + guest_output_image_binding.binding = 0; + guest_output_image_binding.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + guest_output_image_binding.descriptorCount = 1; + guest_output_image_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + guest_output_image_binding.pImmutableSamplers = &sampler_linear_clamp; + VkDescriptorSetLayoutCreateInfo + guest_output_paint_image_descriptor_set_layout_create_info; + guest_output_paint_image_descriptor_set_layout_create_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + guest_output_paint_image_descriptor_set_layout_create_info.pNext = nullptr; + guest_output_paint_image_descriptor_set_layout_create_info.flags = 0; + guest_output_paint_image_descriptor_set_layout_create_info.bindingCount = 1; + guest_output_paint_image_descriptor_set_layout_create_info.pBindings = + &guest_output_image_binding; + if (dfn.vkCreateDescriptorSetLayout( + device, &guest_output_paint_image_descriptor_set_layout_create_info, + nullptr, + &guest_output_paint_image_descriptor_set_layout_) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create the guest output image descriptor " + "set layout"); + return false; + } + + VkPushConstantRange guest_output_paint_push_constant_ranges[2]; + VkPushConstantRange& guest_output_paint_push_constant_range_rect = + guest_output_paint_push_constant_ranges[0]; + guest_output_paint_push_constant_range_rect.stageFlags = + VK_SHADER_STAGE_VERTEX_BIT; + guest_output_paint_push_constant_range_rect.offset = 0; + guest_output_paint_push_constant_range_rect.size = + sizeof(GuestOutputPaintRectangleConstants); + VkPushConstantRange& guest_output_paint_push_constant_range_ffx = + guest_output_paint_push_constant_ranges[1]; + guest_output_paint_push_constant_range_ffx.stageFlags = + VK_SHADER_STAGE_FRAGMENT_BIT; + guest_output_paint_push_constant_range_ffx.offset = + guest_output_paint_push_constant_ranges[0].offset + + guest_output_paint_push_constant_ranges[0].size; + VkPipelineLayoutCreateInfo guest_output_paint_pipeline_layout_create_info; + guest_output_paint_pipeline_layout_create_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + guest_output_paint_pipeline_layout_create_info.pNext = nullptr; + guest_output_paint_pipeline_layout_create_info.flags = 0; + guest_output_paint_pipeline_layout_create_info.setLayoutCount = 1; + guest_output_paint_pipeline_layout_create_info.pSetLayouts = + &guest_output_paint_image_descriptor_set_layout_; + guest_output_paint_pipeline_layout_create_info.pPushConstantRanges = + guest_output_paint_push_constant_ranges; + for (size_t i = 0; i < size_t(kGuestOutputPaintPipelineLayoutCount); ++i) { + switch (GuestOutputPaintPipelineLayoutIndex(i)) { + case kGuestOutputPaintPipelineLayoutIndexBilinear: + guest_output_paint_push_constant_range_ffx.size = + sizeof(BilinearConstants); + break; + case kGuestOutputPaintPipelineLayoutIndexCasSharpen: + guest_output_paint_push_constant_range_ffx.size = + sizeof(CasSharpenConstants); + break; + case kGuestOutputPaintPipelineLayoutIndexCasResample: + guest_output_paint_push_constant_range_ffx.size = + sizeof(CasResampleConstants); + break; + case kGuestOutputPaintPipelineLayoutIndexFsrEasu: + guest_output_paint_push_constant_range_ffx.size = + sizeof(FsrEasuConstants); + break; + case kGuestOutputPaintPipelineLayoutIndexFsrRcas: + guest_output_paint_push_constant_range_ffx.size = + sizeof(FsrRcasConstants); + break; + default: + assert_unhandled_case(GuestOutputPaintPipelineLayoutIndex(i)); + continue; + } + guest_output_paint_pipeline_layout_create_info.pushConstantRangeCount = + 1 + uint32_t(guest_output_paint_push_constant_range_ffx.size != 0); + if (dfn.vkCreatePipelineLayout( + device, &guest_output_paint_pipeline_layout_create_info, nullptr, + &guest_output_paint_pipeline_layouts_[i]) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create a guest output presentation " + "pipeline layout with {} bytes of push constants", + guest_output_paint_push_constant_range_rect.size + + guest_output_paint_push_constant_range_ffx.size); + return false; + } + } + + VkShaderModuleCreateInfo shader_module_create_info; + shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_module_create_info.pNext = nullptr; + shader_module_create_info.flags = 0; + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_triangle_strip_rect_vert); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_triangle_strip_rect_vert); + if (dfn.vkCreateShaderModule(device, &shader_module_create_info, nullptr, + &guest_output_paint_vs_) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create the guest output presentation " + "vertex shader module"); + return false; + } + for (size_t i = 0; i < size_t(GuestOutputPaintEffect::kCount); ++i) { + GuestOutputPaintEffect guest_output_paint_effect = + GuestOutputPaintEffect(i); + switch (guest_output_paint_effect) { + case GuestOutputPaintEffect::kBilinear: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_bilinear_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_bilinear_frag); + break; + case GuestOutputPaintEffect::kBilinearDither: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_bilinear_dither_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_bilinear_dither_frag); + break; + case GuestOutputPaintEffect::kCasSharpen: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_ffx_cas_sharpen_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_ffx_cas_sharpen_frag); + break; + case GuestOutputPaintEffect::kCasSharpenDither: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_ffx_cas_sharpen_dither_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_ffx_cas_sharpen_dither_frag); + break; + case GuestOutputPaintEffect::kCasResample: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_ffx_cas_resample_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_ffx_cas_resample_frag); + break; + case GuestOutputPaintEffect::kCasResampleDither: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_ffx_cas_resample_dither_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_ffx_cas_resample_dither_frag); + break; + case GuestOutputPaintEffect::kFsrEasu: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_ffx_fsr_easu_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_ffx_fsr_easu_frag); + break; + case GuestOutputPaintEffect::kFsrRcas: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_ffx_fsr_rcas_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_ffx_fsr_rcas_frag); + break; + case GuestOutputPaintEffect::kFsrRcasDither: + shader_module_create_info.codeSize = + sizeof(shaders::guest_output_ffx_fsr_rcas_dither_frag); + shader_module_create_info.pCode = reinterpret_cast( + shaders::guest_output_ffx_fsr_rcas_dither_frag); + break; + default: + // Not supported by this implementation. + continue; + } + if (dfn.vkCreateShaderModule(device, &shader_module_create_info, nullptr, + &guest_output_paint_fs_[i]) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create the guest output painting shader " + "module for effect {}", + i); + return false; + } + } + + VkAttachmentDescription intermediate_render_pass_attachment; + intermediate_render_pass_attachment.flags = 0; + intermediate_render_pass_attachment.format = kGuestOutputFormat; + intermediate_render_pass_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + intermediate_render_pass_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + intermediate_render_pass_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + intermediate_render_pass_attachment.stencilLoadOp = + VK_ATTACHMENT_LOAD_OP_DONT_CARE; + intermediate_render_pass_attachment.stencilStoreOp = + VK_ATTACHMENT_STORE_OP_DONT_CARE; + intermediate_render_pass_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + intermediate_render_pass_attachment.finalLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkAttachmentReference intermediate_render_pass_color_attachment; + intermediate_render_pass_color_attachment.attachment = 0; + intermediate_render_pass_color_attachment.layout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription intermediate_render_pass_subpass = {}; + intermediate_render_pass_subpass.pipelineBindPoint = + VK_PIPELINE_BIND_POINT_GRAPHICS; + intermediate_render_pass_subpass.colorAttachmentCount = 1; + intermediate_render_pass_subpass.pColorAttachments = + &intermediate_render_pass_color_attachment; + VkSubpassDependency intermediate_render_pass_dependencies[2]; + intermediate_render_pass_dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + intermediate_render_pass_dependencies[0].dstSubpass = 0; + intermediate_render_pass_dependencies[0].srcStageMask = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + intermediate_render_pass_dependencies[0].dstStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + intermediate_render_pass_dependencies[0].srcAccessMask = + VK_ACCESS_SHADER_READ_BIT; + intermediate_render_pass_dependencies[0].dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + intermediate_render_pass_dependencies[0].dependencyFlags = 0; + intermediate_render_pass_dependencies[1].srcSubpass = 0; + intermediate_render_pass_dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + intermediate_render_pass_dependencies[1].srcStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + intermediate_render_pass_dependencies[1].dstStageMask = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + intermediate_render_pass_dependencies[1].srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + intermediate_render_pass_dependencies[1].dstAccessMask = + VK_ACCESS_SHADER_READ_BIT; + intermediate_render_pass_dependencies[1].dependencyFlags = 0; + VkRenderPassCreateInfo intermediate_render_pass_create_info; + intermediate_render_pass_create_info.sType = + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + intermediate_render_pass_create_info.pNext = nullptr; + intermediate_render_pass_create_info.flags = 0; + intermediate_render_pass_create_info.attachmentCount = 1; + intermediate_render_pass_create_info.pAttachments = + &intermediate_render_pass_attachment; + intermediate_render_pass_create_info.subpassCount = 1; + intermediate_render_pass_create_info.pSubpasses = + &intermediate_render_pass_subpass; + intermediate_render_pass_create_info.dependencyCount = + uint32_t(xe::countof(intermediate_render_pass_dependencies)); + intermediate_render_pass_create_info.pDependencies = + intermediate_render_pass_dependencies; + if (dfn.vkCreateRenderPass( + device, &intermediate_render_pass_create_info, nullptr, + &guest_output_intermediate_render_pass_) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create the guest output intermediate image " + "render pass"); + return false; + } + + // Initialize connection-independent parts of the painting context. + + for (size_t i = 0; i < paint_context_.submissions.size(); ++i) { + paint_context_.submissions[i] = PaintContext::Submission::Create(provider_); + if (!paint_context_.submissions[i]) { + return false; + } + } + + // Guest output paint pipelines drawing to intermediate images, not depending + // on runtime state unlike ones drawing to the swapchain images as those + // depend on the swapchain format. + for (size_t i = 0; i < size_t(GuestOutputPaintEffect::kCount); ++i) { + if (!CanGuestOutputPaintEffectBeIntermediate(GuestOutputPaintEffect(i)) || + guest_output_paint_fs_[i] == VK_NULL_HANDLE) { + continue; + } + VkPipeline guest_output_paint_intermediate_pipeline = + CreateGuestOutputPaintPipeline(GuestOutputPaintEffect(i), + guest_output_intermediate_render_pass_); + if (guest_output_paint_intermediate_pipeline == VK_NULL_HANDLE) { + return false; + } + paint_context_.guest_output_paint_pipelines[i].intermediate_pipeline = + guest_output_paint_intermediate_pipeline; + } + + // Guest output painting descriptor sets. + VkDescriptorPoolSize guest_output_paint_descriptor_pool_size; + guest_output_paint_descriptor_pool_size.type = + guest_output_image_binding.descriptorType; + // Each descriptor set contains only 1 descriptor. + guest_output_paint_descriptor_pool_size.descriptorCount = + PaintContext::kGuestOutputDescriptorSetCount; + VkDescriptorPoolCreateInfo guest_output_paint_descriptor_pool_create_info; + guest_output_paint_descriptor_pool_create_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + guest_output_paint_descriptor_pool_create_info.pNext = nullptr; + guest_output_paint_descriptor_pool_create_info.flags = 0; + guest_output_paint_descriptor_pool_create_info.maxSets = + PaintContext::kGuestOutputDescriptorSetCount; + guest_output_paint_descriptor_pool_create_info.poolSizeCount = 1; + guest_output_paint_descriptor_pool_create_info.pPoolSizes = + &guest_output_paint_descriptor_pool_size; + if (dfn.vkCreateDescriptorPool( + device, &guest_output_paint_descriptor_pool_create_info, nullptr, + &paint_context_.guest_output_descriptor_pool) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create the guest output painting " + "descriptor pool"); + return false; + } + VkDescriptorSetLayout guest_output_paint_descriptor_set_layouts + [PaintContext::kGuestOutputDescriptorSetCount]; + std::fill(guest_output_paint_descriptor_set_layouts, + guest_output_paint_descriptor_set_layouts + + xe::countof(guest_output_paint_descriptor_set_layouts), + guest_output_paint_image_descriptor_set_layout_); + VkDescriptorSetAllocateInfo guest_output_paint_descriptor_set_allocate_info; + guest_output_paint_descriptor_set_allocate_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + guest_output_paint_descriptor_set_allocate_info.pNext = nullptr; + guest_output_paint_descriptor_set_allocate_info.descriptorPool = + paint_context_.guest_output_descriptor_pool; + guest_output_paint_descriptor_set_allocate_info.descriptorSetCount = + PaintContext::kGuestOutputDescriptorSetCount; + guest_output_paint_descriptor_set_allocate_info.pSetLayouts = + guest_output_paint_descriptor_set_layouts; + if (dfn.vkAllocateDescriptorSets( + device, &guest_output_paint_descriptor_set_allocate_info, + paint_context_.guest_output_descriptor_sets) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to allocate the guest output painting " + "descriptor sets"); + return false; + } + + return InitializeCommonSurfaceIndependent(); +} + +VkPipeline VulkanPresenter::CreateGuestOutputPaintPipeline( + GuestOutputPaintEffect effect, VkRenderPass render_pass) { + VkPipelineShaderStageCreateInfo stages[2] = {}; + for (uint32_t i = 0; i < 2; ++i) { + VkPipelineShaderStageCreateInfo& stage = stages[i]; + stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage.stage = i ? VK_SHADER_STAGE_FRAGMENT_BIT : VK_SHADER_STAGE_VERTEX_BIT; + stage.pName = "main"; + } + stages[0].module = guest_output_paint_vs_; + stages[1].module = guest_output_paint_fs_[size_t(effect)]; + assert_true(stages[1].module != VK_NULL_HANDLE); + + VkPipelineVertexInputStateCreateInfo vertex_input_state = {}; + vertex_input_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + VkPipelineInputAssemblyStateCreateInfo input_assembly_state = {}; + input_assembly_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + + VkPipelineViewportStateCreateInfo viewport_state = {}; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterization_state = {}; + rasterization_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterization_state.polygonMode = VK_POLYGON_MODE_FILL; + rasterization_state.cullMode = VK_CULL_MODE_NONE; + rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterization_state.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo multisample_state = {}; + multisample_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState color_blend_attachment_state = {}; + color_blend_attachment_state.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + VkPipelineColorBlendStateCreateInfo color_blend_state = {}; + color_blend_state.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blend_state.attachmentCount = 1; + color_blend_state.pAttachments = &color_blend_attachment_state; + + static const VkDynamicState kPipelineDynamicStates[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + VkPipelineDynamicStateCreateInfo dynamic_state = {}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = + uint32_t(xe::countof(kPipelineDynamicStates)); + dynamic_state.pDynamicStates = kPipelineDynamicStates; + + VkGraphicsPipelineCreateInfo pipeline_create_info; + pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_create_info.pNext = nullptr; + pipeline_create_info.flags = 0; + pipeline_create_info.stageCount = uint32_t(xe::countof(stages)); + pipeline_create_info.pStages = stages; + pipeline_create_info.pVertexInputState = &vertex_input_state; + pipeline_create_info.pInputAssemblyState = &input_assembly_state; + pipeline_create_info.pTessellationState = nullptr; + pipeline_create_info.pViewportState = &viewport_state; + pipeline_create_info.pRasterizationState = &rasterization_state; + pipeline_create_info.pMultisampleState = &multisample_state; + pipeline_create_info.pDepthStencilState = nullptr; + pipeline_create_info.pColorBlendState = &color_blend_state; + pipeline_create_info.pDynamicState = &dynamic_state; + pipeline_create_info.layout = guest_output_paint_pipeline_layouts_ + [GetGuestOutputPaintPipelineLayoutIndex(effect)]; + pipeline_create_info.renderPass = render_pass; + pipeline_create_info.subpass = 0; + pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_create_info.basePipelineIndex = UINT32_MAX; + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkPipeline pipeline; + if (dfn.vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, + &pipeline_create_info, nullptr, + &pipeline) != VK_SUCCESS) { + XELOGE( + "VulkanPresenter: Failed to create the guest output painting pipeline " + "for effect {}", + size_t(effect)); + return VK_NULL_HANDLE; + } + return pipeline; +} + +} // namespace vulkan +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_presenter.h b/src/xenia/ui/vulkan/vulkan_presenter.h new file mode 100644 index 000000000..91e055c6d --- /dev/null +++ b/src/xenia/ui/vulkan/vulkan_presenter.h @@ -0,0 +1,518 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VULKAN_VULKAN_PRESENTER_H_ +#define XENIA_UI_VULKAN_VULKAN_PRESENTER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "xenia/base/assert.h" +#include "xenia/base/platform.h" +#include "xenia/ui/presenter.h" +#include "xenia/ui/surface.h" +#include "xenia/ui/vulkan/vulkan_provider.h" +#include "xenia/ui/vulkan/vulkan_submission_tracker.h" + +namespace xe { +namespace ui { +namespace vulkan { + +class VulkanUIDrawContext final : public UIDrawContext { + public: + VulkanUIDrawContext(Presenter& presenter, uint32_t render_target_width, + uint32_t render_target_height, + VkCommandBuffer draw_command_buffer, + uint64_t submission_index_current, + uint64_t submission_index_completed, + VkRenderPass render_pass, VkFormat render_pass_format) + : UIDrawContext(presenter, render_target_width, render_target_height), + draw_command_buffer_(draw_command_buffer), + submission_index_current_(submission_index_current), + submission_index_completed_(submission_index_completed), + render_pass_(render_pass), + render_pass_format_(render_pass_format) {} + + VkCommandBuffer draw_command_buffer() const { return draw_command_buffer_; } + uint64_t submission_index_current() const { + return submission_index_current_; + } + uint64_t submission_index_completed() const { + return submission_index_completed_; + } + VkRenderPass render_pass() const { return render_pass_; } + VkFormat render_pass_format() const { return render_pass_format_; } + + private: + VkCommandBuffer draw_command_buffer_; + uint64_t submission_index_current_; + uint64_t submission_index_completed_; + // Has 1 subpass with a single render_pass_format_ attachment. + VkRenderPass render_pass_; + VkFormat render_pass_format_; +}; + +class VulkanPresenter final : public Presenter { + public: + // Maximum number of different guest output image versions still potentially + // considered alive that may be given to the refresher - this many instances + // of dependent objects (such as framebuffers) may need to be kept by the + // refresher across invocations (due to multiple-buffering of guest output + // images inside the presenter, different versions may be given even every + // invocation), to avoid recreation of dependent objects every frame. + static constexpr size_t kMaxActiveGuestOutputImageVersions = + kGuestOutputMailboxSize; + + static constexpr VkFormat kGuestOutputFormat = + VK_FORMAT_A2B10G10R10_UNORM_PACK32; + // The guest output is expected to be acquired and released in this state by + // the refresher. The exception is the first write to the current guest output + // image - in this case, a barrier is only needed from + // VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT without access. Also if the image is + // being refreshed for the first time, it's in VK_IMAGE_LAYOUT_UNDEFINED (but + // it's safe, and preferred, to transition it from VK_IMAGE_LAYOUT_UNDEFINED + // when writing to it in general). + static constexpr VkPipelineStageFlagBits kGuestOutputInternalStageMask = + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + static constexpr VkAccessFlags kGuestOutputInternalAccessMask = + VK_ACCESS_SHADER_READ_BIT; + static constexpr VkImageLayout kGuestOutputInternalLayout = + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + // The callback must use the graphics and compute queue 0 of the provider. + class VulkanGuestOutputRefreshContext final + : public GuestOutputRefreshContext { + public: + VulkanGuestOutputRefreshContext(bool& is_8bpc_out_ref, VkImage image, + VkImageView image_view, + uint64_t image_version, + bool image_ever_written_previously) + : GuestOutputRefreshContext(is_8bpc_out_ref), + image_(image), + image_view_(image_view), + image_version_(image_version), + image_ever_written_previously_(image_ever_written_previously) {} + + // The format is kGuestOutputFormat. + // Supports usage as a color attachment and as a sampled image, as well as + // transfer source (but the reason of that is guest output capturing). + VkImage image() const { return image_; } + VkImageView image_view() const { return image_view_; } + uint64_t image_version() const { return image_version_; } + // Whether a proper barrier must be done to acquire the image. + bool image_ever_written_previously() const { + return image_ever_written_previously_; + } + + private: + VkImage image_; + VkImageView image_view_; + uint64_t image_version_; + bool image_ever_written_previously_; + }; + + static std::unique_ptr Create( + HostGpuLossCallback host_gpu_loss_callback, VulkanProvider& provider) { + auto presenter = std::unique_ptr( + new VulkanPresenter(host_gpu_loss_callback, provider)); + if (!presenter->InitializeSurfaceIndependent()) { + return nullptr; + } + return presenter; + } + + ~VulkanPresenter(); + + VulkanProvider& provider() const { return provider_; } + + static Surface::TypeFlags GetSurfaceTypesSupportedByInstance( + const VulkanProvider::InstanceExtensions& instance_extensions) { + if (!instance_extensions.khr_surface) { + return 0; + } + Surface::TypeFlags type_flags = 0; +#if XE_PLATFORM_ANDROID + if (instance_extensions.khr_android_surface) { + type_flags |= Surface::kTypeFlag_AndroidNativeWindow; + } +#elif XE_PLATFORM_GNU_LINUX + if (instance_extensions.khr_xcb_surface) { + type_flags |= Surface::kTypeFlag_XcbWindow; + } +#elif XE_PLATFORM_WIN32 + if (instance_extensions.khr_win32_surface) { + type_flags |= Surface::kTypeFlag_Win32Hwnd; + } +#endif + return type_flags; + } + Surface::TypeFlags GetSupportedSurfaceTypes() const override; + + bool CaptureGuestOutput(RawImage& image_out) override; + + void AwaitUISubmissionCompletionFromUIThread(uint64_t submission_index) { + ui_submission_tracker_.AwaitSubmissionCompletion(submission_index); + } + VkCommandBuffer AcquireUISetupCommandBufferFromUIThread(); + + protected: + SurfacePaintConnectResult ConnectOrReconnectPaintingToSurfaceFromUIThread( + Surface& new_surface, uint32_t new_surface_width, + uint32_t new_surface_height, bool was_paintable, + bool& is_vsync_implicit_out) override; + void DisconnectPaintingFromSurfaceFromUIThreadImpl() override; + + bool RefreshGuestOutputImpl( + uint32_t mailbox_index, uint32_t frontbuffer_width, + uint32_t frontbuffer_height, + std::function refresher, + bool& is_8bpc_out_ref) override; + + PaintResult PaintAndPresentImpl(bool execute_ui_drawers) override; + + private: + // Usable for both the guest output image itself and for intermediate images. + class GuestOutputImage { + public: + static std::unique_ptr Create( + const VulkanProvider& provider, uint32_t width, uint32_t height) { + assert_not_zero(width); + assert_not_zero(height); + auto image = std::unique_ptr( + new GuestOutputImage(provider, width, height)); + if (!image->Initialize()) { + return nullptr; + } + return std::move(image); + } + + GuestOutputImage(const GuestOutputImage& image) = delete; + GuestOutputImage& operator=(const GuestOutputImage& image) = delete; + ~GuestOutputImage(); + + const VkExtent2D& extent() const { return extent_; } + + VkImage image() const { return image_; } + VkDeviceMemory memory() const { return memory_; } + VkImageView view() const { return view_; } + + private: + GuestOutputImage(const VulkanProvider& provider, uint32_t width, + uint32_t height) + : provider_(provider) { + extent_.width = width; + extent_.height = height; + } + + bool Initialize(); + + const VulkanProvider& provider_; + + VkExtent2D extent_; + VkImage image_ = VK_NULL_HANDLE; + VkDeviceMemory memory_ = VK_NULL_HANDLE; + VkImageView view_ = VK_NULL_HANDLE; + }; + + struct GuestOutputImageInstance { + // Refresher-side reference (painting has its own references for the purpose + // of destruction only after painting is done on the GPU). + std::shared_ptr image; + uint64_t version = UINT64_MAX; + uint64_t last_refresher_submission = 0; + // For choosing the barrier stage and access mask and layout depending on + // whether the image has previously been written. If an image is active + // after a refresh, it can be assumed that this is true. + bool ever_successfully_refreshed = false; + + void SetToNewImage(const std::shared_ptr& new_image, + uint64_t new_version) { + image = new_image; + version = new_version; + last_refresher_submission = 0; + ever_successfully_refreshed = false; + } + }; + + struct GuestOutputPaintRectangleConstants { + union { + struct { + float x; + float y; + }; + float offset[2]; + }; + union { + struct { + float width; + float height; + }; + float size[2]; + }; + }; + + enum GuestOutputPaintPipelineLayoutIndex : size_t { + kGuestOutputPaintPipelineLayoutIndexBilinear, + kGuestOutputPaintPipelineLayoutIndexCasSharpen, + kGuestOutputPaintPipelineLayoutIndexCasResample, + kGuestOutputPaintPipelineLayoutIndexFsrEasu, + kGuestOutputPaintPipelineLayoutIndexFsrRcas, + + kGuestOutputPaintPipelineLayoutCount, + }; + + static constexpr GuestOutputPaintPipelineLayoutIndex + GetGuestOutputPaintPipelineLayoutIndex(GuestOutputPaintEffect effect) { + switch (effect) { + case GuestOutputPaintEffect::kBilinear: + case GuestOutputPaintEffect::kBilinearDither: + return kGuestOutputPaintPipelineLayoutIndexBilinear; + case GuestOutputPaintEffect::kCasSharpen: + case GuestOutputPaintEffect::kCasSharpenDither: + return kGuestOutputPaintPipelineLayoutIndexCasSharpen; + case GuestOutputPaintEffect::kCasResample: + case GuestOutputPaintEffect::kCasResampleDither: + return kGuestOutputPaintPipelineLayoutIndexCasResample; + case GuestOutputPaintEffect::kFsrEasu: + return kGuestOutputPaintPipelineLayoutIndexFsrEasu; + case GuestOutputPaintEffect::kFsrRcas: + case GuestOutputPaintEffect::kFsrRcasDither: + return kGuestOutputPaintPipelineLayoutIndexFsrRcas; + default: + assert_unhandled_case(effect); + return kGuestOutputPaintPipelineLayoutCount; + } + } + + struct PaintContext { + class Submission { + public: + static std::unique_ptr Create( + const VulkanProvider& provider) { + auto submission = std::unique_ptr(new Submission(provider)); + if (!submission->Initialize()) { + return nullptr; + } + return submission; + } + + Submission(const Submission& submission) = delete; + Submission& operator=(const Submission& submission) = delete; + ~Submission(); + + VkSemaphore acquire_semaphore() const { return acquire_semaphore_; } + VkSemaphore present_semaphore() const { return present_semaphore_; } + VkCommandPool draw_command_pool() const { return draw_command_pool_; } + VkCommandBuffer draw_command_buffer() const { + return draw_command_buffer_; + } + + private: + explicit Submission(const VulkanProvider& provider) + : provider_(provider) {} + bool Initialize(); + + const VulkanProvider& provider_; + VkSemaphore acquire_semaphore_ = VK_NULL_HANDLE; + VkSemaphore present_semaphore_ = VK_NULL_HANDLE; + VkCommandPool draw_command_pool_ = VK_NULL_HANDLE; + VkCommandBuffer draw_command_buffer_ = VK_NULL_HANDLE; + }; + + static constexpr uint32_t kSubmissionCount = 3; + + struct GuestOutputPaintPipeline { + // Created on initialization. + VkPipeline intermediate_pipeline = VK_NULL_HANDLE; + // Created during guest output painting (after awaiting the last guest + // output paint if outdated and needs to be recreated), when needed, for + // the up-to-date render pass that draws to the swapchain with the actual + // image format. + VkPipeline swapchain_pipeline = VK_NULL_HANDLE; + VkFormat swapchain_format = VK_FORMAT_UNDEFINED; + }; + + enum GuestOutputDescriptorSet : uint32_t { + kGuestOutputDescriptorSetGuestOutput0Sampled, + + kGuestOutputDescriptorSetIntermediate0Sampled = + kGuestOutputDescriptorSetGuestOutput0Sampled + + kGuestOutputMailboxSize, + + kGuestOutputDescriptorSetCount = + kGuestOutputDescriptorSetIntermediate0Sampled + + kMaxGuestOutputPaintEffects - 1, + }; + + struct UISetupCommandBuffer { + UISetupCommandBuffer(VkCommandPool command_pool, + VkCommandBuffer command_buffer, + uint64_t last_usage_submission_index = 0) + : command_pool(command_pool), + command_buffer(command_buffer), + last_usage_submission_index(last_usage_submission_index) {} + + VkCommandPool command_pool; + VkCommandBuffer command_buffer; + uint64_t last_usage_submission_index; + }; + + struct SwapchainFramebuffer { + SwapchainFramebuffer(VkImageView image_view, VkFramebuffer framebuffer) + : image_view(image_view), framebuffer(framebuffer) {} + + VkImageView image_view; + VkFramebuffer framebuffer; + }; + + explicit PaintContext(VulkanProvider& provider) + : provider(provider), submission_tracker(provider) {} + PaintContext(const PaintContext& paint_context) = delete; + PaintContext& operator=(const PaintContext& paint_context) = delete; + + // The old swapchain, if passed, should be assumed to be retired after this + // call (though it may fail before the vkCreateSwapchainKHR that will + // technically retire it, so it will be in an undefined state), and needs to + // be destroyed externally no matter what the result is. + static VkSwapchainKHR CreateSwapchainForVulkanSurface( + const VulkanProvider& provider, VkSurfaceKHR surface, uint32_t width, + uint32_t height, VkSwapchainKHR old_swapchain, + uint32_t& present_queue_family_out, VkFormat& image_format_out, + VkExtent2D& image_extent_out, bool& is_fifo_out, + bool& ui_surface_unusable_out); + + // Destroys the swapchain and its derivatives, nulls `swapchain` and returns + // the original swapchain object, if it existed, for use as oldSwapchain if + // needed and for destruction. + VkSwapchainKHR PrepareForSwapchainRetirement(); + // May be called from the destructor of the presenter. + void DestroySwapchainAndVulkanSurface(); + + // Connection-indepedent. + + const VulkanProvider& provider; + + std::array, kSubmissionCount> + submissions; + VulkanSubmissionTracker submission_tracker; + + std::array + guest_output_paint_pipelines; + + VkDescriptorPool guest_output_descriptor_pool = VK_NULL_HANDLE; + // Descriptors are updated while painting if they're out of date. + VkDescriptorSet + guest_output_descriptor_sets[kGuestOutputDescriptorSetCount]; + + // Refreshed and cleaned up during guest output painting. The first is the + // paint submission index in which the guest output image (and its + // descriptors) was last used, the second is the reference to the image, + // which may be null. The indices are not mailbox indices here, rather, if + // the reference is not in this array yet, the most outdated reference, if + // needed, is replaced with the new one, awaiting the usage completion of + // the last paint usage. + std::array>, + kGuestOutputMailboxSize> + guest_output_image_paint_refs; + // The latest submission index at which any guest output image was drawn. + uint64_t guest_output_image_paint_last_submission = 0; + + // Current intermediate images for guest output painting, refreshed when + // painting guest output. + std::array, + kMaxGuestOutputPaintEffects - 1> + guest_output_intermediate_images; + // Created and destroyed alongside the images. UNORM only. + std::array + guest_output_intermediate_framebuffers = {}; + uint64_t guest_output_intermediate_image_last_submission = 0; + + // Command buffers optionally executed before the draw command buffer, + // outside the painting render pass. + std::vector ui_setup_command_buffers; + size_t ui_setup_command_buffer_current_index = SIZE_MAX; + + // Connection-specific. + + // May be reused between connections if the format stays the same. + VkRenderPass swapchain_render_pass = VK_NULL_HANDLE; + VkFormat swapchain_render_pass_format = VK_FORMAT_UNDEFINED; + bool swapchain_render_pass_clear_load_op = false; + + VkSurfaceKHR vulkan_surface = VK_NULL_HANDLE; + uint32_t present_queue_family = UINT32_MAX; + VkSwapchainKHR swapchain = VK_NULL_HANDLE; + VkExtent2D swapchain_extent = {}; + bool swapchain_is_fifo = false; + std::vector swapchain_images; + std::vector swapchain_framebuffers; + }; + + explicit VulkanPresenter(HostGpuLossCallback host_gpu_loss_callback, + VulkanProvider& provider) + : Presenter(host_gpu_loss_callback), + provider_(provider), + guest_output_image_refresher_submission_tracker_(provider), + ui_submission_tracker_(provider), + paint_context_(provider) {} + + bool InitializeSurfaceIndependent(); + + [[nodiscard]] VkPipeline CreateGuestOutputPaintPipeline( + GuestOutputPaintEffect effect, VkRenderPass render_pass); + + VulkanProvider& provider_; + + // Static objects for guest output presentation, used only when painting the + // main target (can be destroyed only after awaiting main target usage + // completion). + VkDescriptorSetLayout guest_output_paint_image_descriptor_set_layout_ = + VK_NULL_HANDLE; + std::array + guest_output_paint_pipeline_layouts_ = {}; + VkShaderModule guest_output_paint_vs_ = VK_NULL_HANDLE; + std::array + guest_output_paint_fs_ = {}; + // Not compatible with the swapchain render pass even if the format is the + // same due to different dependencies (this is shader read > color + // attachment > shader read). + VkRenderPass guest_output_intermediate_render_pass_ = VK_NULL_HANDLE; + + // Value monotonically increased every time a new guest output image is + // initialized, for recreation of dependent objects such as framebuffers in + // the refreshers - saving and comparing the handle in the refresher is not + // enough as Create > Destroy > Create may result in the same handle for + // actually different objects without the refresher being aware of the + // destruction. + uint64_t guest_output_image_next_version_ = 0; + std::array + guest_output_images_; + VulkanSubmissionTracker guest_output_image_refresher_submission_tracker_; + + // UI submission tracker with the submission index that can be given to UI + // drawers (accessible from the UI thread only, at any time). + VulkanSubmissionTracker ui_submission_tracker_; + + // Accessible only by painting and by surface connection lifetime management + // (ConnectOrReconnectPaintingToSurfaceFromUIThread, + // DisconnectPaintingFromSurfaceFromUIThreadImpl) by the thread doing it, as + // well as by presenter initialization and shutdown. + PaintContext paint_context_; +}; + +} // namespace vulkan +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_D3D12_D3D12_PRESENTER_H_ diff --git a/src/xenia/ui/vulkan/vulkan_provider.cc b/src/xenia/ui/vulkan/vulkan_provider.cc index d67a5e263..5b1ef5794 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.cc +++ b/src/xenia/ui/vulkan/vulkan_provider.cc @@ -2,36 +2,74 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/ui/vulkan/vulkan_provider.h" -#include +#include +#include +#include +#include "xenia/base/assert.h" +#include "xenia/base/cvar.h" #include "xenia/base/logging.h" -#include "xenia/ui/vulkan/vulkan_context.h" -#include "xenia/ui/vulkan/vulkan_device.h" -#include "xenia/ui/vulkan/vulkan_instance.h" -#include "xenia/ui/vulkan/vulkan_util.h" +#include "xenia/base/math.h" +#include "xenia/base/platform.h" +#include "xenia/ui/vulkan/vulkan_immediate_drawer.h" +#include "xenia/ui/vulkan/vulkan_presenter.h" -DEFINE_uint64(vulkan_device_index, 0, "Index of the physical device to use.", - "Vulkan"); +#if XE_PLATFORM_LINUX +#include +#elif XE_PLATFORM_WIN32 +#include "xenia/base/platform_win.h" +#endif + +// Implement AMD's VMA here. +#define VMA_IMPLEMENTATION +#include "xenia/ui/vulkan/vulkan_mem_alloc.h" + +// TODO(Triang3l): Disable Vulkan validation before releasing a stable version. +DEFINE_bool( + vulkan_validation, true, + "Enable Vulkan validation (VK_LAYER_KHRONOS_validation). Messages will be " + "written to the OS debug log without vulkan_debug_messenger or to the " + "Xenia log with it.", + "Vulkan"); +DEFINE_bool( + vulkan_debug_utils_messenger, false, + "Enable writing Vulkan debug messages via VK_EXT_debug_utils to the Xenia " + "log.", + "Vulkan"); +DEFINE_uint32( + vulkan_debug_utils_messenger_severity, 2, + "Maximum severity of messages to log via the Vulkan debug messenger: 0 - " + "error, 1 - warning, 2 - info, 3 - verbose.", + "Vulkan"); +DEFINE_bool(vulkan_debug_utils_names, false, + "Enable naming Vulkan objects via VK_EXT_debug_utils.", "Vulkan"); +DEFINE_int32( + vulkan_device, -1, + "Index of the physical device to use, or -1 for any compatible device.", + "Vulkan"); namespace xe { namespace ui { namespace vulkan { -std::unique_ptr VulkanProvider::Create() { - std::unique_ptr provider(new VulkanProvider); +std::unique_ptr VulkanProvider::Create( + bool is_surface_required) { + std::unique_ptr provider( + new VulkanProvider(is_surface_required)); if (!provider->Initialize()) { xe::FatalError( "Unable to initialize Vulkan graphics subsystem.\n" "\n" - "Ensure you have the latest drivers for your GPU and that it " - "supports Vulkan.\n" + "Ensure that you have the latest drivers for your GPU and it supports " + "Vulkan, and that you have the latest Vulkan runtime installed, which " + "can be downloaded at https://vulkan.lunarg.com/sdk/home.\n" "\n" "See https://xenia.jp/faq/ for more information and a list of " "supported GPUs."); @@ -41,71 +79,1078 @@ std::unique_ptr VulkanProvider::Create() { } VulkanProvider::~VulkanProvider() { - device_.reset(); - instance_.reset(); + for (size_t i = 0; i < size_t(HostSampler::kCount); ++i) { + if (host_samplers_[i] != VK_NULL_HANDLE) { + dfn_.vkDestroySampler(device_, host_samplers_[i], nullptr); + } + } + + if (device_ != VK_NULL_HANDLE) { + ifn_.vkDestroyDevice(device_, nullptr); + } + if (instance_ != VK_NULL_HANDLE) { + if (debug_messenger_ != VK_NULL_HANDLE) { + ifn_.vkDestroyDebugUtilsMessengerEXT(instance_, debug_messenger_, + nullptr); + } + lfn_.vkDestroyInstance(instance_, nullptr); + } + +#if XE_PLATFORM_LINUX + if (library_) { + dlclose(library_); + } +#elif XE_PLATFORM_WIN32 + if (library_) { + FreeLibrary(library_); + } +#endif } bool VulkanProvider::Initialize() { - instance_ = std::make_unique(); + renderdoc_api_.Initialize(); - // Always enable the swapchain. -#if XE_PLATFORM_GNU_LINUX || XE_PLATFORM_WIN32 - instance_->DeclareRequiredExtension("VK_KHR_surface", Version::Make(0, 0, 0), - false); -#if XE_PLATFORM_GNU_LINUX - instance_->DeclareRequiredExtension("VK_KHR_xcb_surface", - Version::Make(0, 0, 0), false); + // Load the library. + bool library_functions_loaded = true; +#if XE_PLATFORM_LINUX +#if XE_PLATFORM_ANDROID + const char* libvulkan_name = "libvulkan.so"; +#else + const char* libvulkan_name = "libvulkan.so.1"; +#endif + // http://developer.download.nvidia.com/mobile/shield/assets/Vulkan/UsingtheVulkanAPI.pdf + library_ = dlopen(libvulkan_name, RTLD_NOW | RTLD_LOCAL); + if (!library_) { + XELOGE("Failed to load {}", libvulkan_name); + return false; + } +#define XE_VULKAN_LOAD_MODULE_LFN(name) \ + library_functions_loaded &= \ + (lfn_.name = PFN_##name(dlsym(library_, #name))) != nullptr; #elif XE_PLATFORM_WIN32 - instance_->DeclareRequiredExtension("VK_KHR_win32_surface", - Version::Make(0, 0, 0), false); -#endif + library_ = LoadLibraryA("vulkan-1.dll"); + if (!library_) { + XELOGE("Failed to load vulkan-1.dll"); + return false; + } +#define XE_VULKAN_LOAD_MODULE_LFN(name) \ + library_functions_loaded &= \ + (lfn_.name = PFN_##name(GetProcAddress(library_, #name))) != nullptr; +#else +#error No Vulkan library loading provided for the target platform. #endif + XE_VULKAN_LOAD_MODULE_LFN(vkGetInstanceProcAddr); + XE_VULKAN_LOAD_MODULE_LFN(vkDestroyInstance); +#undef XE_VULKAN_LOAD_MODULE_LFN + if (!library_functions_loaded) { + XELOGE("Failed to get Vulkan library function pointers"); + return false; + } + library_functions_loaded &= + (lfn_.vkCreateInstance = PFN_vkCreateInstance(lfn_.vkGetInstanceProcAddr( + VK_NULL_HANDLE, "vkCreateInstance"))) != nullptr; + library_functions_loaded &= + (lfn_.vkEnumerateInstanceExtensionProperties = + PFN_vkEnumerateInstanceExtensionProperties( + lfn_.vkGetInstanceProcAddr( + VK_NULL_HANDLE, + "vkEnumerateInstanceExtensionProperties"))) != nullptr; + library_functions_loaded &= + (lfn_.vkEnumerateInstanceLayerProperties = + PFN_vkEnumerateInstanceLayerProperties(lfn_.vkGetInstanceProcAddr( + VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"))) != + nullptr; + if (!library_functions_loaded) { + XELOGE( + "Failed to get Vulkan library function pointers via " + "vkGetInstanceProcAddr"); + return false; + } + lfn_.v_1_1.vkEnumerateInstanceVersion = PFN_vkEnumerateInstanceVersion( + lfn_.vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion")); - // Attempt initialization and device query. - if (!instance_->Initialize()) { - XELOGE("Failed to initialize vulkan instance"); + // Get the API version. + uint32_t instance_api_version; + if (!lfn_.v_1_1.vkEnumerateInstanceVersion || + lfn_.v_1_1.vkEnumerateInstanceVersion(&instance_api_version) != + VK_SUCCESS) { + instance_api_version = VK_API_VERSION_1_0; + } + XELOGVK("Vulkan instance version: {}.{}.{}", + VK_VERSION_MAJOR(instance_api_version), + VK_VERSION_MINOR(instance_api_version), + VK_VERSION_PATCH(instance_api_version)); + + // Get the instance extensions without layers, as well as extensions promoted + // to the core. + bool debug_utils_messenger_requested = cvars::vulkan_debug_utils_messenger; + bool debug_utils_names_requested = cvars::vulkan_debug_utils_names; + bool debug_utils_requested = + debug_utils_messenger_requested || debug_utils_names_requested; + std::memset(&instance_extensions_, 0, sizeof(instance_extensions_)); + if (instance_api_version >= VK_MAKE_API_VERSION(0, 1, 1, 0)) { + instance_extensions_.khr_get_physical_device_properties2 = true; + } + std::vector instance_extensions_enabled; + std::vector instance_or_layer_extension_properties; + VkResult instance_extensions_enumerate_result; + for (;;) { + uint32_t instance_extension_count = + uint32_t(instance_or_layer_extension_properties.size()); + bool instance_extensions_were_empty = !instance_extension_count; + instance_extensions_enumerate_result = + lfn_.vkEnumerateInstanceExtensionProperties( + nullptr, &instance_extension_count, + instance_extensions_were_empty + ? nullptr + : instance_or_layer_extension_properties.data()); + // If the original extension count was 0 (first call), SUCCESS is returned, + // not INCOMPLETE. + if (instance_extensions_enumerate_result == VK_SUCCESS || + instance_extensions_enumerate_result == VK_INCOMPLETE) { + instance_or_layer_extension_properties.resize(instance_extension_count); + if (instance_extensions_enumerate_result == VK_SUCCESS && + (!instance_extensions_were_empty || !instance_extension_count)) { + break; + } + } else { + break; + } + } + if (instance_extensions_enumerate_result == VK_SUCCESS) { + AccumulateInstanceExtensions(instance_or_layer_extension_properties.size(), + instance_or_layer_extension_properties.data(), + debug_utils_requested, instance_extensions_, + instance_extensions_enabled); + } + size_t instance_extensions_enabled_count_without_layers = + instance_extensions_enabled.size(); + InstanceExtensions instance_extensions_without_layers = instance_extensions_; + + // Get the instance layers and their extensions. + std::vector layer_properties; + VkResult layers_enumerate_result; + for (;;) { + uint32_t layer_count = uint32_t(layer_properties.size()); + bool layers_were_empty = !layer_count; + layers_enumerate_result = lfn_.vkEnumerateInstanceLayerProperties( + &layer_count, layers_were_empty ? nullptr : layer_properties.data()); + // If the original layer count was 0 (first call), SUCCESS is returned, not + // INCOMPLETE. + if (layers_enumerate_result == VK_SUCCESS || + layers_enumerate_result == VK_INCOMPLETE) { + layer_properties.resize(layer_count); + if (layers_enumerate_result == VK_SUCCESS && + (!layers_were_empty || !layer_count)) { + break; + } + } else { + break; + } + } + if (layers_enumerate_result != VK_SUCCESS) { + layer_properties.clear(); + } + struct { + bool khronos_validation; + } layer_enabled_flags = {}; + std::vector layers_enabled; + for (const VkLayerProperties& layer : layer_properties) { + // Check if the layer is needed. + // Checking if already enabled as an optimization to do fewer and fewer + // string comparisons. Adding literals to layers_enabled for the most C + // string lifetime safety. + if (!layer_enabled_flags.khronos_validation && cvars::vulkan_validation && + !std::strcmp(layer.layerName, "VK_LAYER_KHRONOS_validation")) { + layers_enabled.push_back("VK_LAYER_KHRONOS_validation"); + layer_enabled_flags.khronos_validation = true; + } else { + // Not enabling this layer, so don't need the extensions from it as well. + continue; + } + // Load extensions from the layer. + instance_or_layer_extension_properties.clear(); + for (;;) { + uint32_t instance_extension_count = + uint32_t(instance_or_layer_extension_properties.size()); + bool instance_extensions_were_empty = !instance_extension_count; + instance_extensions_enumerate_result = + lfn_.vkEnumerateInstanceExtensionProperties( + layer.layerName, &instance_extension_count, + instance_extensions_were_empty + ? nullptr + : instance_or_layer_extension_properties.data()); + // If the original extension count was 0 (first call), SUCCESS is + // returned, not INCOMPLETE. + if (instance_extensions_enumerate_result == VK_SUCCESS || + instance_extensions_enumerate_result == VK_INCOMPLETE) { + instance_or_layer_extension_properties.resize(instance_extension_count); + if (instance_extensions_enumerate_result == VK_SUCCESS && + (!instance_extensions_were_empty || !instance_extension_count)) { + break; + } + } else { + break; + } + } + if (instance_extensions_enumerate_result == VK_SUCCESS) { + AccumulateInstanceExtensions( + instance_or_layer_extension_properties.size(), + instance_or_layer_extension_properties.data(), debug_utils_requested, + instance_extensions_, instance_extensions_enabled); + } + } + + // Create the instance. + VkApplicationInfo application_info; + application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + application_info.pNext = nullptr; + application_info.pApplicationName = "Xenia"; + application_info.applicationVersion = 1; + application_info.pEngineName = nullptr; + application_info.engineVersion = 0; + // "apiVersion must be the highest version of Vulkan that the application is + // designed to use" + // "Vulkan 1.0 implementations were required to return + // VK_ERROR_INCOMPATIBLE_DRIVER if apiVersion was larger than 1.0" + application_info.apiVersion = + instance_api_version >= VK_MAKE_API_VERSION(0, 1, 1, 0) + ? VK_HEADER_VERSION_COMPLETE + : instance_api_version; + VkInstanceCreateInfo instance_create_info; + instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_create_info.pNext = nullptr; + instance_create_info.flags = 0; + instance_create_info.pApplicationInfo = &application_info; + instance_create_info.enabledLayerCount = uint32_t(layers_enabled.size()); + instance_create_info.ppEnabledLayerNames = layers_enabled.data(); + instance_create_info.enabledExtensionCount = + uint32_t(instance_extensions_enabled.size()); + instance_create_info.ppEnabledExtensionNames = + instance_extensions_enabled.data(); + VkResult instance_create_result = + lfn_.vkCreateInstance(&instance_create_info, nullptr, &instance_); + if (instance_create_result != VK_SUCCESS) { + if ((instance_create_result == VK_ERROR_LAYER_NOT_PRESENT || + instance_create_result == VK_ERROR_EXTENSION_NOT_PRESENT) && + !layers_enabled.empty()) { + XELOGE("Failed to enable Vulkan layers"); + // Try to create without layers and their extensions. + std::memset(&layer_enabled_flags, 0, sizeof(layer_enabled_flags)); + instance_create_info.enabledLayerCount = 0; + instance_create_info.ppEnabledLayerNames = nullptr; + instance_create_info.enabledExtensionCount = + uint32_t(instance_extensions_enabled_count_without_layers); + instance_extensions_ = instance_extensions_without_layers; + instance_create_result = + lfn_.vkCreateInstance(&instance_create_info, nullptr, &instance_); + } + if (instance_create_result != VK_SUCCESS) { + XELOGE("Failed to create a Vulkan instance"); + return false; + } + } + + // Get instance functions. + std::memset(&ifn_, 0, sizeof(ifn_)); +#define XE_UI_VULKAN_FUNCTION(name) \ + functions_loaded &= (ifn_.name = PFN_##name(lfn_.vkGetInstanceProcAddr( \ + instance_, #name))) != nullptr; +#define XE_UI_VULKAN_FUNCTION_DONT_PROMOTE(extension_name, core_name) \ + functions_loaded &= \ + (ifn_.extension_name = PFN_##extension_name(lfn_.vkGetInstanceProcAddr( \ + instance_, #extension_name))) != nullptr; +#define XE_UI_VULKAN_FUNCTION_PROMOTE(extension_name, core_name) \ + functions_loaded &= \ + (ifn_.extension_name = PFN_##extension_name( \ + lfn_.vkGetInstanceProcAddr(instance_, #core_name))) != nullptr; + // Core - require unconditionally. + { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/instance_1_0.inc" + if (!functions_loaded) { + XELOGE("Failed to get Vulkan instance function pointers"); + return false; + } + } + // Extensions - disable the specific extension if failed to get its functions. + if (instance_extensions_.ext_debug_utils) { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/instance_ext_debug_utils.inc" + instance_extensions_.ext_debug_utils = functions_loaded; + } + if (instance_extensions_.khr_get_physical_device_properties2) { + bool functions_loaded = true; + if (instance_api_version >= VK_MAKE_API_VERSION(0, 1, 1, 0)) { +#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_PROMOTE +#include "xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc" +#undef XE_UI_VULKAN_FUNCTION_PROMOTED + } else { +#define XE_UI_VULKAN_FUNCTION_PROMOTED XE_UI_VULKAN_FUNCTION_DONT_PROMOTE +#include "xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc" +#undef XE_UI_VULKAN_FUNCTION_PROMOTED + } + instance_extensions_.khr_get_physical_device_properties2 = functions_loaded; + } + if (instance_extensions_.khr_surface) { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/instance_khr_surface.inc" + instance_extensions_.khr_surface = functions_loaded; + } +#if XE_PLATFORM_ANDROID + if (instance_extensions_.khr_android_surface) { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc" + instance_extensions_.khr_android_surface = functions_loaded; + } +#elif XE_PLATFORM_GNU_LINUX + if (instance_extensions_.khr_xcb_surface) { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc" + instance_extensions_.khr_xcb_surface = functions_loaded; + } +#elif XE_PLATFORM_WIN32 + if (instance_extensions_.khr_win32_surface) { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc" + instance_extensions_.khr_win32_surface = functions_loaded; + } +#endif // XE_PLATFORM +#undef XE_UI_VULKAN_FUNCTION_PROMOTE +#undef XE_UI_VULKAN_FUNCTION_DONT_PROMOTE +#undef XE_UI_VULKAN_FUNCTION + + // Check if surface is supported after verifying that surface extension + // function pointers could be obtained. + if (is_surface_required_ && + !VulkanPresenter::GetSurfaceTypesSupportedByInstance( + instance_extensions_)) { + XELOGE( + "The Vulkan instance doesn't support the required surface extension " + "for the platform"); return false; } - // Pick the device to use. - auto available_devices = instance_->available_devices(); - if (available_devices.empty()) { - XELOGE("No devices available for use"); + // Report instance information after verifying that extension function + // pointers could be obtained. + XELOGVK("Vulkan layers enabled by Xenia:"); + XELOGVK("* VK_LAYER_KHRONOS_validation: {}", + layer_enabled_flags.khronos_validation ? "yes" : "no"); + XELOGVK("Vulkan instance extensions:"); + XELOGVK("* VK_EXT_debug_utils: {}", + instance_extensions_.ext_debug_utils + ? "yes" + : (debug_utils_requested ? "no" : "not requested")); + XELOGVK( + "* VK_KHR_get_physical_device_properties2: {}", + instance_extensions_.khr_get_physical_device_properties2 ? "yes" : "no"); + XELOGVK("* VK_KHR_surface: {}", + instance_extensions_.khr_surface ? "yes" : "no"); +#if XE_PLATFORM_ANDROID + XELOGVK(" * VK_KHR_android_surface: {}", + instance_extensions_.khr_android_surface ? "yes" : "no"); +#elif XE_PLATFORM_GNU_LINUX + XELOGVK(" * VK_KHR_xcb_surface: {}", + instance_extensions_.khr_xcb_surface ? "yes" : "no"); +#elif XE_PLATFORM_WIN32 + XELOGVK(" * VK_KHR_win32_surface: {}", + instance_extensions_.khr_win32_surface ? "yes" : "no"); +#endif + + // Enable the debug messenger. + if (debug_utils_messenger_requested) { + if (instance_extensions_.ext_debug_utils) { + VkDebugUtilsMessengerCreateInfoEXT debug_messenger_create_info; + debug_messenger_create_info.sType = + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + debug_messenger_create_info.pNext = nullptr; + debug_messenger_create_info.flags = 0; + debug_messenger_create_info.messageSeverity = + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + if (cvars::vulkan_debug_utils_messenger_severity >= 1) { + debug_messenger_create_info.messageSeverity |= + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + if (cvars::vulkan_debug_utils_messenger_severity >= 2) { + debug_messenger_create_info.messageSeverity |= + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + if (cvars::vulkan_debug_utils_messenger_severity >= 3) { + debug_messenger_create_info.messageSeverity |= + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + } + } + } + debug_messenger_create_info.messageType = + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + debug_messenger_create_info.pfnUserCallback = DebugMessengerCallback; + debug_messenger_create_info.pUserData = this; + ifn_.vkCreateDebugUtilsMessengerEXT( + instance_, &debug_messenger_create_info, nullptr, &debug_messenger_); + } + if (debug_messenger_ != VK_NULL_HANDLE) { + XELOGVK("Vulkan debug messenger enabled"); + } else { + XELOGE("Failed to enable the Vulkan debug messenger"); + } + } + debug_names_used_ = + debug_utils_names_requested && instance_extensions_.ext_debug_utils; + + // Get the compatible physical device. + std::vector physical_devices; + for (;;) { + uint32_t physical_device_count = uint32_t(physical_devices.size()); + bool physical_devices_were_empty = !physical_device_count; + VkResult physical_device_enumerate_result = ifn_.vkEnumeratePhysicalDevices( + instance_, &physical_device_count, + physical_devices_were_empty ? nullptr : physical_devices.data()); + // If the original device count was 0 (first call), SUCCESS is returned, not + // INCOMPLETE. + if (physical_device_enumerate_result == VK_SUCCESS || + physical_device_enumerate_result == VK_INCOMPLETE) { + physical_devices.resize(physical_device_count); + if (physical_device_enumerate_result == VK_SUCCESS && + (!physical_devices_were_empty || !physical_device_count)) { + break; + } + } else { + XELOGE("Failed to enumerate Vulkan physical devices"); + return false; + } + } + if (physical_devices.empty()) { + XELOGE("No Vulkan physical devices are available"); return false; } - size_t device_index = - std::min(available_devices.size(), cvars::vulkan_device_index); - auto& device_info = available_devices[device_index]; + size_t physical_device_index_first, physical_device_index_last; + if (cvars::vulkan_device >= 0) { + physical_device_index_first = uint32_t(cvars::vulkan_device); + physical_device_index_last = physical_device_index_first; + if (physical_device_index_first >= physical_devices.size()) { + XELOGE( + "vulkan_device config variable is out of range, {} devices are " + "available", + physical_devices.size()); + return false; + } + } else { + physical_device_index_first = 0; + physical_device_index_last = physical_devices.size() - 1; + } + physical_device_ = VK_NULL_HANDLE; + std::vector queue_families_properties; + std::vector device_extension_properties; + std::vector device_extensions_enabled; + for (size_t i = physical_device_index_first; i <= physical_device_index_last; + ++i) { + VkPhysicalDevice physical_device_current = physical_devices[i]; + + // Get physical device features and check if the needed ones are supported. + // Need this before obtaining the queues as sparse binding is an optional + // feature. + ifn_.vkGetPhysicalDeviceFeatures(physical_device_current, + &device_features_); + // Passing indices directly from guest memory, where they are big-endian; a + // workaround using fetch from shared memory for 32-bit indices that need + // swapping isn't implemented yet. Not supported only Qualcomm Adreno 4xx. + if (!device_features_.fullDrawIndexUint32) { + continue; + } + // TODO(Triang3l): Make geometry shaders optional by providing compute + // shader fallback (though that would require vertex shader stores). + if (!device_features_.geometryShader) { + continue; + } + + // Get the needed queues: + // - Graphics and compute. + // - Sparse binding if used (preferably the same as the graphics and compute + // one for the lowest latency as Xenia submits sparse binding commands + // right before graphics commands anyway). + // - Additional queues for presentation as VulkanProvider may be used with + // different surfaces, and they may have varying support of presentation + // from different queue families. + uint32_t queue_family_count = 0; + ifn_.vkGetPhysicalDeviceQueueFamilyProperties(physical_device_current, + &queue_family_count, nullptr); + queue_families_properties.resize(queue_family_count); + ifn_.vkGetPhysicalDeviceQueueFamilyProperties( + physical_device_current, &queue_family_count, + queue_families_properties.data()); + assert_true(queue_family_count == queue_families_properties.size()); + // Initialize all queue families to unused. + queue_families_.clear(); + queue_families_.resize(queue_family_count); + // First, try to obtain a graphics and compute queue. Preferably find a + // queue with sparse binding support as well. + // The family indices here are listed from the best to the worst. + uint32_t queue_family_graphics_compute_sparse_binding = UINT32_MAX; + uint32_t queue_family_graphics_compute_only = UINT32_MAX; + for (uint32_t j = 0; j < queue_family_count; ++j) { + const VkQueueFamilyProperties& queue_family_properties = + queue_families_properties[j]; + if ((queue_family_properties.queueFlags & + (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) != + (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) { + continue; + } + uint32_t* queue_family_ptr; + if (device_features_.sparseBinding && + (queue_family_properties.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)) { + queue_family_ptr = &queue_family_graphics_compute_sparse_binding; + } else { + queue_family_ptr = &queue_family_graphics_compute_only; + } + if (*queue_family_ptr == UINT32_MAX) { + *queue_family_ptr = j; + } + } + if (queue_family_graphics_compute_sparse_binding != UINT32_MAX) { + assert_true(device_features_.sparseBinding); + queue_family_graphics_compute_ = + queue_family_graphics_compute_sparse_binding; + } else if (queue_family_graphics_compute_only != UINT32_MAX) { + queue_family_graphics_compute_ = queue_family_graphics_compute_only; + } else { + // No graphics and compute queue family. + continue; + } + // Mark the graphics and compute queue as requested. + queue_families_[queue_family_graphics_compute_].queue_count = + std::max(queue_families_[queue_family_graphics_compute_].queue_count, + uint32_t(1)); + // Request a separate sparse binding queue if needed. + queue_family_sparse_binding_ = UINT32_MAX; + if (device_features_.sparseBinding) { + if (queue_families_properties[queue_family_graphics_compute_].queueFlags & + VK_QUEUE_SPARSE_BINDING_BIT) { + queue_family_sparse_binding_ = queue_family_graphics_compute_; + } else { + for (uint32_t j = 0; j < queue_family_count; ++j) { + if (!(queue_families_properties[j].queueFlags & + VK_QUEUE_SPARSE_BINDING_BIT)) { + continue; + } + queue_family_sparse_binding_ = j; + queue_families_[j].queue_count = + std::max(queue_families_[j].queue_count, uint32_t(1)); + break; + } + } + // Don't expose, and disable during logical device creature, the sparse + // binding feature if failed to obtain a queue supporting it. + if (queue_family_sparse_binding_ == UINT32_MAX) { + device_features_.sparseBinding = VK_FALSE; + } + } + bool any_queue_potentially_supports_present = false; + if (instance_extensions_.khr_surface) { + // Request possible presentation queues. + for (uint32_t j = 0; j < queue_family_count; ++j) { +#if XE_PLATFORM_WIN32 + if (instance_extensions_.khr_win32_surface && + !ifn_.vkGetPhysicalDeviceWin32PresentationSupportKHR( + physical_device_current, j)) { + continue; + } +#endif + any_queue_potentially_supports_present = true; + QueueFamily& queue_family = queue_families_[j]; + queue_family.queue_count = + std::max(queue_families_[j].queue_count, uint32_t(1)); + queue_family.potentially_supports_present = true; + } + } + if (!any_queue_potentially_supports_present && is_surface_required_) { + continue; + } + + // Get device properties, will be needed to check if extensions have been + // promoted to core. + ifn_.vkGetPhysicalDeviceProperties(physical_device_current, + &device_properties_); + + // Get the extensions, check if swapchain is supported. + device_extension_properties.clear(); + VkResult device_extensions_enumerate_result; + for (;;) { + uint32_t device_extension_count = + uint32_t(device_extension_properties.size()); + bool device_extensions_were_empty = !device_extension_count; + device_extensions_enumerate_result = + ifn_.vkEnumerateDeviceExtensionProperties( + physical_device_current, nullptr, &device_extension_count, + device_extensions_were_empty + ? nullptr + : device_extension_properties.data()); + // If the original extension count was 0 (first call), SUCCESS is + // returned, not INCOMPLETE. + if (device_extensions_enumerate_result == VK_SUCCESS || + device_extensions_enumerate_result == VK_INCOMPLETE) { + device_extension_properties.resize(device_extension_count); + if (device_extensions_enumerate_result == VK_SUCCESS && + (!device_extensions_were_empty || !device_extension_count)) { + break; + } + } else { + break; + } + } + if (device_extensions_enumerate_result != VK_SUCCESS) { + continue; + } + std::memset(&device_extensions_, 0, sizeof(device_extensions_)); + if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) { + device_extensions_.khr_dedicated_allocation = true; + if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 2, 0)) { + device_extensions_.khr_image_format_list = true; + device_extensions_.khr_shader_float_controls = true; + device_extensions_.khr_spirv_1_4 = true; + } + } + device_extensions_enabled.clear(); + for (const VkExtensionProperties& device_extension : + device_extension_properties) { + const char* device_extension_name = device_extension.extensionName; + // Checking if already enabled as an optimization to do fewer and fewer + // string comparisons, as well as to skip adding extensions promoted to + // the core to device_extensions_enabled. Adding literals to + // device_extensions_enabled for the most C string lifetime safety. + if (!device_extensions_.amd_shader_info && + !std::strcmp(device_extension_name, "VK_AMD_shader_info")) { + device_extensions_enabled.push_back("VK_AMD_shader_info"); + device_extensions_.amd_shader_info = true; + } else if (!device_extensions_.ext_fragment_shader_interlock && + !std::strcmp(device_extension_name, + "VK_EXT_fragment_shader_interlock")) { + device_extensions_enabled.push_back("VK_EXT_fragment_shader_interlock"); + device_extensions_.ext_fragment_shader_interlock = true; + } else if (!device_extensions_.khr_dedicated_allocation && + !std::strcmp(device_extension_name, + "VK_KHR_dedicated_allocation")) { + device_extensions_enabled.push_back("VK_KHR_dedicated_allocation"); + device_extensions_.khr_dedicated_allocation = true; + } else if (!device_extensions_.khr_image_format_list && + !std::strcmp(device_extension_name, + "VK_KHR_image_format_list")) { + device_extensions_enabled.push_back("VK_KHR_image_format_list"); + device_extensions_.khr_image_format_list = true; + } else if (!device_extensions_.khr_shader_float_controls && + !std::strcmp(device_extension_name, + "VK_KHR_shader_float_controls")) { + device_extensions_enabled.push_back("VK_KHR_shader_float_controls"); + device_extensions_.khr_shader_float_controls = true; + } else if (!device_extensions_.khr_spirv_1_4 && + !std::strcmp(device_extension_name, "VK_KHR_spirv_1_4")) { + device_extensions_enabled.push_back("VK_KHR_spirv_1_4"); + device_extensions_.khr_spirv_1_4 = true; + } else if (!device_extensions_.khr_swapchain && + !std::strcmp(device_extension_name, "VK_KHR_swapchain")) { + device_extensions_enabled.push_back("VK_KHR_swapchain"); + device_extensions_.khr_swapchain = true; + } + } + if (is_surface_required_ && !device_extensions_.khr_swapchain) { + continue; + } + + // Get the memory types. + VkPhysicalDeviceMemoryProperties memory_properties; + ifn_.vkGetPhysicalDeviceMemoryProperties(physical_device_current, + &memory_properties); + memory_types_device_local_ = 0; + memory_types_host_visible_ = 0; + memory_types_host_coherent_ = 0; + memory_types_host_cached_ = 0; + for (uint32_t j = 0; j < memory_properties.memoryTypeCount; ++j) { + VkMemoryPropertyFlags memory_property_flags = + memory_properties.memoryTypes[j].propertyFlags; + uint32_t memory_type_bit = uint32_t(1) << j; + if (memory_property_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + memory_types_device_local_ |= memory_type_bit; + } + if (memory_property_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + memory_types_host_visible_ |= memory_type_bit; + } + if (memory_property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { + memory_types_host_coherent_ |= memory_type_bit; + } + if (memory_property_flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { + memory_types_host_cached_ |= memory_type_bit; + } + } + if (!memory_types_device_local_ && !memory_types_host_visible_) { + // Shouldn't happen according to the specification. + continue; + } + + physical_device_ = physical_device_current; + break; + } + if (physical_device_ == VK_NULL_HANDLE) { + XELOGE( + "Failed to get a compatible Vulkan physical device with swapchain " + "support"); + return false; + } + + // Get additional device properties. + std::memset(&device_float_controls_properties_, 0, + sizeof(device_float_controls_properties_)); + if (instance_extensions_.khr_get_physical_device_properties2) { + VkPhysicalDeviceProperties2KHR device_properties_2; + device_properties_2.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + device_properties_2.pNext = nullptr; + VkPhysicalDeviceProperties2KHR* device_properties_2_last = + &device_properties_2; + if (device_extensions_.khr_shader_float_controls) { + device_float_controls_properties_.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR; + device_float_controls_properties_.pNext = nullptr; + device_properties_2_last->pNext = &device_float_controls_properties_; + device_properties_2_last = + reinterpret_cast( + &device_float_controls_properties_); + } + if (device_properties_2_last != &device_properties_2) { + ifn_.vkGetPhysicalDeviceProperties2KHR(physical_device_, + &device_properties_2); + } + } // Create the device. - device_ = std::make_unique(instance_.get()); - device_->DeclareRequiredExtension("VK_KHR_swapchain", Version::Make(0, 0, 0), - false); - if (!device_->Initialize(device_info)) { - XELOGE("Unable to initialize device"); + std::vector queue_create_infos; + queue_create_infos.reserve(queue_families_.size()); + uint32_t used_queue_count = 0; + uint32_t max_queue_count_per_family = 0; + for (size_t i = 0; i < queue_families_.size(); ++i) { + QueueFamily& queue_family = queue_families_[i]; + queue_family.queue_first_index = used_queue_count; + if (!queue_family.queue_count) { + continue; + } + VkDeviceQueueCreateInfo& queue_create_info = + queue_create_infos.emplace_back(); + queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info.pNext = nullptr; + queue_create_info.flags = 0; + queue_create_info.queueFamilyIndex = uint32_t(i); + queue_create_info.queueCount = queue_family.queue_count; + // pQueuePriorities will be set later based on max_queue_count_per_family. + max_queue_count_per_family = + std::max(max_queue_count_per_family, queue_family.queue_count); + used_queue_count += queue_family.queue_count; + } + std::vector queue_priorities; + queue_priorities.resize(max_queue_count_per_family, 1.0f); + for (VkDeviceQueueCreateInfo& queue_create_info : queue_create_infos) { + queue_create_info.pQueuePriorities = queue_priorities.data(); + } + VkDeviceCreateInfo device_create_info; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_create_info.pNext = nullptr; + device_create_info.flags = 0; + device_create_info.queueCreateInfoCount = uint32_t(queue_create_infos.size()); + device_create_info.pQueueCreateInfos = queue_create_infos.data(); + // Device layers are deprecated - using validation layer on the instance. + device_create_info.enabledLayerCount = 0; + device_create_info.ppEnabledLayerNames = nullptr; + device_create_info.enabledExtensionCount = + uint32_t(device_extensions_enabled.size()); + device_create_info.ppEnabledExtensionNames = device_extensions_enabled.data(); + // TODO(Triang3l): Enable only needed features. + device_create_info.pEnabledFeatures = &device_features_; + if (ifn_.vkCreateDevice(physical_device_, &device_create_info, nullptr, + &device_) != VK_SUCCESS) { + XELOGE("Failed to create a Vulkan device"); + return false; + } + + // Get device functions. + std::memset(&dfn_, 0, sizeof(ifn_)); + bool device_functions_loaded = true; +#define XE_UI_VULKAN_FUNCTION(name) \ + functions_loaded &= \ + (dfn_.name = PFN_##name(ifn_.vkGetDeviceProcAddr(device_, #name))) != \ + nullptr; +#define XE_UI_VULKAN_FUNCTION_DONT_PROMOTE(extension_name, core_name) \ + functions_loaded &= \ + (dfn_.extension_name = PFN_##extension_name( \ + ifn_.vkGetDeviceProcAddr(device_, #extension_name))) != nullptr; +#define XE_UI_VULKAN_FUNCTION_PROMOTE(extension_name, core_name) \ + functions_loaded &= \ + (dfn_.extension_name = PFN_##extension_name( \ + ifn_.vkGetDeviceProcAddr(device_, #core_name))) != nullptr; + // Core - require unconditionally. + { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/device_1_0.inc" + if (!functions_loaded) { + XELOGE("Failed to get Vulkan device function pointers"); + return false; + } + } + // Extensions - disable the specific extension if failed to get its functions. + if (device_extensions_.amd_shader_info) { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/device_amd_shader_info.inc" + device_extensions_.amd_shader_info = functions_loaded; + } + if (device_extensions_.khr_swapchain) { + bool functions_loaded = true; +#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc" + if (!functions_loaded) { + // Outside the physical device selection loop, so can't just skip the + // device anymore, but this shouldn't really happen anyway. + XELOGE( + "Failed to get Vulkan swapchain function pointers while swapchain " + "support is required"); + return false; + } + device_extensions_.khr_swapchain = functions_loaded; + } +#undef XE_UI_VULKAN_FUNCTION_PROMOTE +#undef XE_UI_VULKAN_FUNCTION_DONT_PROMOTE +#undef XE_UI_VULKAN_FUNCTION + if (!device_functions_loaded) { + XELOGE("Failed to get Vulkan device function pointers"); + return false; + } + + // Report device information after verifying that extension function pointers + // could be obtained. + XELOGVK( + "Vulkan device: {} (vendor {:04X}, device {:04X}, driver {:08X}, API " + "{}.{}.{})", + device_properties_.deviceName, device_properties_.vendorID, + device_properties_.deviceID, device_properties_.driverVersion, + VK_VERSION_MAJOR(device_properties_.apiVersion), + VK_VERSION_MINOR(device_properties_.apiVersion), + VK_VERSION_PATCH(device_properties_.apiVersion)); + XELOGVK("Vulkan device extensions:"); + XELOGVK("* VK_AMD_shader_info: {}", + device_extensions_.amd_shader_info ? "yes" : "no"); + XELOGVK("* VK_EXT_fragment_shader_interlock: {}", + device_extensions_.ext_fragment_shader_interlock ? "yes" : "no"); + XELOGVK("* VK_KHR_dedicated_allocation: {}", + device_extensions_.khr_dedicated_allocation ? "yes" : "no"); + XELOGVK("* VK_KHR_image_format_list: {}", + device_extensions_.khr_image_format_list ? "yes" : "no"); + XELOGVK("* VK_KHR_shader_float_controls: {}", + device_extensions_.khr_shader_float_controls ? "yes" : "no"); + if (device_extensions_.khr_shader_float_controls) { + XELOGVK( + " * Signed zero, inf, nan preserve for float32: {}", + device_float_controls_properties_.shaderSignedZeroInfNanPreserveFloat32 + ? "yes" + : "no"); + XELOGVK(" * Denorm flush to zero for float32: {}", + device_float_controls_properties_.shaderDenormFlushToZeroFloat32 + ? "yes" + : "no"); + XELOGVK("* VK_KHR_spirv_1_4: {}", + device_extensions_.khr_spirv_1_4 ? "yes" : "no"); + XELOGVK("* VK_KHR_swapchain: {}", + device_extensions_.khr_swapchain ? "yes" : "no"); + } + // TODO(Triang3l): Report properties, features. + + // Get the queues. + queues_.reset(); + queues_ = std::make_unique(used_queue_count); + uint32_t queue_index = 0; + for (size_t i = 0; i < queue_families_.size(); ++i) { + const QueueFamily& queue_family = queue_families_[i]; + if (!queue_family.queue_count) { + continue; + } + assert_true(queue_index == queue_family.queue_first_index); + for (uint32_t j = 0; j < queue_family.queue_count; ++j) { + VkQueue queue; + dfn_.vkGetDeviceQueue(device_, uint32_t(i), j, &queue); + queues_[queue_index++].queue = queue; + } + } + + // Create host-side samplers. + VkSamplerCreateInfo sampler_create_info = {}; + sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_create_info.magFilter = VK_FILTER_NEAREST; + sampler_create_info.minFilter = VK_FILTER_NEAREST; + sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler_create_info.maxLod = FLT_MAX; + if (dfn_.vkCreateSampler( + device_, &sampler_create_info, nullptr, + &host_samplers_[size_t(HostSampler::kNearestClamp)]) != VK_SUCCESS) { + XELOGE("Failed to create the nearest-neighbor clamping Vulkan sampler"); + return false; + } + sampler_create_info.magFilter = VK_FILTER_LINEAR; + sampler_create_info.minFilter = VK_FILTER_LINEAR; + sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + if (dfn_.vkCreateSampler( + device_, &sampler_create_info, nullptr, + &host_samplers_[size_t(HostSampler::kLinearClamp)]) != VK_SUCCESS) { + XELOGE("Failed to create the bilinear-filtering clamping Vulkan sampler"); + return false; + } + sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + if (dfn_.vkCreateSampler( + device_, &sampler_create_info, nullptr, + &host_samplers_[size_t(HostSampler::kLinearRepeat)]) != VK_SUCCESS) { + XELOGE("Failed to create the bilinear-filtering repeating Vulkan sampler"); + return false; + } + sampler_create_info.magFilter = VK_FILTER_NEAREST; + sampler_create_info.minFilter = VK_FILTER_NEAREST; + sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + if (dfn_.vkCreateSampler( + device_, &sampler_create_info, nullptr, + &host_samplers_[size_t(HostSampler::kNearestRepeat)]) != VK_SUCCESS) { + XELOGE("Failed to create the nearest-neighbor repeating Vulkan sampler"); return false; } return true; } -std::unique_ptr VulkanProvider::CreateContext( - Window* target_window) { - auto new_context = - std::unique_ptr(new VulkanContext(this, target_window)); - if (!new_context->Initialize()) { - return nullptr; - } - return std::unique_ptr(new_context.release()); +std::unique_ptr VulkanProvider::CreatePresenter( + Presenter::HostGpuLossCallback host_gpu_loss_callback) { + return VulkanPresenter::Create(host_gpu_loss_callback, *this); } -std::unique_ptr VulkanProvider::CreateOffscreenContext() { - auto new_context = - std::unique_ptr(new VulkanContext(this, nullptr)); - if (!new_context->Initialize()) { - return nullptr; +std::unique_ptr VulkanProvider::CreateImmediateDrawer() { + return VulkanImmediateDrawer::Create(*this); +} + +void VulkanProvider::SetDeviceObjectName(VkObjectType type, uint64_t handle, + const char* name) const { + if (!debug_names_used_) { + return; } - return std::unique_ptr(new_context.release()); + VkDebugUtilsObjectNameInfoEXT name_info; + name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + name_info.pNext = nullptr; + name_info.objectType = type; + name_info.objectHandle = handle; + name_info.pObjectName = name; + ifn_.vkSetDebugUtilsObjectNameEXT(device_, &name_info); +} + +void VulkanProvider::AccumulateInstanceExtensions( + size_t properties_count, const VkExtensionProperties* properties, + bool request_debug_utils, InstanceExtensions& instance_extensions, + std::vector& instance_extensions_enabled) { + for (size_t i = 0; i < properties_count; ++i) { + const char* instance_extension_name = properties[i].extensionName; + // Checking if already enabled as an optimization to do fewer and fewer + // string comparisons, as well as to skip adding extensions promoted to the + // core to instance_extensions_enabled. Adding literals to + // instance_extensions_enabled for the most C string lifetime safety. + if (request_debug_utils && !instance_extensions.ext_debug_utils && + !std::strcmp(instance_extension_name, "VK_EXT_debug_utils")) { + // Debug utilities are only enabled when needed. Overhead in Xenia not + // profiled, but better to avoid unless enabled by the user. + instance_extensions_enabled.push_back("VK_EXT_debug_utils"); + instance_extensions.ext_debug_utils = true; + } else if (!instance_extensions.khr_get_physical_device_properties2 && + !std::strcmp(instance_extension_name, + "VK_KHR_get_physical_device_properties2")) { + instance_extensions_enabled.push_back( + "VK_KHR_get_physical_device_properties2"); + instance_extensions.khr_get_physical_device_properties2 = true; + } else if (!instance_extensions.khr_surface && + !std::strcmp(instance_extension_name, "VK_KHR_surface")) { + instance_extensions_enabled.push_back("VK_KHR_surface"); + instance_extensions.khr_surface = true; + } else { +#if XE_PLATFORM_ANDROID + if (!instance_extensions.khr_android_surface && + !std::strcmp(instance_extension_name, "VK_KHR_android_surface")) { + instance_extensions_enabled.push_back("VK_KHR_android_surface"); + instance_extensions.khr_android_surface = true; + } +#elif XE_PLATFORM_GNU_LINUX + if (!instance_extensions.khr_xcb_surface && + !std::strcmp(instance_extension_name, "VK_KHR_xcb_surface")) { + instance_extensions_enabled.push_back("VK_KHR_xcb_surface"); + instance_extensions.khr_xcb_surface = true; + } +#elif XE_PLATFORM_WIN32 + if (!instance_extensions.khr_win32_surface && + !std::strcmp(instance_extension_name, "VK_KHR_win32_surface")) { + instance_extensions_enabled.push_back("VK_KHR_win32_surface"); + instance_extensions.khr_win32_surface = true; + } +#endif + } + } +} + +VkBool32 VKAPI_CALL VulkanProvider::DebugMessengerCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageTypeFlagsEXT message_types, + const VkDebugUtilsMessengerCallbackDataEXT* callback_data, + void* user_data) { + const char* severity_string; + switch (message_severity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + severity_string = "verbose output"; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + severity_string = "info"; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + severity_string = "warning"; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + severity_string = "error"; + break; + default: + switch (xe::bit_count(uint32_t(message_severity))) { + case 0: + severity_string = "no-severity"; + break; + case 1: + severity_string = "unknown-severity"; + break; + default: + severity_string = "multi-severity"; + } + } + const char* type_string; + switch (message_types) { + case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: + type_string = "general"; + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: + type_string = "validation"; + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: + type_string = "performance"; + break; + default: + switch (xe::bit_count(uint32_t(message_types))) { + case 0: + type_string = "no-type"; + break; + case 1: + type_string = "unknown-type"; + break; + default: + type_string = "multi-type"; + } + } + XELOGVK("Vulkan {} {}: {}", type_string, severity_string, + callback_data->pMessage); + return VK_FALSE; } } // namespace vulkan diff --git a/src/xenia/ui/vulkan/vulkan_provider.h b/src/xenia/ui/vulkan/vulkan_provider.h index 12c303785..15884c93e 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.h +++ b/src/xenia/ui/vulkan/vulkan_provider.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,37 +10,282 @@ #ifndef XENIA_UI_VULKAN_VULKAN_PROVIDER_H_ #define XENIA_UI_VULKAN_VULKAN_PROVIDER_H_ +#include +#include #include +#include +#include +#include +#include "xenia/base/assert.h" +#include "xenia/base/platform.h" #include "xenia/ui/graphics_provider.h" +#include "xenia/ui/renderdoc_api.h" + +#if XE_PLATFORM_ANDROID +#ifndef VK_USE_PLATFORM_ANDROID_KHR +#define VK_USE_PLATFORM_ANDROID_KHR 1 +#endif +#elif XE_PLATFORM_GNU_LINUX +#ifndef VK_USE_PLATFORM_XCB_KHR +#define VK_USE_PLATFORM_XCB_KHR 1 +#endif +#elif XE_PLATFORM_WIN32 +// Must be included before vulkan.h with VK_USE_PLATFORM_WIN32_KHR because it +// includes Windows.h too. +#include "xenia/base/platform_win.h" +#ifndef VK_USE_PLATFORM_WIN32_KHR +#define VK_USE_PLATFORM_WIN32_KHR 1 +#endif +#endif + +#ifndef VK_NO_PROTOTYPES +#define VK_NO_PROTOTYPES 1 +#endif +#include "third_party/vulkan/vulkan.h" + +#define XELOGVK XELOGI + +#define XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES 1 namespace xe { namespace ui { namespace vulkan { -class VulkanDevice; -class VulkanInstance; - class VulkanProvider : public GraphicsProvider { public: - ~VulkanProvider() override; + ~VulkanProvider(); - static std::unique_ptr Create(); + static std::unique_ptr Create(bool is_surface_required); - VulkanInstance* instance() const { return instance_.get(); } - VulkanDevice* device() const { return device_.get(); } + std::unique_ptr CreatePresenter( + Presenter::HostGpuLossCallback host_gpu_loss_callback = + Presenter::FatalErrorHostGpuLossCallback) override; - std::unique_ptr CreateContext( - Window* target_window) override; - std::unique_ptr CreateOffscreenContext() override; + std::unique_ptr CreateImmediateDrawer() override; - protected: - VulkanProvider() = default; + const RenderdocApi& renderdoc_api() const { return renderdoc_api_; } + + struct LibraryFunctions { + // From the module. + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + PFN_vkDestroyInstance vkDestroyInstance; + // From vkGetInstanceProcAddr. + PFN_vkCreateInstance vkCreateInstance; + PFN_vkEnumerateInstanceExtensionProperties + vkEnumerateInstanceExtensionProperties; + PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; + struct { + PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion; + } v_1_1; + }; + const LibraryFunctions& lfn() const { return lfn_; } + + struct InstanceExtensions { + bool ext_debug_utils; + // Core since 1.1.0. + bool khr_get_physical_device_properties2; + + // Surface extensions. + bool khr_surface; +#if XE_PLATFORM_ANDROID + bool khr_android_surface; +#elif XE_PLATFORM_GNU_LINUX + bool khr_xcb_surface; +#elif XE_PLATFORM_WIN32 + bool khr_win32_surface; +#endif + }; + const InstanceExtensions& instance_extensions() const { + return instance_extensions_; + } + VkInstance instance() const { return instance_; } + struct InstanceFunctions { +#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name; +#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \ + PFN_##extension_name extension_name; +#include "xenia/ui/vulkan/functions/instance_1_0.inc" +#include "xenia/ui/vulkan/functions/instance_ext_debug_utils.inc" +#include "xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc" +#include "xenia/ui/vulkan/functions/instance_khr_surface.inc" +#if XE_PLATFORM_ANDROID +#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc" +#elif XE_PLATFORM_GNU_LINUX +#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc" +#elif XE_PLATFORM_WIN32 +#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc" +#endif +#undef XE_UI_VULKAN_FUNCTION_PROMOTED +#undef XE_UI_VULKAN_FUNCTION + }; + const InstanceFunctions& ifn() const { return ifn_; } + + VkPhysicalDevice physical_device() const { return physical_device_; } + const VkPhysicalDeviceProperties& device_properties() const { + return device_properties_; + } + const VkPhysicalDeviceFeatures& device_features() const { + return device_features_; + } + struct DeviceExtensions { + bool amd_shader_info; + bool ext_fragment_shader_interlock; + // Core since 1.1.0. + bool khr_dedicated_allocation; + // Core since 1.2.0. + bool khr_image_format_list; + // Core since 1.2.0. + bool khr_shader_float_controls; + // Core since 1.2.0. + bool khr_spirv_1_4; + bool khr_swapchain; + }; + const DeviceExtensions& device_extensions() const { + return device_extensions_; + } + uint32_t memory_types_device_local() const { + return memory_types_device_local_; + } + uint32_t memory_types_host_visible() const { + return memory_types_host_visible_; + } + uint32_t memory_types_host_coherent() const { + return memory_types_host_coherent_; + } + uint32_t memory_types_host_cached() const { + return memory_types_host_cached_; + } + struct QueueFamily { + uint32_t queue_first_index = 0; + uint32_t queue_count = 0; + bool potentially_supports_present = false; + }; + const std::vector& queue_families() const { + return queue_families_; + } + // Required. + uint32_t queue_family_graphics_compute() const { + return queue_family_graphics_compute_; + } + // Optional, if sparse binding is supported (UINT32_MAX otherwise). May be the + // same as queue_family_graphics_compute_. + uint32_t queue_family_sparse_binding() const { + return queue_family_sparse_binding_; + } + const VkPhysicalDeviceFloatControlsPropertiesKHR& + device_float_controls_properties() const { + return device_float_controls_properties_; + } + + struct Queue { + VkQueue queue = VK_NULL_HANDLE; + std::recursive_mutex mutex; + }; + struct QueueAcquisition { + QueueAcquisition(std::unique_lock&& lock, + VkQueue queue) + : lock(std::move(lock)), queue(queue) {} + std::unique_lock lock; + VkQueue queue; + }; + QueueAcquisition AcquireQueue(uint32_t index) { + Queue& queue = queues_[index]; + return QueueAcquisition(std::unique_lock(queue.mutex), + queue.queue); + } + QueueAcquisition AcquireQueue(uint32_t family_index, uint32_t index) { + assert_true(family_index != UINT32_MAX); + return AcquireQueue(queue_families_[family_index].queue_first_index + + index); + } + + VkDevice device() const { return device_; } + struct DeviceFunctions { +#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name; +#include "xenia/ui/vulkan/functions/device_1_0.inc" +#include "xenia/ui/vulkan/functions/device_amd_shader_info.inc" +#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc" +#undef XE_UI_VULKAN_FUNCTION + }; + const DeviceFunctions& dfn() const { return dfn_; } + + void SetDeviceObjectName(VkObjectType type, uint64_t handle, + const char* name) const; + + bool IsSparseBindingSupported() const { + return queue_family_sparse_binding_ != UINT32_MAX; + } + + // Samplers that may be useful for host needs. Only these samplers should be + // used in host, non-emulation contexts, because the total number of samplers + // is heavily limited (4000) on Nvidia GPUs - the rest of samplers are + // allocated for emulation. + enum class HostSampler { + kNearestClamp, + kLinearClamp, + kNearestRepeat, + kLinearRepeat, + + kCount, + }; + VkSampler GetHostSampler(HostSampler sampler) const { + return host_samplers_[size_t(sampler)]; + } + + private: + explicit VulkanProvider(bool is_surface_required) + : is_surface_required_(is_surface_required) {} bool Initialize(); - std::unique_ptr instance_; - std::unique_ptr device_; + static void AccumulateInstanceExtensions( + size_t properties_count, const VkExtensionProperties* properties, + bool request_debug_utils, InstanceExtensions& instance_extensions, + std::vector& instance_extensions_enabled); + + static VkBool32 VKAPI_CALL DebugMessengerCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, + VkDebugUtilsMessageTypeFlagsEXT message_types, + const VkDebugUtilsMessengerCallbackDataEXT* callback_data, + void* user_data); + + bool is_surface_required_; + + RenderdocApi renderdoc_api_; + +#if XE_PLATFORM_LINUX + void* library_ = nullptr; +#elif XE_PLATFORM_WIN32 + HMODULE library_ = nullptr; +#endif + + LibraryFunctions lfn_ = {}; + + InstanceExtensions instance_extensions_; + VkInstance instance_ = VK_NULL_HANDLE; + InstanceFunctions ifn_; + VkDebugUtilsMessengerEXT debug_messenger_ = VK_NULL_HANDLE; + bool debug_names_used_ = false; + + VkPhysicalDevice physical_device_ = VK_NULL_HANDLE; + VkPhysicalDeviceProperties device_properties_; + VkPhysicalDeviceFeatures device_features_; + DeviceExtensions device_extensions_; + uint32_t memory_types_device_local_; + uint32_t memory_types_host_visible_; + uint32_t memory_types_host_coherent_; + uint32_t memory_types_host_cached_; + std::vector queue_families_; + uint32_t queue_family_graphics_compute_; + uint32_t queue_family_sparse_binding_; + VkPhysicalDeviceFloatControlsPropertiesKHR device_float_controls_properties_; + + VkDevice device_ = VK_NULL_HANDLE; + DeviceFunctions dfn_ = {}; + // Queues contain a mutex, can't use std::vector. + std::unique_ptr queues_; + + VkSampler host_samplers_[size_t(HostSampler::kCount)] = {}; }; } // namespace vulkan diff --git a/src/xenia/ui/vulkan/vulkan_submission_tracker.cc b/src/xenia/ui/vulkan/vulkan_submission_tracker.cc new file mode 100644 index 000000000..f6ebe60bb --- /dev/null +++ b/src/xenia/ui/vulkan/vulkan_submission_tracker.cc @@ -0,0 +1,174 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/vulkan/vulkan_submission_tracker.h" + +#include + +#include "xenia/base/assert.h" +#include "xenia/ui/vulkan/vulkan_util.h" + +namespace xe { +namespace ui { +namespace vulkan { + +VulkanSubmissionTracker::FenceAcquisition::~FenceAcquisition() { + if (!submission_tracker_) { + // Dropped submission or left after std::move. + return; + } + assert_true(submission_tracker_->fence_acquired_ == fence_); + if (fence_ != VK_NULL_HANDLE) { + if (signal_failed_) { + // Left in the unsignaled state. + submission_tracker_->fences_reclaimed_.push_back(fence_); + } else { + // Left in the pending state. + submission_tracker_->fences_pending_.emplace_back( + submission_tracker_->submission_current_, fence_); + } + submission_tracker_->fence_acquired_ = VK_NULL_HANDLE; + } + ++submission_tracker_->submission_current_; +} + +void VulkanSubmissionTracker::Shutdown() { + AwaitAllSubmissionsCompletion(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + for (VkFence fence : fences_reclaimed_) { + dfn.vkDestroyFence(device, fence, nullptr); + } + fences_reclaimed_.clear(); + for (const std::pair& fence_pair : fences_pending_) { + dfn.vkDestroyFence(device, fence_pair.second, nullptr); + } + fences_pending_.clear(); + assert_true(fence_acquired_ == VK_NULL_HANDLE); + util::DestroyAndNullHandle(dfn.vkDestroyFence, device, fence_acquired_); +} + +void VulkanSubmissionTracker::FenceAcquisition::SubmissionFailedOrDropped() { + if (!submission_tracker_) { + return; + } + assert_true(submission_tracker_->fence_acquired_ == fence_); + if (fence_ != VK_NULL_HANDLE) { + submission_tracker_->fences_reclaimed_.push_back(fence_); + } + submission_tracker_->fence_acquired_ = VK_NULL_HANDLE; + fence_ = VK_NULL_HANDLE; + // No submission acquisition from now on, don't increment the current + // submission index as well. + submission_tracker_ = VK_NULL_HANDLE; +} + +uint64_t VulkanSubmissionTracker::UpdateAndGetCompletedSubmission() { + if (!fences_pending_.empty()) { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + while (!fences_pending_.empty()) { + const std::pair& pending_pair = + fences_pending_.front(); + assert_true(pending_pair.first > submission_completed_on_gpu_); + if (dfn.vkGetFenceStatus(device, pending_pair.second) != VK_SUCCESS) { + break; + } + fences_reclaimed_.push_back(pending_pair.second); + submission_completed_on_gpu_ = pending_pair.first; + fences_pending_.pop_front(); + } + } + return submission_completed_on_gpu_; +} + +bool VulkanSubmissionTracker::AwaitSubmissionCompletion( + uint64_t submission_index) { + // The tracker itself can't give a submission index for a submission that + // hasn't even started being recorded yet, the client has provided a + // completely invalid value or has done overly optimistic math if such an + // index has been obtained somehow. + assert_true(submission_index <= submission_current_); + // Waiting for the current submission is fine if there was a failure or a + // refusal to submit, and the submission index wasn't incremented, but still + // need to release objects referenced in the dropped submission (while + // shutting down, for instance - in this case, waiting for the last successful + // submission, which could have also referenced the objects from the new + // submission - we can't know since the client has already overwritten its + // last usage index, would correctly ensure that GPU usage of the objects is + // not pending). Waiting for successful submissions, but failed signals, will + // result in a true race condition, however, but waiting for the closest + // successful signal is the best approximation - also retrying to signal in + // this case. + // Go from the most recent to wait only for one fence, which includes all the + // preceding ones. + // "Fence signal operations that are defined by vkQueueSubmit additionally + // include in the first synchronization scope all commands that occur earlier + // in submission order." + size_t reclaim_end = fences_pending_.size(); + if (reclaim_end) { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + while (reclaim_end) { + const std::pair& pending_pair = + fences_pending_[reclaim_end - 1]; + assert_true(pending_pair.first > submission_completed_on_gpu_); + if (pending_pair.first <= submission_index) { + // Wait if requested. + if (dfn.vkWaitForFences(device, 1, &pending_pair.second, VK_TRUE, + UINT64_MAX) == VK_SUCCESS) { + break; + } + } + // Just refresh the completed submission. + if (dfn.vkGetFenceStatus(device, pending_pair.second) == VK_SUCCESS) { + break; + } + --reclaim_end; + } + if (reclaim_end) { + submission_completed_on_gpu_ = fences_pending_[reclaim_end - 1].first; + for (; reclaim_end; --reclaim_end) { + fences_reclaimed_.push_back(fences_pending_.front().second); + fences_pending_.pop_front(); + } + } + } + return submission_completed_on_gpu_ == submission_index; +} + +VulkanSubmissionTracker::FenceAcquisition +VulkanSubmissionTracker::AcquireFenceToAdvanceSubmission() { + assert_true(fence_acquired_ == VK_NULL_HANDLE); + // Reclaim fences if the client only gets the completed submission index or + // awaits in special cases such as shutdown. + UpdateAndGetCompletedSubmission(); + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + if (!fences_reclaimed_.empty()) { + VkFence reclaimed_fence = fences_reclaimed_.back(); + if (dfn.vkResetFences(device, 1, &reclaimed_fence) == VK_SUCCESS) { + fence_acquired_ = fences_reclaimed_.back(); + fences_reclaimed_.pop_back(); + } + } + if (fence_acquired_ == VK_NULL_HANDLE) { + VkFenceCreateInfo fence_create_info; + fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_create_info.pNext = nullptr; + fence_create_info.flags = 0; + // May fail, a null fence is handled in FenceAcquisition. + dfn.vkCreateFence(device, &fence_create_info, nullptr, &fence_acquired_); + } + return FenceAcquisition(*this, fence_acquired_); +} + +} // namespace vulkan +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_submission_tracker.h b/src/xenia/ui/vulkan/vulkan_submission_tracker.h new file mode 100644 index 000000000..c1f055c87 --- /dev/null +++ b/src/xenia/ui/vulkan/vulkan_submission_tracker.h @@ -0,0 +1,135 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VULKAN_VULKAN_SUBMISSION_TRACKER_H_ +#define XENIA_UI_VULKAN_VULKAN_SUBMISSION_TRACKER_H_ + +#include +#include +#include +#include + +#include "xenia/ui/vulkan/vulkan_provider.h" + +namespace xe { +namespace ui { +namespace vulkan { + +// Fence wrapper, safely handling cases when the fence has not been initialized +// yet or has already been shut down, and failed or dropped submissions. +// +// The current submission index can be associated with the usage of objects to +// release them when the GPU isn't potentially referencing them anymore, and +// should be incremented only +// +// 0 can be used as a "never referenced" submission index. +// +// The submission index timeline survives Shutdown, so submission indices can be +// given to clients that are not aware of the lifetime of the tracker. +// +// To transfer the tracker to another queue (to make sure the first signal on +// the new queue does not happen before the last signal on the old one), call +// AwaitAllSubmissionsCompletion before doing the first submission on the new +// one. +class VulkanSubmissionTracker { + public: + class FenceAcquisition { + public: + FenceAcquisition() : submission_tracker_(nullptr), fence_(VK_NULL_HANDLE) {} + FenceAcquisition(VulkanSubmissionTracker& submission_tracker, VkFence fence) + : submission_tracker_(&submission_tracker), fence_(fence) {} + FenceAcquisition(const FenceAcquisition& fence_acquisition) = delete; + FenceAcquisition& operator=(const FenceAcquisition& fence_acquisition) = + delete; + FenceAcquisition(FenceAcquisition&& fence_acquisition) { + *this = std::move(fence_acquisition); + } + FenceAcquisition& operator=(FenceAcquisition&& fence_acquisition) { + if (this == &fence_acquisition) { + return *this; + } + submission_tracker_ = fence_acquisition.submission_tracker_; + fence_acquisition.submission_tracker_ = nullptr; + fence_ = fence_acquisition.fence_; + fence_acquisition.fence_ = VK_NULL_HANDLE; + return *this; + } + ~FenceAcquisition(); + + // In unsignaled state. May be null if failed to create or to reset a fence. + VkFence fence() { return fence_; } + + // Call if vkQueueSubmit has failed (or it was decided not to commit the + // submission), and the submission index shouldn't be incremented by + // releasing this submission (for instance, to retry commands with long-term + // effects like copying or image layout changes later if in the attempt the + // submission index stays the same). + void SubmissionFailedOrDropped(); + // Call if for some reason (like signaling multiple fences) the fence + // signaling was done in a separate submission than the command buffer, and + // the command buffer vkQueueSubmit succeeded (so commands with long-term + // effects will be executed), but the fence-only vkQueueSubmit has failed, + // thus the tracker shouldn't attempt to wait for that fence (it will be in + // the unsignaled state). + void SubmissionSucceededSignalFailed() { signal_failed_ = true; } + + private: + // If nullptr, has been moved to another FenceAcquisition - not holding a + // fence from now on. + VulkanSubmissionTracker* submission_tracker_; + VkFence fence_; + bool signal_failed_ = false; + }; + + VulkanSubmissionTracker(VulkanProvider& provider) : provider_(provider) {} + VulkanSubmissionTracker(const VulkanSubmissionTracker& submission_tracker) = + delete; + VulkanSubmissionTracker& operator=( + const VulkanSubmissionTracker& submission_tracker) = delete; + ~VulkanSubmissionTracker() { Shutdown(); } + + void Shutdown(); + + uint64_t GetCurrentSubmission() const { return submission_current_; } + uint64_t UpdateAndGetCompletedSubmission(); + + // Returns whether the expected GPU signal has actually been reached (rather + // than some fallback condition) for cases when stronger completeness + // guarantees as needed (when downloading, as opposed to just destroying). + // If false is returned, it's also not guaranteed that GetCompletedSubmission + // will return a value >= submission_index. + bool AwaitSubmissionCompletion(uint64_t submission_index); + bool AwaitAllSubmissionsCompletion() { + return AwaitSubmissionCompletion(submission_current_ - 1); + } + + [[nodiscard]] FenceAcquisition AcquireFenceToAdvanceSubmission(); + + private: + VulkanProvider& provider_; + uint64_t submission_current_ = 1; + // Last submission with a successful fence signal as well as a successful + // fence wait / query. + uint64_t submission_completed_on_gpu_ = 0; + // The flow is: + // Reclaimed (or create if empty) > acquired > pending > reclaimed. + // Or, if dropped the submission while acquired: + // Reclaimed (or create if empty) > acquired > reclaimed. + VkFence fence_acquired_ = VK_NULL_HANDLE; + // Ordered by the submission index (the first pair member). + std::deque> fences_pending_; + // Fences are reclaimed when awaiting or when refreshing the completed value. + std::vector fences_reclaimed_; +}; + +} // namespace vulkan +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_VULKAN_VULKAN_SUBMISSION_TRACKER_H_ diff --git a/src/xenia/ui/vulkan/vulkan_swap_chain.cc b/src/xenia/ui/vulkan/vulkan_swap_chain.cc deleted file mode 100644 index 79f74a782..000000000 --- a/src/xenia/ui/vulkan/vulkan_swap_chain.cc +++ /dev/null @@ -1,824 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/vulkan/vulkan_swap_chain.h" - -#include -#include - -#include "xenia/base/assert.h" -#include "xenia/base/cvar.h" -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_device.h" -#include "xenia/ui/vulkan/vulkan_instance.h" -#include "xenia/ui/vulkan/vulkan_util.h" - -DEFINE_bool(vulkan_random_clear_color, false, - "Randomizes framebuffer clear color.", "Vulkan"); - -namespace xe { -namespace ui { -namespace vulkan { - -VulkanSwapChain::VulkanSwapChain(VulkanInstance* instance, VulkanDevice* device) - : instance_(instance), device_(device) {} - -VulkanSwapChain::~VulkanSwapChain() { Shutdown(); } - -VkResult VulkanSwapChain::Initialize(VkSurfaceKHR surface) { - surface_ = surface; - const VulkanInstance::InstanceFunctions& ifn = instance_->ifn(); - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - VkResult status; - - // Find a queue family that supports presentation. - VkBool32 surface_supported = false; - uint32_t queue_family_index = -1; - for (uint32_t i = 0; - i < device_->device_info().queue_family_properties.size(); i++) { - const VkQueueFamilyProperties& family_props = - device_->device_info().queue_family_properties[i]; - if (!(family_props.queueFlags & VK_QUEUE_GRAPHICS_BIT) || - !(family_props.queueFlags & VK_QUEUE_TRANSFER_BIT)) { - continue; - } - - status = ifn.vkGetPhysicalDeviceSurfaceSupportKHR(*device_, i, surface, - &surface_supported); - if (status == VK_SUCCESS && surface_supported == VK_TRUE) { - queue_family_index = i; - break; - } - } - - if (!surface_supported) { - XELOGE( - "Physical device does not have a queue that supports " - "graphics/transfer/presentation!"); - return VK_ERROR_INCOMPATIBLE_DRIVER; - } - - presentation_queue_ = device_->AcquireQueue(queue_family_index); - presentation_queue_family_ = queue_family_index; - if (!presentation_queue_) { - // That's okay, use the primary queue. - presentation_queue_ = device_->primary_queue(); - presentation_queue_mutex_ = &device_->primary_queue_mutex(); - presentation_queue_family_ = device_->queue_family_index(); - - if (!presentation_queue_) { - XELOGE("Failed to acquire swap chain presentation queue!"); - return VK_ERROR_INITIALIZATION_FAILED; - } - } - - // Query supported target formats. - uint32_t count = 0; - status = ifn.vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count, - nullptr); - CheckResult(status, "vkGetPhysicalDeviceSurfaceFormatsKHR"); - std::vector surface_formats; - surface_formats.resize(count); - status = ifn.vkGetPhysicalDeviceSurfaceFormatsKHR(*device_, surface_, &count, - surface_formats.data()); - CheckResult(status, "vkGetPhysicalDeviceSurfaceFormatsKHR"); - if (status != VK_SUCCESS) { - return status; - } - - // If the format list includes just one entry of VK_FORMAT_UNDEFINED the - // surface has no preferred format. - // Otherwise, at least one supported format will be returned. - assert_true(surface_formats.size() >= 1); - if (surface_formats.size() == 1 && - surface_formats[0].format == VK_FORMAT_UNDEFINED) { - // Fallback to common RGBA. - surface_format_ = VK_FORMAT_R8G8B8A8_UNORM; - } else { - // Use first defined format. - surface_format_ = surface_formats[0].format; - } - - // Query surface min/max/caps. - VkSurfaceCapabilitiesKHR surface_caps; - status = ifn.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(*device_, surface_, - &surface_caps); - CheckResult(status, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); - if (status != VK_SUCCESS) { - return status; - } - - // Query surface properties so we can configure ourselves within bounds. - std::vector present_modes; - status = ifn.vkGetPhysicalDeviceSurfacePresentModesKHR(*device_, surface_, - &count, nullptr); - CheckResult(status, "vkGetPhysicalDeviceSurfacePresentModesKHR"); - if (status != VK_SUCCESS) { - return status; - } - - present_modes.resize(count); - status = ifn.vkGetPhysicalDeviceSurfacePresentModesKHR( - *device_, surface_, &count, present_modes.data()); - CheckResult(status, "vkGetPhysicalDeviceSurfacePresentModesKHR"); - if (status != VK_SUCCESS) { - return status; - } - - // Calculate swapchain target dimensions. - VkExtent2D extent = surface_caps.currentExtent; - if (surface_caps.currentExtent.width == -1) { - assert_true(surface_caps.currentExtent.height == -1); - // Undefined extents, so we need to pick something. - XELOGI("Swap chain target surface extents undefined; guessing value"); - extent.width = 1280; - extent.height = 720; - } - surface_width_ = extent.width; - surface_height_ = extent.height; - - // Always prefer mailbox mode (non-tearing, low-latency). - // If it's not available we'll use immediate (tearing, low-latency). - // If not even that we fall back to FIFO, which sucks. - VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; - for (size_t i = 0; i < present_modes.size(); ++i) { - if (present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { - // This is the best, so early-out. - present_mode = VK_PRESENT_MODE_MAILBOX_KHR; - break; - } else if (present_modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) { - present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; - } - } - - // Determine the number of images (1 + number queued). - uint32_t image_count = surface_caps.minImageCount + 1; - if (surface_caps.maxImageCount > 0 && - image_count > surface_caps.maxImageCount) { - // Too many requested - use whatever we can. - XELOGI("Requested number of swapchain images ({}) exceeds maximum ({})", - image_count, surface_caps.maxImageCount); - image_count = surface_caps.maxImageCount; - } - - // Always pass through whatever transform the surface started with (so long - // as it's supported). - VkSurfaceTransformFlagBitsKHR pre_transform = surface_caps.currentTransform; - - VkSwapchainCreateInfoKHR create_info; - create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - create_info.pNext = nullptr; - create_info.flags = 0; - create_info.surface = surface_; - create_info.minImageCount = image_count; - create_info.imageFormat = surface_format_; - create_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - create_info.imageExtent.width = extent.width; - create_info.imageExtent.height = extent.height; - create_info.imageArrayLayers = 1; - create_info.imageUsage = - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - create_info.queueFamilyIndexCount = 0; - create_info.pQueueFamilyIndices = nullptr; - create_info.preTransform = pre_transform; - create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - create_info.presentMode = present_mode; - create_info.clipped = VK_TRUE; - create_info.oldSwapchain = nullptr; - - XELOGVK("Creating swap chain:"); - XELOGVK(" minImageCount = {}", create_info.minImageCount); - XELOGVK(" imageFormat = {}", to_string(create_info.imageFormat)); - XELOGVK(" imageExtent = {} x {}", create_info.imageExtent.width, - create_info.imageExtent.height); - auto pre_transform_str = to_flags_string(create_info.preTransform); - XELOGVK(" preTransform = {}", pre_transform_str); - XELOGVK(" imageArrayLayers = {}", create_info.imageArrayLayers); - XELOGVK(" presentMode = {}", to_string(create_info.presentMode)); - XELOGVK(" clipped = {}", create_info.clipped ? "true" : "false"); - XELOGVK(" imageColorSpace = {}", to_string(create_info.imageColorSpace)); - auto image_usage_flags_str = to_flags_string( - static_cast(create_info.imageUsage)); - XELOGVK(" imageUsageFlags = {}", image_usage_flags_str); - XELOGVK(" imageSharingMode = {}", to_string(create_info.imageSharingMode)); - XELOGVK(" queueFamilyCount = {}", create_info.queueFamilyIndexCount); - - status = dfn.vkCreateSwapchainKHR(*device_, &create_info, nullptr, &handle); - if (status != VK_SUCCESS) { - XELOGE("Failed to create swapchain: {}", to_string(status)); - return status; - } - - // Create the pool used for transient buffers, so we can reset them all at - // once. - VkCommandPoolCreateInfo cmd_pool_info; - cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmd_pool_info.pNext = nullptr; - cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - cmd_pool_info.queueFamilyIndex = presentation_queue_family_; - status = - dfn.vkCreateCommandPool(*device_, &cmd_pool_info, nullptr, &cmd_pool_); - CheckResult(status, "vkCreateCommandPool"); - if (status != VK_SUCCESS) { - return status; - } - - // Primary command buffer - VkCommandBufferAllocateInfo cmd_buffer_info; - cmd_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmd_buffer_info.pNext = nullptr; - cmd_buffer_info.commandPool = cmd_pool_; - cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_buffer_info.commandBufferCount = 2; - status = - dfn.vkAllocateCommandBuffers(*device_, &cmd_buffer_info, &cmd_buffer_); - CheckResult(status, "vkCreateCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - // Make two command buffers we'll do all our primary rendering from. - VkCommandBuffer command_buffers[2]; - cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; - cmd_buffer_info.commandBufferCount = 2; - status = - dfn.vkAllocateCommandBuffers(*device_, &cmd_buffer_info, command_buffers); - CheckResult(status, "vkCreateCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - render_cmd_buffer_ = command_buffers[0]; - copy_cmd_buffer_ = command_buffers[1]; - - // Create the render pass used to draw to the swap chain. - // The actual framebuffer attached will depend on which image we are drawing - // into. - VkAttachmentDescription color_attachment; - color_attachment.flags = 0; - color_attachment.format = surface_format_; - color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; - color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; // CLEAR; - color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - color_attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - color_attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkAttachmentReference color_reference; - color_reference.attachment = 0; - color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkAttachmentReference depth_reference; - depth_reference.attachment = VK_ATTACHMENT_UNUSED; - depth_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; - VkSubpassDescription render_subpass; - render_subpass.flags = 0; - render_subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - render_subpass.inputAttachmentCount = 0; - render_subpass.pInputAttachments = nullptr; - render_subpass.colorAttachmentCount = 1; - render_subpass.pColorAttachments = &color_reference; - render_subpass.pResolveAttachments = nullptr; - render_subpass.pDepthStencilAttachment = &depth_reference; - render_subpass.preserveAttachmentCount = 0, - render_subpass.pPreserveAttachments = nullptr; - VkRenderPassCreateInfo render_pass_info; - render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - render_pass_info.pNext = nullptr; - render_pass_info.flags = 0; - render_pass_info.attachmentCount = 1; - render_pass_info.pAttachments = &color_attachment; - render_pass_info.subpassCount = 1; - render_pass_info.pSubpasses = &render_subpass; - render_pass_info.dependencyCount = 0; - render_pass_info.pDependencies = nullptr; - status = dfn.vkCreateRenderPass(*device_, &render_pass_info, nullptr, - &render_pass_); - CheckResult(status, "vkCreateRenderPass"); - if (status != VK_SUCCESS) { - return status; - } - - // Create a semaphore we'll use to synchronize with the swapchain. - VkSemaphoreCreateInfo semaphore_info; - semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semaphore_info.pNext = nullptr; - semaphore_info.flags = 0; - status = dfn.vkCreateSemaphore(*device_, &semaphore_info, nullptr, - &image_available_semaphore_); - CheckResult(status, "vkCreateSemaphore"); - if (status != VK_SUCCESS) { - return status; - } - - // Create another semaphore used to synchronize writes to the swap image. - status = dfn.vkCreateSemaphore(*device_, &semaphore_info, nullptr, - &image_usage_semaphore_); - CheckResult(status, "vkCreateSemaphore"); - if (status != VK_SUCCESS) { - return status; - } - - // Get images we will be presenting to. - // Note that this may differ from our requested amount. - uint32_t actual_image_count = 0; - std::vector images; - status = dfn.vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count, - nullptr); - CheckResult(status, "vkGetSwapchainImagesKHR"); - if (status != VK_SUCCESS) { - return status; - } - - images.resize(actual_image_count); - status = dfn.vkGetSwapchainImagesKHR(*device_, handle, &actual_image_count, - images.data()); - CheckResult(status, "vkGetSwapchainImagesKHR"); - if (status != VK_SUCCESS) { - return status; - } - - // Create all buffers. - buffers_.resize(images.size()); - for (size_t i = 0; i < buffers_.size(); ++i) { - status = InitializeBuffer(&buffers_[i], images[i]); - if (status != VK_SUCCESS) { - XELOGE("Failed to initialize a swapchain buffer"); - return status; - } - - buffers_[i].image_layout = VK_IMAGE_LAYOUT_UNDEFINED; - } - - // Create a fence we'll use to wait for commands to finish. - VkFenceCreateInfo fence_create_info = { - VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - nullptr, - VK_FENCE_CREATE_SIGNALED_BIT, - }; - status = dfn.vkCreateFence(*device_, &fence_create_info, nullptr, - &synchronization_fence_); - CheckResult(status, "vkGetSwapchainImagesKHR"); - if (status != VK_SUCCESS) { - return status; - } - - XELOGVK("Swap chain initialized successfully!"); - return VK_SUCCESS; -} - -VkResult VulkanSwapChain::InitializeBuffer(Buffer* buffer, - VkImage target_image) { - DestroyBuffer(buffer); - buffer->image = target_image; - - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - VkResult status; - - // Create an image view for the presentation image. - // This will be used as a framebuffer attachment. - VkImageViewCreateInfo image_view_info; - image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - image_view_info.pNext = nullptr; - image_view_info.flags = 0; - image_view_info.image = buffer->image; - image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - image_view_info.format = surface_format_; - image_view_info.components.r = VK_COMPONENT_SWIZZLE_R; - image_view_info.components.g = VK_COMPONENT_SWIZZLE_G; - image_view_info.components.b = VK_COMPONENT_SWIZZLE_B; - image_view_info.components.a = VK_COMPONENT_SWIZZLE_A; - image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - image_view_info.subresourceRange.baseMipLevel = 0; - image_view_info.subresourceRange.levelCount = 1; - image_view_info.subresourceRange.baseArrayLayer = 0; - image_view_info.subresourceRange.layerCount = 1; - status = dfn.vkCreateImageView(*device_, &image_view_info, nullptr, - &buffer->image_view); - CheckResult(status, "vkCreateImageView"); - if (status != VK_SUCCESS) { - return status; - } - - // Create the framebuffer used to render into this image. - VkImageView attachments[] = {buffer->image_view}; - VkFramebufferCreateInfo framebuffer_info; - framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebuffer_info.pNext = nullptr; - framebuffer_info.flags = 0; - framebuffer_info.renderPass = render_pass_; - framebuffer_info.attachmentCount = - static_cast(xe::countof(attachments)); - framebuffer_info.pAttachments = attachments; - framebuffer_info.width = surface_width_; - framebuffer_info.height = surface_height_; - framebuffer_info.layers = 1; - status = dfn.vkCreateFramebuffer(*device_, &framebuffer_info, nullptr, - &buffer->framebuffer); - CheckResult(status, "vkCreateFramebuffer"); - if (status != VK_SUCCESS) { - return status; - } - - return VK_SUCCESS; -} - -void VulkanSwapChain::DestroyBuffer(Buffer* buffer) { - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - if (buffer->framebuffer) { - dfn.vkDestroyFramebuffer(*device_, buffer->framebuffer, nullptr); - buffer->framebuffer = nullptr; - } - if (buffer->image_view) { - dfn.vkDestroyImageView(*device_, buffer->image_view, nullptr); - buffer->image_view = nullptr; - } - // Image is taken care of by the presentation engine. - buffer->image = nullptr; -} - -VkResult VulkanSwapChain::Reinitialize() { - // Hacky, but stash the surface so we can reuse it. - auto surface = surface_; - surface_ = nullptr; - Shutdown(); - return Initialize(surface); -} - -void VulkanSwapChain::WaitOnSemaphore(VkSemaphore sem) { - wait_semaphores_.push_back(sem); -} - -void VulkanSwapChain::Shutdown() { - // TODO(benvanik): properly wait for a clean state. - for (auto& buffer : buffers_) { - DestroyBuffer(&buffer); - } - buffers_.clear(); - - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - - DestroyAndNullHandle(dfn.vkDestroySemaphore, *device_, - image_available_semaphore_); - DestroyAndNullHandle(dfn.vkDestroyRenderPass, *device_, render_pass_); - - if (copy_cmd_buffer_) { - dfn.vkFreeCommandBuffers(*device_, cmd_pool_, 1, ©_cmd_buffer_); - copy_cmd_buffer_ = nullptr; - } - if (render_cmd_buffer_) { - dfn.vkFreeCommandBuffers(*device_, cmd_pool_, 1, &render_cmd_buffer_); - render_cmd_buffer_ = nullptr; - } - DestroyAndNullHandle(dfn.vkDestroyCommandPool, *device_, cmd_pool_); - - if (presentation_queue_) { - if (!presentation_queue_mutex_) { - // We own the queue and need to release it. - device_->ReleaseQueue(presentation_queue_, presentation_queue_family_); - } - presentation_queue_ = nullptr; - presentation_queue_mutex_ = nullptr; - presentation_queue_family_ = -1; - } - - DestroyAndNullHandle(dfn.vkDestroyFence, *device_, synchronization_fence_); - - // images_ doesn't need to be cleaned up as the swapchain does it implicitly. - DestroyAndNullHandle(dfn.vkDestroySwapchainKHR, *device_, handle); - const VulkanInstance::InstanceFunctions& ifn = instance_->ifn(); - DestroyAndNullHandle(ifn.vkDestroySurfaceKHR, *instance_, surface_); -} - -VkResult VulkanSwapChain::Begin() { - wait_semaphores_.clear(); - - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - VkResult status; - - // Wait for the last swap to finish. - status = - dfn.vkWaitForFences(*device_, 1, &synchronization_fence_, VK_TRUE, -1); - if (status != VK_SUCCESS) { - return status; - } - - status = dfn.vkResetFences(*device_, 1, &synchronization_fence_); - if (status != VK_SUCCESS) { - return status; - } - - // Get the index of the next available swapchain image. - status = - dfn.vkAcquireNextImageKHR(*device_, handle, 0, image_available_semaphore_, - nullptr, ¤t_buffer_index_); - if (status != VK_SUCCESS) { - return status; - } - - // Wait for the acquire semaphore to be signaled so that the following - // operations know they can start modifying the image. - VkSubmitInfo wait_submit_info; - wait_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - wait_submit_info.pNext = nullptr; - - VkPipelineStageFlags wait_dst_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - wait_submit_info.waitSemaphoreCount = 1; - wait_submit_info.pWaitSemaphores = &image_available_semaphore_; - wait_submit_info.pWaitDstStageMask = &wait_dst_stage; - - wait_submit_info.commandBufferCount = 0; - wait_submit_info.pCommandBuffers = nullptr; - wait_submit_info.signalSemaphoreCount = 1; - wait_submit_info.pSignalSemaphores = &image_usage_semaphore_; - if (presentation_queue_mutex_) { - presentation_queue_mutex_->lock(); - } - status = - dfn.vkQueueSubmit(presentation_queue_, 1, &wait_submit_info, nullptr); - if (presentation_queue_mutex_) { - presentation_queue_mutex_->unlock(); - } - if (status != VK_SUCCESS) { - return status; - } - - // Reset all command buffers. - dfn.vkResetCommandBuffer(render_cmd_buffer_, 0); - dfn.vkResetCommandBuffer(copy_cmd_buffer_, 0); - auto& current_buffer = buffers_[current_buffer_index_]; - - // Build the command buffer that will execute all queued rendering buffers. - VkCommandBufferInheritanceInfo inherit_info; - inherit_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; - inherit_info.pNext = nullptr; - inherit_info.renderPass = render_pass_; - inherit_info.subpass = 0; - inherit_info.framebuffer = current_buffer.framebuffer; - inherit_info.occlusionQueryEnable = VK_FALSE; - inherit_info.queryFlags = 0; - inherit_info.pipelineStatistics = 0; - - VkCommandBufferBeginInfo begin_info; - begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - begin_info.pNext = nullptr; - begin_info.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT | - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - begin_info.pInheritanceInfo = &inherit_info; - status = dfn.vkBeginCommandBuffer(render_cmd_buffer_, &begin_info); - CheckResult(status, "vkBeginCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - // Start recording the copy command buffer as well. - begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - status = dfn.vkBeginCommandBuffer(copy_cmd_buffer_, &begin_info); - CheckResult(status, "vkBeginCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - // First: Issue a command to clear the render target. - VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - VkClearColorValue clear_color; - clear_color.float32[0] = 238 / 255.0f; - clear_color.float32[1] = 238 / 255.0f; - clear_color.float32[2] = 238 / 255.0f; - clear_color.float32[3] = 1.0f; - if (cvars::vulkan_random_clear_color) { - clear_color.float32[0] = - rand() / static_cast(RAND_MAX); // NOLINT(runtime/threadsafe_fn) - clear_color.float32[1] = 1.0f; - clear_color.float32[2] = 0.0f; - } - dfn.vkCmdClearColorImage(copy_cmd_buffer_, current_buffer.image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, - 1, &clear_range); - - return VK_SUCCESS; -} - -VkResult VulkanSwapChain::End() { - auto& current_buffer = buffers_[current_buffer_index_]; - const VulkanDevice::DeviceFunctions& dfn = device_->dfn(); - VkResult status; - - status = dfn.vkEndCommandBuffer(render_cmd_buffer_); - CheckResult(status, "vkEndCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - status = dfn.vkEndCommandBuffer(copy_cmd_buffer_); - CheckResult(status, "vkEndCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - // Build primary command buffer. - status = dfn.vkResetCommandBuffer(cmd_buffer_, 0); - CheckResult(status, "vkResetCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - VkCommandBufferBeginInfo begin_info; - begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - begin_info.pNext = nullptr; - begin_info.flags = 0; - begin_info.pInheritanceInfo = nullptr; - status = dfn.vkBeginCommandBuffer(cmd_buffer_, &begin_info); - CheckResult(status, "vkBeginCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - // Transition the image to a format we can copy to. - VkImageMemoryBarrier pre_image_copy_barrier; - pre_image_copy_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - pre_image_copy_barrier.pNext = nullptr; - pre_image_copy_barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - pre_image_copy_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - pre_image_copy_barrier.oldLayout = current_buffer.image_layout; - pre_image_copy_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - pre_image_copy_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - pre_image_copy_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - pre_image_copy_barrier.image = current_buffer.image; - pre_image_copy_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, - 1}; - dfn.vkCmdPipelineBarrier(cmd_buffer_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, - nullptr, 1, &pre_image_copy_barrier); - - current_buffer.image_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - - // Execute copy commands - dfn.vkCmdExecuteCommands(cmd_buffer_, 1, ©_cmd_buffer_); - - // Transition the image to a color attachment target for drawing. - VkImageMemoryBarrier pre_image_memory_barrier; - pre_image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - pre_image_memory_barrier.pNext = nullptr; - pre_image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - pre_image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - pre_image_memory_barrier.image = current_buffer.image; - pre_image_memory_barrier.subresourceRange.aspectMask = - VK_IMAGE_ASPECT_COLOR_BIT; - pre_image_memory_barrier.subresourceRange.baseMipLevel = 0; - pre_image_memory_barrier.subresourceRange.levelCount = 1; - pre_image_memory_barrier.subresourceRange.baseArrayLayer = 0; - pre_image_memory_barrier.subresourceRange.layerCount = 1; - - pre_image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - pre_image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - pre_image_memory_barrier.oldLayout = current_buffer.image_layout; - pre_image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - dfn.vkCmdPipelineBarrier(cmd_buffer_, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, - nullptr, 0, nullptr, 1, &pre_image_memory_barrier); - - current_buffer.image_layout = pre_image_memory_barrier.newLayout; - - // Begin render pass. - VkRenderPassBeginInfo render_pass_begin_info; - render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - render_pass_begin_info.pNext = nullptr; - render_pass_begin_info.renderPass = render_pass_; - render_pass_begin_info.framebuffer = current_buffer.framebuffer; - render_pass_begin_info.renderArea.offset.x = 0; - render_pass_begin_info.renderArea.offset.y = 0; - render_pass_begin_info.renderArea.extent.width = surface_width_; - render_pass_begin_info.renderArea.extent.height = surface_height_; - render_pass_begin_info.clearValueCount = 0; - render_pass_begin_info.pClearValues = nullptr; - dfn.vkCmdBeginRenderPass(cmd_buffer_, &render_pass_begin_info, - VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - - // Render commands. - dfn.vkCmdExecuteCommands(cmd_buffer_, 1, &render_cmd_buffer_); - - // End render pass. - dfn.vkCmdEndRenderPass(cmd_buffer_); - - // Transition the image to a format the presentation engine can source from. - // FIXME: Do we need more synchronization here between the copy buffer? - VkImageMemoryBarrier post_image_memory_barrier; - post_image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - post_image_memory_barrier.pNext = nullptr; - post_image_memory_barrier.srcAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - post_image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - post_image_memory_barrier.oldLayout = current_buffer.image_layout; - post_image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - post_image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - post_image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - post_image_memory_barrier.image = current_buffer.image; - post_image_memory_barrier.subresourceRange.aspectMask = - VK_IMAGE_ASPECT_COLOR_BIT; - post_image_memory_barrier.subresourceRange.baseMipLevel = 0; - post_image_memory_barrier.subresourceRange.levelCount = 1; - post_image_memory_barrier.subresourceRange.baseArrayLayer = 0; - post_image_memory_barrier.subresourceRange.layerCount = 1; - dfn.vkCmdPipelineBarrier(cmd_buffer_, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, - nullptr, 1, &post_image_memory_barrier); - - current_buffer.image_layout = post_image_memory_barrier.newLayout; - - status = dfn.vkEndCommandBuffer(cmd_buffer_); - CheckResult(status, "vkEndCommandBuffer"); - if (status != VK_SUCCESS) { - return status; - } - - std::vector semaphores; - std::vector wait_dst_stages; - for (size_t i = 0; i < wait_semaphores_.size(); i++) { - semaphores.push_back(wait_semaphores_[i]); - wait_dst_stages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - } - semaphores.push_back(image_usage_semaphore_); - wait_dst_stages.push_back(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); - - // Submit commands. - // Wait on the image usage semaphore (signaled when an image is available) - VkSubmitInfo render_submit_info; - render_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - render_submit_info.pNext = nullptr; - render_submit_info.waitSemaphoreCount = uint32_t(semaphores.size()); - render_submit_info.pWaitSemaphores = semaphores.data(); - render_submit_info.pWaitDstStageMask = wait_dst_stages.data(); - render_submit_info.commandBufferCount = 1; - render_submit_info.pCommandBuffers = &cmd_buffer_; - render_submit_info.signalSemaphoreCount = 0; - render_submit_info.pSignalSemaphores = nullptr; - if (presentation_queue_mutex_) { - presentation_queue_mutex_->lock(); - } - status = dfn.vkQueueSubmit(presentation_queue_, 1, &render_submit_info, - synchronization_fence_); - if (presentation_queue_mutex_) { - presentation_queue_mutex_->unlock(); - } - - if (status != VK_SUCCESS) { - return status; - } - - // Queue the present of our current image. - const VkSwapchainKHR swap_chains[] = {handle}; - const uint32_t swap_chain_image_indices[] = {current_buffer_index_}; - VkPresentInfoKHR present_info; - present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - present_info.pNext = nullptr; - present_info.waitSemaphoreCount = 0; - present_info.pWaitSemaphores = nullptr; - present_info.swapchainCount = static_cast(xe::countof(swap_chains)); - present_info.pSwapchains = swap_chains; - present_info.pImageIndices = swap_chain_image_indices; - present_info.pResults = nullptr; - if (presentation_queue_mutex_) { - presentation_queue_mutex_->lock(); - } - status = dfn.vkQueuePresentKHR(presentation_queue_, &present_info); - if (presentation_queue_mutex_) { - presentation_queue_mutex_->unlock(); - } - - switch (status) { - case VK_SUCCESS: - break; - case VK_SUBOPTIMAL_KHR: - // We are not rendering at the right size - but the presentation engine - // will scale the output for us. - status = VK_SUCCESS; - break; - case VK_ERROR_OUT_OF_DATE_KHR: - // Lost presentation ability; need to recreate the swapchain. - // TODO(benvanik): recreate swapchain. - assert_always("Swapchain recreation not implemented"); - break; - case VK_ERROR_DEVICE_LOST: - // Fatal. Device lost. - break; - default: - XELOGE("Failed to queue present: {}", to_string(status)); - assert_always("Unexpected queue present failure"); - } - - return status; -} - -} // namespace vulkan -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_swap_chain.h b/src/xenia/ui/vulkan/vulkan_swap_chain.h deleted file mode 100644 index 0adb35ac5..000000000 --- a/src/xenia/ui/vulkan/vulkan_swap_chain.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_VULKAN_VULKAN_SWAP_CHAIN_H_ -#define XENIA_UI_VULKAN_VULKAN_SWAP_CHAIN_H_ - -#include -#include -#include -#include - -#include "xenia/ui/vulkan/vulkan.h" -#include "xenia/ui/vulkan/vulkan_util.h" - -namespace xe { -namespace ui { -namespace vulkan { - -class VulkanDevice; -class VulkanInstance; - -class VulkanSwapChain { - public: - VulkanSwapChain(VulkanInstance* instance, VulkanDevice* device); - ~VulkanSwapChain(); - - VkSwapchainKHR handle = nullptr; - - operator VkSwapchainKHR() const { return handle; } - - uint32_t surface_width() const { return surface_width_; } - uint32_t surface_height() const { return surface_height_; } - VkImage surface_image() const { - return buffers_[current_buffer_index_].image; - } - - // Render pass used for compositing. - VkRenderPass render_pass() const { return render_pass_; } - // Render command buffer, active inside the render pass from Begin to End. - VkCommandBuffer render_cmd_buffer() const { return render_cmd_buffer_; } - // Copy commands, ran before the render command buffer. - VkCommandBuffer copy_cmd_buffer() const { return copy_cmd_buffer_; } - - // Initializes the swap chain with the given WSI surface. - VkResult Initialize(VkSurfaceKHR surface); - // Reinitializes the swap chain with the initial surface. - // The surface will be retained but all other swap chain resources will be - // torn down and recreated with the new surface properties (size/etc). - VkResult Reinitialize(); - - // Waits on and signals a semaphore in this operation. - void WaitOnSemaphore(VkSemaphore sem); - - // Begins the swap operation, preparing state for rendering. - VkResult Begin(); - // Ends the swap operation, finalizing rendering and presenting the results. - VkResult End(); - - private: - struct Buffer { - VkImage image = nullptr; - VkImageLayout image_layout = VK_IMAGE_LAYOUT_UNDEFINED; - VkImageView image_view = nullptr; - VkFramebuffer framebuffer = nullptr; - }; - - VkResult InitializeBuffer(Buffer* buffer, VkImage target_image); - void DestroyBuffer(Buffer* buffer); - - // Safely releases all swap chain resources. - void Shutdown(); - - VulkanInstance* instance_ = nullptr; - VulkanDevice* device_ = nullptr; - - VkFence synchronization_fence_ = nullptr; - VkQueue presentation_queue_ = nullptr; - std::mutex* presentation_queue_mutex_ = nullptr; - uint32_t presentation_queue_family_ = -1; - VkSurfaceKHR surface_ = nullptr; - uint32_t surface_width_ = 0; - uint32_t surface_height_ = 0; - VkFormat surface_format_ = VK_FORMAT_UNDEFINED; - VkCommandPool cmd_pool_ = nullptr; - VkCommandBuffer cmd_buffer_ = nullptr; - VkCommandBuffer copy_cmd_buffer_ = nullptr; - VkCommandBuffer render_cmd_buffer_ = nullptr; - VkRenderPass render_pass_ = nullptr; - VkSemaphore image_available_semaphore_ = nullptr; - VkSemaphore image_usage_semaphore_ = nullptr; - uint32_t current_buffer_index_ = 0; - std::vector buffers_; - std::vector wait_semaphores_; -}; - -} // namespace vulkan -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_VULKAN_VULKAN_SWAP_CHAIN_H_ diff --git a/src/xenia/ui/vulkan/vulkan_upload_buffer_pool.cc b/src/xenia/ui/vulkan/vulkan_upload_buffer_pool.cc new file mode 100644 index 000000000..2e0a054c7 --- /dev/null +++ b/src/xenia/ui/vulkan/vulkan_upload_buffer_pool.cc @@ -0,0 +1,199 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h" + +#include + +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/ui/vulkan/vulkan_util.h" + +namespace xe { +namespace ui { +namespace vulkan { + +// Host-visible memory sizes are likely to be internally rounded to +// nonCoherentAtomSize (it's the memory mapping granularity, though as the map +// or flush range must be clamped to the actual allocation size as a special +// case, but it's still unlikely that the allocation won't be aligned to it), so +// try not to waste that padding. +VulkanUploadBufferPool::VulkanUploadBufferPool(const VulkanProvider& provider, + VkBufferUsageFlags usage, + size_t page_size) + : GraphicsUploadBufferPool(size_t( + util::GetMappableMemorySize(provider, VkDeviceSize(page_size)))), + provider_(provider), + usage_(usage) {} + +uint8_t* VulkanUploadBufferPool::Request(uint64_t submission_index, size_t size, + size_t alignment, VkBuffer& buffer_out, + VkDeviceSize& offset_out) { + size_t offset; + const VulkanPage* page = + static_cast(GraphicsUploadBufferPool::Request( + submission_index, size, alignment, offset)); + if (!page) { + return nullptr; + } + buffer_out = page->buffer_; + offset_out = VkDeviceSize(offset); + return reinterpret_cast(page->mapping_) + offset; +} + +uint8_t* VulkanUploadBufferPool::RequestPartial(uint64_t submission_index, + size_t size, size_t alignment, + VkBuffer& buffer_out, + VkDeviceSize& offset_out, + VkDeviceSize& size_out) { + size_t offset, size_obtained; + const VulkanPage* page = + static_cast(GraphicsUploadBufferPool::RequestPartial( + submission_index, size, alignment, offset, size_obtained)); + if (!page) { + return nullptr; + } + buffer_out = page->buffer_; + offset_out = VkDeviceSize(offset); + size_out = VkDeviceSize(size_obtained); + return reinterpret_cast(page->mapping_) + offset; +} + +GraphicsUploadBufferPool::Page* +VulkanUploadBufferPool::CreatePageImplementation() { + if (memory_type_ == kMemoryTypeUnavailable) { + // Don't try to create everything again and again if totally broken. + return nullptr; + } + + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + + VkBufferCreateInfo buffer_create_info; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.pNext = nullptr; + buffer_create_info.flags = 0; + buffer_create_info.size = VkDeviceSize(page_size_); + buffer_create_info.usage = usage_; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + buffer_create_info.queueFamilyIndexCount = 0; + buffer_create_info.pQueueFamilyIndices = nullptr; + VkBuffer buffer; + if (dfn.vkCreateBuffer(device, &buffer_create_info, nullptr, &buffer) != + VK_SUCCESS) { + XELOGE("Failed to create a Vulkan upload buffer with {} bytes", page_size_); + return nullptr; + } + + if (memory_type_ == kMemoryTypeUnknown) { + VkMemoryRequirements memory_requirements; + dfn.vkGetBufferMemoryRequirements(device, buffer, &memory_requirements); + memory_type_ = util::ChooseHostMemoryType( + provider_, memory_requirements.memoryTypeBits, false); + if (memory_type_ == UINT32_MAX) { + XELOGE( + "No host-visible memory types can store an Vulkan upload buffer with " + "{} bytes", + page_size_); + memory_type_ = kMemoryTypeUnavailable; + dfn.vkDestroyBuffer(device, buffer, nullptr); + return nullptr; + } + allocation_size_ = memory_requirements.size; + if (allocation_size_ > page_size_) { + // Try to occupy the allocation padding. If that's going to require even + // more memory for some reason, don't. + buffer_create_info.size = allocation_size_; + VkBuffer buffer_expanded; + if (dfn.vkCreateBuffer(device, &buffer_create_info, nullptr, + &buffer_expanded) == VK_SUCCESS) { + VkMemoryRequirements memory_requirements_expanded; + dfn.vkGetBufferMemoryRequirements(device, buffer_expanded, + &memory_requirements_expanded); + uint32_t memory_type_expanded = util::ChooseHostMemoryType( + provider_, memory_requirements.memoryTypeBits, false); + if (memory_requirements_expanded.size <= allocation_size_ && + memory_type_expanded != UINT32_MAX) { + page_size_ = size_t(allocation_size_); + allocation_size_ = memory_requirements_expanded.size; + memory_type_ = memory_type_expanded; + dfn.vkDestroyBuffer(device, buffer, nullptr); + buffer = buffer_expanded; + } else { + dfn.vkDestroyBuffer(device, buffer_expanded, nullptr); + } + } + } + } + + VkMemoryAllocateInfo memory_allocate_info; + VkMemoryAllocateInfo* memory_allocate_info_last = &memory_allocate_info; + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = nullptr; + memory_allocate_info.allocationSize = allocation_size_; + memory_allocate_info.memoryTypeIndex = memory_type_; + VkMemoryDedicatedAllocateInfoKHR memory_dedicated_allocate_info; + if (provider_.device_extensions().khr_dedicated_allocation) { + memory_allocate_info_last->pNext = &memory_dedicated_allocate_info; + memory_allocate_info_last = reinterpret_cast( + &memory_dedicated_allocate_info); + memory_dedicated_allocate_info.sType = + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + memory_dedicated_allocate_info.pNext = nullptr; + memory_dedicated_allocate_info.image = VK_NULL_HANDLE; + memory_dedicated_allocate_info.buffer = buffer; + } + VkDeviceMemory memory; + if (dfn.vkAllocateMemory(device, &memory_allocate_info, nullptr, &memory) != + VK_SUCCESS) { + XELOGE("Failed to allocate {} bytes of Vulkan upload buffer memory", + allocation_size_); + dfn.vkDestroyBuffer(device, buffer, nullptr); + return nullptr; + } + + if (dfn.vkBindBufferMemory(device, buffer, memory, 0) != VK_SUCCESS) { + XELOGE("Failed to bind memory to a Vulkan upload buffer with {} bytes", + page_size_); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, memory, nullptr); + return nullptr; + } + + void* mapping; + if (dfn.vkMapMemory(device, memory, 0, VK_WHOLE_SIZE, 0, &mapping) != + VK_SUCCESS) { + XELOGE("Failed to map {} bytes of Vulkan upload buffer memory", + allocation_size_); + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, memory, nullptr); + return nullptr; + } + + return new VulkanPage(provider_, buffer, memory, mapping); +} + +void VulkanUploadBufferPool::FlushPageWrites(Page* page, size_t offset, + size_t size) { + util::FlushMappedMemoryRange( + provider_, static_cast(page)->memory_, memory_type_, + VkDeviceSize(offset), allocation_size_, VkDeviceSize(size)); +} + +VulkanUploadBufferPool::VulkanPage::~VulkanPage() { + const VulkanProvider::DeviceFunctions& dfn = provider_.dfn(); + VkDevice device = provider_.device(); + dfn.vkDestroyBuffer(device, buffer_, nullptr); + // Unmapping is done implicitly when the memory is freed. + dfn.vkFreeMemory(device, memory_, nullptr); +} + +} // namespace vulkan +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_upload_buffer_pool.h b/src/xenia/ui/vulkan/vulkan_upload_buffer_pool.h new file mode 100644 index 000000000..309c44ff1 --- /dev/null +++ b/src/xenia/ui/vulkan/vulkan_upload_buffer_pool.h @@ -0,0 +1,67 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VULKAN_VULKAN_UPLOAD_BUFFER_POOL_H_ +#define XENIA_UI_VULKAN_VULKAN_UPLOAD_BUFFER_POOL_H_ + +#include "xenia/ui/graphics_upload_buffer_pool.h" +#include "xenia/ui/vulkan/vulkan_provider.h" + +namespace xe { +namespace ui { +namespace vulkan { + +class VulkanUploadBufferPool : public GraphicsUploadBufferPool { + public: + VulkanUploadBufferPool(const VulkanProvider& provider, + VkBufferUsageFlags usage, + size_t page_size = kDefaultPageSize); + + uint8_t* Request(uint64_t submission_index, size_t size, size_t alignment, + VkBuffer& buffer_out, VkDeviceSize& offset_out); + uint8_t* RequestPartial(uint64_t submission_index, size_t size, + size_t alignment, VkBuffer& buffer_out, + VkDeviceSize& offset_out, VkDeviceSize& size_out); + + protected: + Page* CreatePageImplementation() override; + + void FlushPageWrites(Page* page, size_t offset, size_t size) override; + + private: + struct VulkanPage : public Page { + // Takes ownership of the buffer and its memory and mapping. + VulkanPage(const VulkanProvider& provider, VkBuffer buffer, + VkDeviceMemory memory, void* mapping) + : provider_(provider), + buffer_(buffer), + memory_(memory), + mapping_(mapping) {} + ~VulkanPage() override; + const VulkanProvider& provider_; + VkBuffer buffer_; + VkDeviceMemory memory_; + void* mapping_; + }; + + const VulkanProvider& provider_; + + VkDeviceSize allocation_size_; + static constexpr uint32_t kMemoryTypeUnknown = UINT32_MAX; + static constexpr uint32_t kMemoryTypeUnavailable = kMemoryTypeUnknown - 1; + uint32_t memory_type_ = UINT32_MAX; + + VkBufferUsageFlags usage_; +}; + +} // namespace vulkan +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_VULKAN_VULKAN_UPLOAD_BUFFER_POOL_H_ diff --git a/src/xenia/ui/vulkan/vulkan_util.cc b/src/xenia/ui/vulkan/vulkan_util.cc index a237b45d1..f8dd5846e 100644 --- a/src/xenia/ui/vulkan/vulkan_util.cc +++ b/src/xenia/ui/vulkan/vulkan_util.cc @@ -9,496 +9,187 @@ #include "xenia/ui/vulkan/vulkan_util.h" -#include "third_party/fmt/include/fmt/format.h" -#include "xenia/base/assert.h" -#include "xenia/base/logging.h" +#include -// Implement AMD's VMA here. -#define VMA_IMPLEMENTATION -#include "xenia/ui/vulkan/vulkan_mem_alloc.h" +#include "xenia/base/assert.h" +#include "xenia/base/math.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace ui { namespace vulkan { +namespace util { -uint32_t Version::Make(uint32_t major, uint32_t minor, uint32_t patch) { - return VK_MAKE_VERSION(major, minor, patch); -} - -Version Version::Parse(uint32_t value) { - Version version; - version.major = VK_VERSION_MAJOR(value); - version.minor = VK_VERSION_MINOR(value); - version.patch = VK_VERSION_PATCH(value); - version.pretty_string = - fmt::format("{}.{}.{}", version.major, version.minor, version.patch); - return version; -} - -const char* to_string(VkFormat format) { - switch (format) { -#define STR(r) \ - case r: \ - return #r - STR(VK_FORMAT_UNDEFINED); - STR(VK_FORMAT_R4G4_UNORM_PACK8); - STR(VK_FORMAT_R4G4B4A4_UNORM_PACK16); - STR(VK_FORMAT_B4G4R4A4_UNORM_PACK16); - STR(VK_FORMAT_R5G6B5_UNORM_PACK16); - STR(VK_FORMAT_B5G6R5_UNORM_PACK16); - STR(VK_FORMAT_R5G5B5A1_UNORM_PACK16); - STR(VK_FORMAT_B5G5R5A1_UNORM_PACK16); - STR(VK_FORMAT_A1R5G5B5_UNORM_PACK16); - STR(VK_FORMAT_R8_UNORM); - STR(VK_FORMAT_R8_SNORM); - STR(VK_FORMAT_R8_USCALED); - STR(VK_FORMAT_R8_SSCALED); - STR(VK_FORMAT_R8_UINT); - STR(VK_FORMAT_R8_SINT); - STR(VK_FORMAT_R8_SRGB); - STR(VK_FORMAT_R8G8_UNORM); - STR(VK_FORMAT_R8G8_SNORM); - STR(VK_FORMAT_R8G8_USCALED); - STR(VK_FORMAT_R8G8_SSCALED); - STR(VK_FORMAT_R8G8_UINT); - STR(VK_FORMAT_R8G8_SINT); - STR(VK_FORMAT_R8G8_SRGB); - STR(VK_FORMAT_R8G8B8_UNORM); - STR(VK_FORMAT_R8G8B8_SNORM); - STR(VK_FORMAT_R8G8B8_USCALED); - STR(VK_FORMAT_R8G8B8_SSCALED); - STR(VK_FORMAT_R8G8B8_UINT); - STR(VK_FORMAT_R8G8B8_SINT); - STR(VK_FORMAT_R8G8B8_SRGB); - STR(VK_FORMAT_B8G8R8_UNORM); - STR(VK_FORMAT_B8G8R8_SNORM); - STR(VK_FORMAT_B8G8R8_USCALED); - STR(VK_FORMAT_B8G8R8_SSCALED); - STR(VK_FORMAT_B8G8R8_UINT); - STR(VK_FORMAT_B8G8R8_SINT); - STR(VK_FORMAT_B8G8R8_SRGB); - STR(VK_FORMAT_R8G8B8A8_UNORM); - STR(VK_FORMAT_R8G8B8A8_SNORM); - STR(VK_FORMAT_R8G8B8A8_USCALED); - STR(VK_FORMAT_R8G8B8A8_SSCALED); - STR(VK_FORMAT_R8G8B8A8_UINT); - STR(VK_FORMAT_R8G8B8A8_SINT); - STR(VK_FORMAT_R8G8B8A8_SRGB); - STR(VK_FORMAT_B8G8R8A8_UNORM); - STR(VK_FORMAT_B8G8R8A8_SNORM); - STR(VK_FORMAT_B8G8R8A8_USCALED); - STR(VK_FORMAT_B8G8R8A8_SSCALED); - STR(VK_FORMAT_B8G8R8A8_UINT); - STR(VK_FORMAT_B8G8R8A8_SINT); - STR(VK_FORMAT_B8G8R8A8_SRGB); - STR(VK_FORMAT_A8B8G8R8_UNORM_PACK32); - STR(VK_FORMAT_A8B8G8R8_SNORM_PACK32); - STR(VK_FORMAT_A8B8G8R8_USCALED_PACK32); - STR(VK_FORMAT_A8B8G8R8_SSCALED_PACK32); - STR(VK_FORMAT_A8B8G8R8_UINT_PACK32); - STR(VK_FORMAT_A8B8G8R8_SINT_PACK32); - STR(VK_FORMAT_A8B8G8R8_SRGB_PACK32); - STR(VK_FORMAT_A2R10G10B10_UNORM_PACK32); - STR(VK_FORMAT_A2R10G10B10_SNORM_PACK32); - STR(VK_FORMAT_A2R10G10B10_USCALED_PACK32); - STR(VK_FORMAT_A2R10G10B10_SSCALED_PACK32); - STR(VK_FORMAT_A2R10G10B10_UINT_PACK32); - STR(VK_FORMAT_A2R10G10B10_SINT_PACK32); - STR(VK_FORMAT_A2B10G10R10_UNORM_PACK32); - STR(VK_FORMAT_A2B10G10R10_SNORM_PACK32); - STR(VK_FORMAT_A2B10G10R10_USCALED_PACK32); - STR(VK_FORMAT_A2B10G10R10_SSCALED_PACK32); - STR(VK_FORMAT_A2B10G10R10_UINT_PACK32); - STR(VK_FORMAT_A2B10G10R10_SINT_PACK32); - STR(VK_FORMAT_R16_UNORM); - STR(VK_FORMAT_R16_SNORM); - STR(VK_FORMAT_R16_USCALED); - STR(VK_FORMAT_R16_SSCALED); - STR(VK_FORMAT_R16_UINT); - STR(VK_FORMAT_R16_SINT); - STR(VK_FORMAT_R16_SFLOAT); - STR(VK_FORMAT_R16G16_UNORM); - STR(VK_FORMAT_R16G16_SNORM); - STR(VK_FORMAT_R16G16_USCALED); - STR(VK_FORMAT_R16G16_SSCALED); - STR(VK_FORMAT_R16G16_UINT); - STR(VK_FORMAT_R16G16_SINT); - STR(VK_FORMAT_R16G16_SFLOAT); - STR(VK_FORMAT_R16G16B16_UNORM); - STR(VK_FORMAT_R16G16B16_SNORM); - STR(VK_FORMAT_R16G16B16_USCALED); - STR(VK_FORMAT_R16G16B16_SSCALED); - STR(VK_FORMAT_R16G16B16_UINT); - STR(VK_FORMAT_R16G16B16_SINT); - STR(VK_FORMAT_R16G16B16_SFLOAT); - STR(VK_FORMAT_R16G16B16A16_UNORM); - STR(VK_FORMAT_R16G16B16A16_SNORM); - STR(VK_FORMAT_R16G16B16A16_USCALED); - STR(VK_FORMAT_R16G16B16A16_SSCALED); - STR(VK_FORMAT_R16G16B16A16_UINT); - STR(VK_FORMAT_R16G16B16A16_SINT); - STR(VK_FORMAT_R16G16B16A16_SFLOAT); - STR(VK_FORMAT_R32_UINT); - STR(VK_FORMAT_R32_SINT); - STR(VK_FORMAT_R32_SFLOAT); - STR(VK_FORMAT_R32G32_UINT); - STR(VK_FORMAT_R32G32_SINT); - STR(VK_FORMAT_R32G32_SFLOAT); - STR(VK_FORMAT_R32G32B32_UINT); - STR(VK_FORMAT_R32G32B32_SINT); - STR(VK_FORMAT_R32G32B32_SFLOAT); - STR(VK_FORMAT_R32G32B32A32_UINT); - STR(VK_FORMAT_R32G32B32A32_SINT); - STR(VK_FORMAT_R32G32B32A32_SFLOAT); - STR(VK_FORMAT_R64_UINT); - STR(VK_FORMAT_R64_SINT); - STR(VK_FORMAT_R64_SFLOAT); - STR(VK_FORMAT_R64G64_UINT); - STR(VK_FORMAT_R64G64_SINT); - STR(VK_FORMAT_R64G64_SFLOAT); - STR(VK_FORMAT_R64G64B64_UINT); - STR(VK_FORMAT_R64G64B64_SINT); - STR(VK_FORMAT_R64G64B64_SFLOAT); - STR(VK_FORMAT_R64G64B64A64_UINT); - STR(VK_FORMAT_R64G64B64A64_SINT); - STR(VK_FORMAT_R64G64B64A64_SFLOAT); - STR(VK_FORMAT_B10G11R11_UFLOAT_PACK32); - STR(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32); - STR(VK_FORMAT_D16_UNORM); - STR(VK_FORMAT_X8_D24_UNORM_PACK32); - STR(VK_FORMAT_D32_SFLOAT); - STR(VK_FORMAT_S8_UINT); - STR(VK_FORMAT_D16_UNORM_S8_UINT); - STR(VK_FORMAT_D24_UNORM_S8_UINT); - STR(VK_FORMAT_D32_SFLOAT_S8_UINT); - STR(VK_FORMAT_BC1_RGB_UNORM_BLOCK); - STR(VK_FORMAT_BC1_RGB_SRGB_BLOCK); - STR(VK_FORMAT_BC1_RGBA_UNORM_BLOCK); - STR(VK_FORMAT_BC1_RGBA_SRGB_BLOCK); - STR(VK_FORMAT_BC2_UNORM_BLOCK); - STR(VK_FORMAT_BC2_SRGB_BLOCK); - STR(VK_FORMAT_BC3_UNORM_BLOCK); - STR(VK_FORMAT_BC3_SRGB_BLOCK); - STR(VK_FORMAT_BC4_UNORM_BLOCK); - STR(VK_FORMAT_BC4_SNORM_BLOCK); - STR(VK_FORMAT_BC5_UNORM_BLOCK); - STR(VK_FORMAT_BC5_SNORM_BLOCK); - STR(VK_FORMAT_BC6H_UFLOAT_BLOCK); - STR(VK_FORMAT_BC6H_SFLOAT_BLOCK); - STR(VK_FORMAT_BC7_UNORM_BLOCK); - STR(VK_FORMAT_BC7_SRGB_BLOCK); - STR(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK); - STR(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK); - STR(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK); - STR(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK); - STR(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK); - STR(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK); - STR(VK_FORMAT_EAC_R11_UNORM_BLOCK); - STR(VK_FORMAT_EAC_R11_SNORM_BLOCK); - STR(VK_FORMAT_EAC_R11G11_UNORM_BLOCK); - STR(VK_FORMAT_EAC_R11G11_SNORM_BLOCK); - STR(VK_FORMAT_ASTC_4x4_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_4x4_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_5x4_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_5x4_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_5x5_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_5x5_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_6x5_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_6x5_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_6x6_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_6x6_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_8x5_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_8x5_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_8x6_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_8x6_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_8x8_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_8x8_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_10x5_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_10x5_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_10x6_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_10x6_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_10x8_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_10x8_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_10x10_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_10x10_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_12x10_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_12x10_SRGB_BLOCK); - STR(VK_FORMAT_ASTC_12x12_UNORM_BLOCK); - STR(VK_FORMAT_ASTC_12x12_SRGB_BLOCK); -#undef STR - default: - return "UNKNOWN_FORMAT"; +void FlushMappedMemoryRange(const VulkanProvider& provider, + VkDeviceMemory memory, uint32_t memory_type, + VkDeviceSize offset, VkDeviceSize memory_size, + VkDeviceSize size) { + assert_false(size != VK_WHOLE_SIZE && memory_size == VK_WHOLE_SIZE); + assert_true(memory_size == VK_WHOLE_SIZE || offset <= memory_size); + assert_true(memory_size == VK_WHOLE_SIZE || size <= memory_size - offset); + if (!size || + (provider.memory_types_host_coherent() & (uint32_t(1) << memory_type))) { + return; } -} - -const char* to_string(VkPhysicalDeviceType type) { - switch (type) { -#define STR(r) \ - case r: \ - return #r - STR(VK_PHYSICAL_DEVICE_TYPE_OTHER); - STR(VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU); - STR(VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); - STR(VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU); - STR(VK_PHYSICAL_DEVICE_TYPE_CPU); -#undef STR - default: - return "UNKNOWN_DEVICE"; - } -} - -const char* to_string(VkSharingMode sharing_mode) { - switch (sharing_mode) { -#define STR(r) \ - case r: \ - return #r - STR(VK_SHARING_MODE_EXCLUSIVE); - STR(VK_SHARING_MODE_CONCURRENT); -#undef STR - default: - return "UNKNOWN_SHARING_MODE"; - } -} - -const char* to_string(VkResult result) { - switch (result) { -#define STR(r) \ - case r: \ - return #r - STR(VK_SUCCESS); - STR(VK_NOT_READY); - STR(VK_TIMEOUT); - STR(VK_EVENT_SET); - STR(VK_EVENT_RESET); - STR(VK_INCOMPLETE); - STR(VK_ERROR_OUT_OF_HOST_MEMORY); - STR(VK_ERROR_OUT_OF_DEVICE_MEMORY); - STR(VK_ERROR_INITIALIZATION_FAILED); - STR(VK_ERROR_DEVICE_LOST); - STR(VK_ERROR_MEMORY_MAP_FAILED); - STR(VK_ERROR_LAYER_NOT_PRESENT); - STR(VK_ERROR_EXTENSION_NOT_PRESENT); - STR(VK_ERROR_FEATURE_NOT_PRESENT); - STR(VK_ERROR_INCOMPATIBLE_DRIVER); - STR(VK_ERROR_TOO_MANY_OBJECTS); - STR(VK_ERROR_FORMAT_NOT_SUPPORTED); - STR(VK_ERROR_SURFACE_LOST_KHR); - STR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); - STR(VK_SUBOPTIMAL_KHR); - STR(VK_ERROR_OUT_OF_DATE_KHR); - STR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); - STR(VK_ERROR_VALIDATION_FAILED_EXT); - STR(VK_ERROR_INVALID_SHADER_NV); - STR(VK_ERROR_OUT_OF_POOL_MEMORY_KHR); - STR(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR); -#undef STR - default: - return "UNKNOWN_RESULT"; - } -} - -std::string to_flags_string(VkImageUsageFlagBits flags) { - std::string result; -#define OR_FLAG(f) \ - if (flags & f) { \ - if (!result.empty()) { \ - result += " | "; \ - } \ - result += #f; \ - } - OR_FLAG(VK_IMAGE_USAGE_TRANSFER_SRC_BIT); - OR_FLAG(VK_IMAGE_USAGE_TRANSFER_DST_BIT); - OR_FLAG(VK_IMAGE_USAGE_SAMPLED_BIT); - OR_FLAG(VK_IMAGE_USAGE_STORAGE_BIT); - OR_FLAG(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); - OR_FLAG(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); - OR_FLAG(VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT); - OR_FLAG(VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT); -#undef OR_FLAG - return result; -} - -std::string to_flags_string(VkFormatFeatureFlagBits flags) { - std::string result; -#define OR_FLAG(f) \ - if (flags & f) { \ - if (!result.empty()) { \ - result += " | "; \ - } \ - result += #f; \ - } - OR_FLAG(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); - OR_FLAG(VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT); - OR_FLAG(VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT); - OR_FLAG(VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT); - OR_FLAG(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT); - OR_FLAG(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT); - OR_FLAG(VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); - OR_FLAG(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT); - OR_FLAG(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT); - OR_FLAG(VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); - OR_FLAG(VK_FORMAT_FEATURE_BLIT_SRC_BIT); - OR_FLAG(VK_FORMAT_FEATURE_BLIT_DST_BIT); - OR_FLAG(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); - OR_FLAG(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG); - OR_FLAG(VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR); - OR_FLAG(VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR); - OR_FLAG(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT); -#undef OR_FLAG - return result; -} - -std::string to_flags_string(VkSurfaceTransformFlagBitsKHR flags) { - std::string result; -#define OR_FLAG(f) \ - if (flags & f) { \ - if (!result.empty()) { \ - result += " | "; \ - } \ - result += #f; \ - } - OR_FLAG(VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR); - OR_FLAG(VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR); - OR_FLAG(VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR); - OR_FLAG(VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR); - OR_FLAG(VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR); - OR_FLAG(VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR); - OR_FLAG(VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR); - OR_FLAG(VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR); - OR_FLAG(VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR); -#undef OR_FLAG - return result; -} - -const char* to_string(VkColorSpaceKHR color_space) { - switch (color_space) { -#define STR(r) \ - case r: \ - return #r - STR(VK_COLORSPACE_SRGB_NONLINEAR_KHR); -#undef STR - default: - return "UNKNOWN_COLORSPACE"; - } -} - -const char* to_string(VkPresentModeKHR present_mode) { - switch (present_mode) { -#define STR(r) \ - case r: \ - return #r - STR(VK_PRESENT_MODE_IMMEDIATE_KHR); - STR(VK_PRESENT_MODE_MAILBOX_KHR); - STR(VK_PRESENT_MODE_FIFO_KHR); - STR(VK_PRESENT_MODE_FIFO_RELAXED_KHR); -#undef STR - default: - return "UNKNOWN_PRESENT_MODE"; - } -} - -void FatalVulkanError(std::string error) { - xe::FatalError( - error + - "\n\n" - "Ensure you have the latest drivers for your GPU and that it supports " - "Vulkan.\n" - "\n" - "See https://xenia.jp/faq/ for more information and a list of supported " - "GPUs."); -} - -void CheckResult(VkResult result, const char* action) { - if (result) { - XELOGE("Vulkan check: {} returned {}", action, to_string(result)); - } - assert_true(result == VK_SUCCESS, action); -} - -std::pair> CheckRequirements( - const std::vector& requirements, - const std::vector& layer_infos) { - bool any_missing = false; - std::vector enabled_layers; - for (auto& requirement : requirements) { - bool found = false; - for (size_t j = 0; j < layer_infos.size(); ++j) { - auto layer_name = layer_infos[j].properties.layerName; - auto layer_version = - Version::Parse(layer_infos[j].properties.specVersion); - if (requirement.name == layer_name) { - found = true; - if (requirement.min_version > layer_infos[j].properties.specVersion) { - if (requirement.is_optional) { - XELOGVK("- optional layer {} ({}) version mismatch", layer_name, - layer_version.pretty_string); - continue; - } - XELOGE("ERROR: required layer {} ({}) version mismatch", layer_name, - layer_version.pretty_string); - any_missing = true; - break; - } - XELOGVK("- enabling layer {} ({})", layer_name, - layer_version.pretty_string); - enabled_layers.push_back(layer_name); - break; - } - } - if (!found) { - if (requirement.is_optional) { - XELOGVK("- optional layer {} not found", requirement.name); - } else { - XELOGE("ERROR: required layer {} not found", requirement.name); - any_missing = true; - } + VkMappedMemoryRange range; + range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range.pNext = nullptr; + range.memory = memory; + range.offset = offset; + range.size = size; + VkDeviceSize non_coherent_atom_size = + provider.device_properties().limits.nonCoherentAtomSize; + // On some Android implementations, nonCoherentAtomSize is 0, not 1. + if (non_coherent_atom_size > 1) { + range.offset = offset / non_coherent_atom_size * non_coherent_atom_size; + if (size != VK_WHOLE_SIZE) { + range.size = std::min(xe::round_up(offset + size, non_coherent_atom_size), + memory_size) - + range.offset; } } - return {!any_missing, enabled_layers}; + provider.dfn().vkFlushMappedMemoryRanges(provider.device(), 1, &range); } -std::pair> CheckRequirements( - const std::vector& requirements, - const std::vector& extension_properties) { - bool any_missing = false; - std::vector enabled_extensions; - for (auto& requirement : requirements) { - bool found = false; - for (size_t j = 0; j < extension_properties.size(); ++j) { - auto extension_name = extension_properties[j].extensionName; - auto extension_version = - Version::Parse(extension_properties[j].specVersion); - if (requirement.name == extension_name) { - found = true; - if (requirement.min_version > extension_properties[j].specVersion) { - if (requirement.is_optional) { - XELOGVK("- optional extension {} ({}) version mismatch", - extension_name, extension_version.pretty_string); - continue; - } - XELOGE("ERROR: required extension {} ({}) version mismatch", - extension_name, extension_version.pretty_string); - any_missing = true; - break; - } - XELOGVK("- enabling extension {} ({})", extension_name, - extension_version.pretty_string); - enabled_extensions.push_back(extension_name); - break; - } - } - if (!found) { - if (requirement.is_optional) { - XELOGVK("- optional extension {} not found", requirement.name); - } else { - XELOGE("ERROR: required extension {} not found", requirement.name); - any_missing = true; - } - } +bool CreateDedicatedAllocationBuffer( + const VulkanProvider& provider, VkDeviceSize size, VkBufferUsageFlags usage, + MemoryPurpose memory_purpose, VkBuffer& buffer_out, + VkDeviceMemory& memory_out, uint32_t* memory_type_out, + VkDeviceSize* memory_size_out) { + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); + + VkBufferCreateInfo buffer_create_info; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.pNext = nullptr; + buffer_create_info.flags = 0; + buffer_create_info.size = size; + buffer_create_info.usage = usage; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + buffer_create_info.queueFamilyIndexCount = 0; + buffer_create_info.pQueueFamilyIndices = nullptr; + VkBuffer buffer; + if (dfn.vkCreateBuffer(device, &buffer_create_info, nullptr, &buffer) != + VK_SUCCESS) { + return false; } - return {!any_missing, enabled_extensions}; + + VkMemoryRequirements memory_requirements; + dfn.vkGetBufferMemoryRequirements(device, buffer, &memory_requirements); + uint32_t memory_type = ChooseMemoryType( + provider, memory_requirements.memoryTypeBits, memory_purpose); + if (memory_type == UINT32_MAX) { + dfn.vkDestroyBuffer(device, buffer, nullptr); + return false; + } + + VkMemoryAllocateInfo memory_allocate_info; + VkMemoryAllocateInfo* memory_allocate_info_last = &memory_allocate_info; + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = nullptr; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_allocate_info.memoryTypeIndex = memory_type; + VkMemoryDedicatedAllocateInfoKHR memory_dedicated_allocate_info; + if (provider.device_extensions().khr_dedicated_allocation) { + memory_allocate_info_last->pNext = &memory_dedicated_allocate_info; + memory_allocate_info_last = reinterpret_cast( + &memory_dedicated_allocate_info); + memory_dedicated_allocate_info.sType = + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + memory_dedicated_allocate_info.pNext = nullptr; + memory_dedicated_allocate_info.image = VK_NULL_HANDLE; + memory_dedicated_allocate_info.buffer = buffer; + } + VkDeviceMemory memory; + if (dfn.vkAllocateMemory(device, &memory_allocate_info, nullptr, &memory) != + VK_SUCCESS) { + dfn.vkDestroyBuffer(device, buffer, nullptr); + return false; + } + + if (dfn.vkBindBufferMemory(device, buffer, memory, 0) != VK_SUCCESS) { + dfn.vkDestroyBuffer(device, buffer, nullptr); + dfn.vkFreeMemory(device, memory, nullptr); + return false; + } + + buffer_out = buffer; + memory_out = memory; + if (memory_type_out) { + *memory_type_out = memory_type; + } + if (memory_size_out) { + *memory_size_out = memory_allocate_info.allocationSize; + } + return true; } +bool CreateDedicatedAllocationImage(const VulkanProvider& provider, + const VkImageCreateInfo& create_info, + MemoryPurpose memory_purpose, + VkImage& image_out, + VkDeviceMemory& memory_out, + uint32_t* memory_type_out, + VkDeviceSize* memory_size_out) { + const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn(); + VkDevice device = provider.device(); + + VkImage image; + if (dfn.vkCreateImage(device, &create_info, nullptr, &image) != VK_SUCCESS) { + return false; + } + + VkMemoryRequirements memory_requirements; + dfn.vkGetImageMemoryRequirements(device, image, &memory_requirements); + uint32_t memory_type = ChooseMemoryType( + provider, memory_requirements.memoryTypeBits, memory_purpose); + if (memory_type == UINT32_MAX) { + dfn.vkDestroyImage(device, image, nullptr); + return false; + } + + VkMemoryAllocateInfo memory_allocate_info; + VkMemoryAllocateInfo* memory_allocate_info_last = &memory_allocate_info; + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = nullptr; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_allocate_info.memoryTypeIndex = memory_type; + VkMemoryDedicatedAllocateInfoKHR memory_dedicated_allocate_info; + if (provider.device_extensions().khr_dedicated_allocation) { + memory_allocate_info_last->pNext = &memory_dedicated_allocate_info; + memory_allocate_info_last = reinterpret_cast( + &memory_dedicated_allocate_info); + memory_dedicated_allocate_info.sType = + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + memory_dedicated_allocate_info.pNext = nullptr; + memory_dedicated_allocate_info.image = image; + memory_dedicated_allocate_info.buffer = VK_NULL_HANDLE; + } + VkDeviceMemory memory; + if (dfn.vkAllocateMemory(device, &memory_allocate_info, nullptr, &memory) != + VK_SUCCESS) { + dfn.vkDestroyImage(device, image, nullptr); + return false; + } + + if (dfn.vkBindImageMemory(device, image, memory, 0) != VK_SUCCESS) { + dfn.vkDestroyImage(device, image, nullptr); + dfn.vkFreeMemory(device, memory, nullptr); + return false; + } + + image_out = image; + memory_out = memory; + if (memory_type_out) { + *memory_type_out = memory_type; + } + if (memory_size_out) { + *memory_size_out = memory_allocate_info.allocationSize; + } + return true; +} + +} // namespace util } // namespace vulkan } // namespace ui } // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_util.h b/src/xenia/ui/vulkan/vulkan_util.h index d57c6c0fa..653c66e2c 100644 --- a/src/xenia/ui/vulkan/vulkan_util.h +++ b/src/xenia/ui/vulkan/vulkan_util.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,21 +10,24 @@ #ifndef XENIA_UI_VULKAN_VULKAN_UTIL_H_ #define XENIA_UI_VULKAN_VULKAN_UTIL_H_ -#include -#include -#include +#include +#include -#include "xenia/ui/vulkan/vulkan.h" - -namespace xe { -namespace ui { -class Window; -} // namespace ui -} // namespace xe +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace ui { namespace vulkan { +namespace util { + +inline void CheckResult(VkResult result, const char* action) { + if (result != VK_SUCCESS) { + XELOGE("Vulkan check: {} returned 0x{:X}", action, uint32_t(result)); + } + assert_true(result == VK_SUCCESS, action); +} template inline bool DestroyAndNullHandle(F* destroy_function, T& handle) { @@ -36,9 +39,8 @@ inline bool DestroyAndNullHandle(F* destroy_function, T& handle) { return false; } -template -inline bool DestroyAndNullHandle(F* destroy_function, VkInstance parent, - T& handle) { +template +inline bool DestroyAndNullHandle(F* destroy_function, P parent, T& handle) { if (handle != VK_NULL_HANDLE) { destroy_function(parent, handle, nullptr); handle = VK_NULL_HANDLE; @@ -47,86 +49,128 @@ inline bool DestroyAndNullHandle(F* destroy_function, VkInstance parent, return false; } -template -inline bool DestroyAndNullHandle(F* destroy_function, VkDevice parent, - T& handle) { - if (handle != VK_NULL_HANDLE) { - destroy_function(parent, handle, nullptr); - handle = VK_NULL_HANDLE; - return true; +enum class MemoryPurpose { + kDeviceLocal, + kUpload, + kReadback, +}; + +inline VkDeviceSize GetMappableMemorySize(const VulkanProvider& provider, + VkDeviceSize size) { + VkDeviceSize non_coherent_atom_size = + provider.device_properties().limits.nonCoherentAtomSize; + // On some Android implementations, nonCoherentAtomSize is 0, not 1. + if (non_coherent_atom_size > 1) { + size = xe::round_up(size, non_coherent_atom_size, false); } - return false; + return size; } -struct Version { - uint32_t major; - uint32_t minor; - uint32_t patch; - std::string pretty_string; +inline uint32_t ChooseHostMemoryType(const VulkanProvider& provider, + uint32_t supported_types, + bool is_readback) { + supported_types &= provider.memory_types_host_visible(); + uint32_t host_cached = provider.memory_types_host_cached(); + uint32_t memory_type; + // For upload, uncached is preferred so writes do not pollute the CPU cache. + // For readback, cached is preferred so multiple CPU reads are fast. + // If the preferred caching behavior is not available, pick any host-visible. + if (xe::bit_scan_forward( + supported_types & (is_readback ? host_cached : ~host_cached), + &memory_type) || + xe::bit_scan_forward(supported_types, &memory_type)) { + return memory_type; + } + return UINT32_MAX; +} - static uint32_t Make(uint32_t major, uint32_t minor, uint32_t patch); +inline uint32_t ChooseMemoryType(const VulkanProvider& provider, + uint32_t supported_types, + MemoryPurpose purpose) { + switch (purpose) { + case MemoryPurpose::kDeviceLocal: { + uint32_t memory_type; + return xe::bit_scan_forward(supported_types, &memory_type) ? memory_type + : UINT32_MAX; + } break; + case MemoryPurpose::kUpload: + case MemoryPurpose::kReadback: + return ChooseHostMemoryType(provider, supported_types, + purpose == MemoryPurpose::kReadback); + default: + assert_unhandled_case(purpose); + return UINT32_MAX; + } +} - static Version Parse(uint32_t value); -}; +// Actual memory size is required if explicit size is specified for clamping to +// the actual memory allocation size while rounding to the non-coherent atom +// size (offset + size passed to vkFlushMappedMemoryRanges inside this function +// must be either a multiple of nonCoherentAtomSize (but not exceeding the +// memory size) or equal to the memory size). +void FlushMappedMemoryRange(const VulkanProvider& provider, + VkDeviceMemory memory, uint32_t memory_type, + VkDeviceSize offset = 0, + VkDeviceSize memory_size = VK_WHOLE_SIZE, + VkDeviceSize size = VK_WHOLE_SIZE); -const char* to_string(VkFormat format); -const char* to_string(VkPhysicalDeviceType type); -const char* to_string(VkSharingMode sharing_mode); -const char* to_string(VkResult result); +inline VkExtent2D GetMax2DFramebufferExtent(const VulkanProvider& provider) { + const VkPhysicalDeviceLimits& limits = provider.device_properties().limits; + VkExtent2D max_extent; + max_extent.width = + std::min(limits.maxFramebufferWidth, limits.maxImageDimension2D); + max_extent.height = + std::min(limits.maxFramebufferHeight, limits.maxImageDimension2D); + return max_extent; +} -std::string to_flags_string(VkImageUsageFlagBits flags); -std::string to_flags_string(VkFormatFeatureFlagBits flags); -std::string to_flags_string(VkSurfaceTransformFlagBitsKHR flags); +inline void InitializeSubresourceRange( + VkImageSubresourceRange& range, + VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT, + uint32_t base_mip_level = 0, uint32_t level_count = VK_REMAINING_MIP_LEVELS, + uint32_t base_array_layer = 0, + uint32_t layer_count = VK_REMAINING_ARRAY_LAYERS) { + range.aspectMask = aspect_mask; + range.baseMipLevel = base_mip_level; + range.levelCount = level_count; + range.baseArrayLayer = base_array_layer; + range.layerCount = layer_count; +} -const char* to_string(VkColorSpaceKHR color_space); -const char* to_string(VkPresentModeKHR present_mode); +// Creates a buffer backed by a dedicated allocation. The allocation size will +// NOT be aligned to nonCoherentAtomSize - if mapping or flushing not the whole +// size, memory_size_out must be used for clamping the range. +bool CreateDedicatedAllocationBuffer( + const VulkanProvider& provider, VkDeviceSize size, VkBufferUsageFlags usage, + MemoryPurpose memory_purpose, VkBuffer& buffer_out, + VkDeviceMemory& memory_out, uint32_t* memory_type_out = nullptr, + VkDeviceSize* memory_size_out = nullptr); -// Throws a fatal error with some Vulkan help text. -void FatalVulkanError(std::string error); +bool CreateDedicatedAllocationImage(const VulkanProvider& provider, + const VkImageCreateInfo& create_info, + MemoryPurpose memory_purpose, + VkImage& image_out, + VkDeviceMemory& memory_out, + uint32_t* memory_type_out = nullptr, + VkDeviceSize* memory_size_out = nullptr); -// Logs and assets expecting the result to be VK_SUCCESS. -void CheckResult(VkResult result, const char* action); - -struct LayerInfo { - VkLayerProperties properties; - std::vector extensions; -}; - -struct DeviceInfo { - VkPhysicalDevice handle; - VkPhysicalDeviceProperties properties; - VkPhysicalDeviceFeatures features; - VkPhysicalDeviceMemoryProperties memory_properties; - std::vector queue_family_properties; - std::vector layers; - std::vector extensions; -}; - -// Defines a requirement for a layer or extension, used to both verify and -// enable them on initialization. -struct Requirement { - // Layer or extension name. - std::string name; - // Minimum required spec version of the layer or extension. - uint32_t min_version; - // True if the requirement is optional (will not cause verification to fail). - bool is_optional; -}; - -// Gets a list of enabled layer names based on the given layer requirements and -// available layer info. -// Returns a boolean indicating whether all required layers are present. -std::pair> CheckRequirements( - const std::vector& requirements, - const std::vector& layer_infos); - -// Gets a list of enabled extension names based on the given extension -// requirements and available extensions. -// Returns a boolean indicating whether all required extensions are present. -std::pair> CheckRequirements( - const std::vector& requirements, - const std::vector& extension_properties); +inline VkShaderModule CreateShaderModule(const VulkanProvider& provider, + const void* code, size_t code_size) { + VkShaderModuleCreateInfo shader_module_create_info; + shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_module_create_info.pNext = nullptr; + shader_module_create_info.flags = 0; + shader_module_create_info.codeSize = code_size; + shader_module_create_info.pCode = reinterpret_cast(code); + VkShaderModule shader_module; + return provider.dfn().vkCreateShaderModule( + provider.device(), &shader_module_create_info, nullptr, + &shader_module) == VK_SUCCESS + ? shader_module + : VK_NULL_HANDLE; +} +} // namespace util } // namespace vulkan } // namespace ui } // namespace xe diff --git a/src/xenia/ui/vulkan/vulkan_window_demo.cc b/src/xenia/ui/vulkan/vulkan_window_demo.cc index f2e0fcff8..f4ba6a669 100644 --- a/src/xenia/ui/vulkan/vulkan_window_demo.cc +++ b/src/xenia/ui/vulkan/vulkan_window_demo.cc @@ -34,7 +34,7 @@ class VulkanWindowDemoApp final : public WindowDemoApp { std::unique_ptr VulkanWindowDemoApp::CreateGraphicsProvider() const { - return VulkanProvider::Create(); + return VulkanProvider::Create(true); } } // namespace vulkan diff --git a/src/xenia/ui/window.cc b/src/xenia/ui/window.cc index 74b81ae21..ad6129307 100644 --- a/src/xenia/ui/window.cc +++ b/src/xenia/ui/window.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,347 +10,719 @@ #include "xenia/ui/window.h" #include +#include #include "third_party/imgui/imgui.h" #include "xenia/base/assert.h" #include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/presenter.h" namespace xe { namespace ui { -constexpr bool kContinuousRepaint = false; -constexpr bool kShowPresentFps = kContinuousRepaint; - -// Enables long press behaviors (context menu, etc). -constexpr bool kTouch = false; - -constexpr uint64_t kDoubleClickDelayMillis = 600; -constexpr double kDoubleClickDistance = 5; - -constexpr int32_t kMouseWheelDetent = 120; - -Window::Window(WindowedAppContext& app_context, const std::string& title) - : app_context_(app_context), title_(title) {} +Window::Window(WindowedAppContext& app_context, const std::string_view title, + uint32_t desired_logical_width, uint32_t desired_logical_height) + : app_context_(app_context), + title_(title), + desired_logical_width_(desired_logical_width), + desired_logical_height_(desired_logical_height) {} Window::~Window() { - // Context must have been cleaned up already in OnDestroy. - assert_null(context_.get()); + // In case the implementation didn't need to call EnterDestructor. Though + // that was likely a mistake, so placing an assertion. + assert_true(phase_ == Phase::kDeleting); + EnterDestructor(); + + if (presenter_) { + Presenter* old_presenter = presenter_; + // Null the pointer to prevent an infinite loop between + // SetWindowSurfaceFromUIThread and SetPresenter calling each other. + presenter_ = nullptr; + old_presenter->SetWindowSurfaceFromUIThread(nullptr, nullptr); + presenter_surface_.reset(); + } + + // Right before destruction has finished, after which no interaction with + // *this can be done, notify the destruction receivers that the window is + // being destroyed and that *this is not accessible anymore. + while (innermost_destruction_receiver_) { + innermost_destruction_receiver_->window_ = nullptr; + innermost_destruction_receiver_ = + innermost_destruction_receiver_->outer_receiver_; + } } -void Window::AttachListener(WindowListener* listener) { - if (in_listener_loop_) { - pending_listener_attaches_.push_back(listener); - return; - } - auto it = std::find(listeners_.begin(), listeners_.end(), listener); - if (it != listeners_.end()) { +void Window::AddListener(WindowListener* listener) { + assert_not_null(listener); + // Check if already added. + if (std::find(listeners_.cbegin(), listeners_.cend(), listener) != + listeners_.cend()) { return; } listeners_.push_back(listener); - Invalidate(); } -void Window::DetachListener(WindowListener* listener) { - if (in_listener_loop_) { - pending_listener_detaches_.push_back(listener); +void Window::RemoveListener(WindowListener* listener) { + assert_not_null(listener); + auto it = std::find(listeners_.cbegin(), listeners_.cend(), listener); + if (it == listeners_.cend()) { return; } - auto it = std::find(listeners_.begin(), listeners_.end(), listener); - if (it == listeners_.end()) { - return; + // Actualize the next listener indices after the erasure from the vector. + ListenerIterationContext* iteration_context = + innermost_listener_iteration_context_; + if (iteration_context) { + size_t existing_index = size_t(std::distance(listeners_.cbegin(), it)); + while (iteration_context) { + if (iteration_context->next_index > existing_index) { + --iteration_context->next_index; + } + iteration_context = iteration_context->outer_context; + } } listeners_.erase(it); } -void Window::ForEachListener(std::function fn) { - assert_false(in_listener_loop_); - in_listener_loop_ = true; - for (auto listener : listeners_) { - fn(listener); +void Window::AddInputListener(WindowInputListener* listener, size_t z_order) { + assert_not_null(listener); + // Check if already added. + for (auto it_existing = input_listeners_.rbegin(); + it_existing != input_listeners_.rend(); ++it_existing) { + if (it_existing->second != listener) { + continue; + } + if (it_existing->first == z_order) { + return; + } + // If removing the listener that is the next in a current listener loop, + // skip it (in a multimap, only one element iterator is invalidated). + InputListenerIterationContext* iteration_context = + innermost_input_listener_iteration_context_; + while (iteration_context) { + if (iteration_context->next_iterator == it_existing) { + ++iteration_context->next_iterator; + } + iteration_context = iteration_context->outer_context; + } + input_listeners_.erase(std::prev(it_existing.base())); + // FIXME(Triang3l): Changing the Z order of an existing listener while + // already executing the listeners may cause listeners to be called twice if + // the Z order is lowered from one that has already been processed to one + // below the current one. Because nested listener calls are supported, a + // single last call index can't be associated with a listener to skip it if + // calling twice for the same event (a "not equal to" comparison of the call + // indices will result in the skipping being cancelled in the outer loop if + // an inner one is done, a "greater than" comparison will cause the inner + // loop to effectively terminate all outer ones). } - in_listener_loop_ = false; - while (!pending_listener_attaches_.empty()) { - auto listener = pending_listener_attaches_.back(); - pending_listener_attaches_.pop_back(); - AttachListener(listener); - } - while (!pending_listener_detaches_.empty()) { - auto listener = pending_listener_detaches_.back(); - pending_listener_detaches_.pop_back(); - DetachListener(listener); - } -} - -void Window::TryForEachListener(std::function fn) { - assert_false(in_listener_loop_); - in_listener_loop_ = true; - for (auto listener : listeners_) { - if (fn(listener)) { - break; + auto it_new = std::prev( + std::make_reverse_iterator(input_listeners_.emplace(z_order, listener))); + // If adding to layers in between the currently being processed ones (from + // highest to lowest) and the previously next, make sure the new listener is + // executed too. Execution within one layer, however, happens in the reverse + // order of addition, so if adding to the Z layer currently being processed, + // the new listener must not be executed within the loop. But, if adding to + // the next Z layer after the current one, it must be executed immediately. + { + InputListenerIterationContext* iteration_context = + innermost_input_listener_iteration_context_; + while (iteration_context) { + if (z_order < iteration_context->current_z_order && + (iteration_context->next_iterator == input_listeners_.crend() || + z_order >= iteration_context->next_iterator->first)) { + iteration_context->next_iterator = it_new; + } + iteration_context = iteration_context->outer_context; } } - in_listener_loop_ = false; - while (!pending_listener_attaches_.empty()) { - auto listener = pending_listener_attaches_.back(); - pending_listener_attaches_.pop_back(); - AttachListener(listener); - } - while (!pending_listener_detaches_.empty()) { - auto listener = pending_listener_detaches_.back(); - pending_listener_detaches_.pop_back(); - DetachListener(listener); - } } -void Window::set_imgui_input_enabled(bool value) { - if (value == is_imgui_input_enabled_) { +void Window::RemoveInputListener(WindowInputListener* listener) { + assert_not_null(listener); + for (auto it_existing = input_listeners_.rbegin(); + it_existing != input_listeners_.rend(); ++it_existing) { + if (it_existing->second != listener) { + continue; + } + // If removing the listener that is the next in a current listener loop, + // skip it (in a multimap, only one element iterator is invalidated). + InputListenerIterationContext* iteration_context = + innermost_input_listener_iteration_context_; + while (iteration_context) { + if (iteration_context->next_iterator == it_existing) { + ++iteration_context->next_iterator; + } + iteration_context = iteration_context->outer_context; + } + input_listeners_.erase(std::prev(it_existing.base())); return; } - is_imgui_input_enabled_ = value; - if (!value) { - DetachListener(imgui_drawer_.get()); - } else { - AttachListener(imgui_drawer_.get()); - } } -bool Window::OnCreate() { return true; } +bool Window::Open() { + if (phase_ != Phase::kClosedOpenable) { + return true; + } -bool Window::MakeReady() { - imgui_drawer_ = std::make_unique(this); + // For consistency of the behavior of OpenImpl and the initial On*Update + // that should be called as a result of it, reset the actual state to its + // defaults for a closed window. + // Note that this is performed in Open, not after closing, because there's + // only one entry point for opening a window, while closing may be done + // different ways - by actually closing, by destroying, or by failing to call + // OpenImpl - instead of performing this reset in every possible close case, + // just returning these defaults from the actual state getters if + // HasActualState is false. + actual_physical_width_ = 0; + actual_physical_height_ = 0; + has_focus_ = false; + phase_ = Phase::kOpening; + bool platform_open_result = OpenImpl(); + if (!platform_open_result) { + phase_ = Phase::kClosedOpenable; + return false; + } + if (phase_ != Phase::kOpening) { + // The window was closed mid-opening. + return true; + } + phase_ = Phase::kOpen; + + // Call the listeners (OnOpened with all the new state so the listeners are + // aware that they can start interacting with the open Window, and after that, + // in case certain listeners don't handle OnOpened, but rather, only need the + // more granular callbacks, make sure those callbacks receive the new state + // too) for the actual state of the new window (that may be different than the + // desired, depending on how the platform has interpreted the desired state). + { + MonitorUpdateEvent e(this, true, true); + OnMonitorUpdate(e); + } + { + UISetupEvent e(this, true); + WindowDestructionReceiver destruction_receiver(this); + SendEventToListeners([&e](auto listener) { listener->OnOpened(e); }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrListenersUncallable()) { + return true; + } + SendEventToListeners([&e](auto listener) { listener->OnDpiChanged(e); }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrListenersUncallable()) { + return true; + } + SendEventToListeners([&e](auto listener) { listener->OnResize(e); }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrListenersUncallable()) { + return true; + } + if (HasFocus()) { + SendEventToListeners([&e](auto listener) { listener->OnGotFocus(e); }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrListenersUncallable()) { + return true; + } + } + } + + // May now try to create a valid surface (though the window may be in a + // minimized state without the possibility of creating a surface, but that + // will be resolved by the implementation), after OnDpiChanged and OnResize so + // nothing related to painting will be making wrong assumptions about the + // size. + OnSurfaceChanged(true); return true; } -void Window::OnMainMenuChange() { - ForEachListener([](auto listener) { listener->OnMainMenuChange(); }); +void Window::SetFullscreen(bool new_fullscreen) { + if (fullscreen_ == new_fullscreen) { + return; + } + fullscreen_ = new_fullscreen; + if (!CanApplyState()) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + ApplyNewFullscreen(); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } } -void Window::OnClose() { - UIEvent e(this); - ForEachListener([&e](auto listener) { listener->OnClosing(&e); }); - on_closing(&e); - ForEachListener([&e](auto listener) { listener->OnClosed(&e); }); - on_closed(&e); +void Window::SetTitle(const std::string_view new_title) { + if (title_ == new_title) { + return; + } + title_ = new_title; + if (!CanApplyState()) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + ApplyNewTitle(); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } } -void Window::OnDestroy() { - if (!context_) { +void Window::SetIcon(const void* buffer, size_t size) { + bool reset = !buffer || !size; + WindowDestructionReceiver destruction_receiver(this); + LoadAndApplyIcon(reset ? nullptr : buffer, reset ? 0 : size, CanApplyState()); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } +} + +void Window::SetMainMenu(std::unique_ptr new_main_menu) { + // The primary reason for this comparison (of two unique pointers) is + // nullptr == nullptr. + if (main_menu_ == new_main_menu) { + return; + } + // Keep the old menu object existing while it's still being detached from + // the platform window. + std::unique_ptr old_main_menu = std::move(main_menu_); + main_menu_ = std::move(new_main_menu); + if (!CanApplyState()) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + ApplyNewMainMenu(old_main_menu.get()); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } +} + +void Window::CompleteMainMenuItemsUpdate() { + if (!main_menu_ || !CanApplyState()) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + CompleteMainMenuItemsUpdateImpl(); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } +} + +void Window::SetMainMenuEnabled(bool enabled) { + if (!main_menu_) { + return; + } + // Not comparing to the actual enabled state, it's a part of the MenuItem, not + // the Window. + // In case enabling (or even disabling) causes menu-related events (like + // pressing) that may execute callbacks potentially destroying the Window via + // the outer architecture. + WindowDestructionReceiver destruction_receiver(this); + main_menu_->SetEnabled(enabled); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } + // Modifying the state of the items, notify the implementation so it makes the + // displaying of the main menu consistent. + CompleteMainMenuItemsUpdate(); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } +} + +void Window::CaptureMouse() { + ++mouse_capture_request_count_; + if (!CanApplyState()) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + // Call even if capturing while the mouse is already assumed to be captured, + // in case something has released it in the OS. + ApplyNewMouseCapture(); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } +} + +void Window::ReleaseMouse() { + assert_not_zero(mouse_capture_request_count_); + if (!mouse_capture_request_count_) { + return; + } + if (--mouse_capture_request_count_) { + return; + } + if (!CanApplyState()) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + ApplyNewMouseRelease(); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } +} + +void Window::SetCursorVisibility(CursorVisibility new_cursor_visibility) { + if (cursor_visibility_ == new_cursor_visibility) { + return; + } + CursorVisibility old_cursor_visibility = cursor_visibility_; + cursor_visibility_ = new_cursor_visibility; + if (!CanApplyState()) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + ApplyNewCursorVisibility(old_cursor_visibility); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } +} + +void Window::Focus() { + if (!CanApplyState() || has_focus_) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + FocusImpl(); + if (destruction_receiver.IsWindowDestroyedOrStateInapplicable()) { + return; + } +} + +void Window::SetPresenter(Presenter* presenter) { + if (presenter_ == presenter) { + return; + } + if (presenter_) { + presenter_->SetWindowSurfaceFromUIThread(nullptr, nullptr); + presenter_surface_.reset(); + } + presenter_ = presenter; + if (presenter_) { + presenter_surface_ = CreateSurface(presenter_->GetSupportedSurfaceTypes()); + presenter_->SetWindowSurfaceFromUIThread(this, presenter_surface_.get()); + } +} + +void Window::OnSurfaceChanged(bool new_surface_potentially_exists) { + if (!presenter_) { return; } - imgui_drawer_.reset(); + // Detach the presenter from the old surface before attaching to the new one. + if (presenter_surface_) { + presenter_->SetWindowSurfaceFromUIThread(this, nullptr); + presenter_surface_.reset(); + } - // Context must go last. - context_.reset(); -} - -void Window::Layout() { - UIEvent e(this); - OnLayout(&e); -} - -void Window::Invalidate() {} - -void Window::OnDpiChanged(UIEvent* e) { - // TODO(DrChat): Notify listeners. -} - -void Window::OnResize(UIEvent* e) { - ForEachListener([e](auto listener) { listener->OnResize(e); }); -} - -void Window::OnLayout(UIEvent* e) { - ForEachListener([e](auto listener) { listener->OnLayout(e); }); -} - -void Window::OnPaint(UIEvent* e) { - if (!context_) { + if (!new_surface_potentially_exists) { return; } - ++frame_count_; - ++fps_frame_count_; - static auto tick_frequency = Clock::QueryHostTickFrequency(); - auto now_ticks = Clock::QueryHostTickCount(); - // Average fps over 1 second. - if (now_ticks > fps_update_time_ticks_ + tick_frequency * 1) { - fps_ = static_cast( - fps_frame_count_ / - (static_cast(now_ticks - fps_update_time_ticks_) / - tick_frequency)); - fps_update_time_ticks_ = now_ticks; - fps_frame_count_ = 0; + presenter_surface_ = CreateSurface(presenter_->GetSupportedSurfaceTypes()); + if (presenter_surface_) { + presenter_->SetWindowSurfaceFromUIThread(this, presenter_surface_.get()); + } +} + +void Window::OnBeforeClose(WindowDestructionReceiver& destruction_receiver) { + // Because events are not sent from closed windows, and to make sure the + // window isn't closed while its surface is still attached to the presenter, + // this must be called before doing what constitutes closing in the platform + // implementation, not after. + + bool was_open = phase_ == Phase::kOpen; + bool was_open_or_opening = was_open || phase_ == Phase::kOpening; + assert_true(was_open_or_opening); + if (!was_open_or_opening) { + return; } - GraphicsContextLock context_lock(context_.get()); + if (was_open) { + phase_ = Phase::kOpenBeforeClosing; - // Prepare ImGui for use this frame. - auto& io = imgui_drawer_->GetIO(); - if (!last_paint_time_ticks_) { - io.DeltaTime = 0.0f; - last_paint_time_ticks_ = now_ticks; + // If the window was focused, notify the listeners that focus is being lost + // because the window is being closed (the implementation usually wouldn't + // be sending any events to the listeners after OnClosing even if the OS + // actually sends the focus loss event as part of the closing process). + OnFocusUpdate(false, destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } + + { + UIEvent e(this); + SendEventToListeners([&e](auto listener) { listener->OnClosing(e); }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } + } + + // Disconnect from the surface without connecting to the new one (after the + // listeners so they don't try to reconnect afterwards). + OnSurfaceChanged(false); + } + + phase_ = Phase::kClosing; +} + +void Window::OnAfterClose() { + assert_true(phase_ == Phase::kClosing); + if (phase_ != Phase::kClosing) { + return; + } + phase_ = (innermost_listener_iteration_context_ || + innermost_input_listener_iteration_context_) + ? Phase::kClosedLeavingListeners + : Phase::kClosedOpenable; +} + +void Window::OnDpiChanged(UISetupEvent& e, + WindowDestructionReceiver& destruction_receiver) { + SendEventToListeners([&e](auto listener) { listener->OnDpiChanged(e); }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } +} + +void Window::OnMonitorUpdate(MonitorUpdateEvent& e) { + if (presenter_surface_) { + presenter_->OnSurfaceMonitorUpdateFromUIThread( + e.old_monitor_potentially_disconnected()); + } +} + +bool Window::OnActualSizeUpdate( + uint32_t new_physical_width, uint32_t new_physical_height, + WindowDestructionReceiver& destruction_receiver) { + if (actual_physical_width_ == new_physical_width && + actual_physical_height_ == new_physical_height) { + return false; + } + actual_physical_width_ = new_physical_width; + actual_physical_height_ = new_physical_height; + // The listeners may reference the presenter, update the presenter first. + if (presenter_surface_) { + presenter_->OnSurfaceResizeFromUIThread(); + } + UISetupEvent e(this); + SendEventToListeners([&e](auto listener) { listener->OnResize(e); }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return true; + } + return true; +} + +void Window::OnFocusUpdate(bool new_has_focus, + WindowDestructionReceiver& destruction_receiver) { + if (has_focus_ == new_has_focus) { + return; + } + has_focus_ = new_has_focus; + UISetupEvent e(this); + if (has_focus_) { + SendEventToListeners([&e](auto listener) { listener->OnGotFocus(e); }, + destruction_receiver); } else { - io.DeltaTime = (now_ticks - last_paint_time_ticks_) / - static_cast(tick_frequency); - last_paint_time_ticks_ = now_ticks; + SendEventToListeners([&e](auto listener) { listener->OnLostFocus(e); }, + destruction_receiver); } - io.DisplaySize = ImVec2(static_cast(scaled_width()), - static_cast(scaled_height())); - - bool can_swap = context_->BeginSwap(); - if (context_->WasLost()) { - on_context_lost(e); + if (destruction_receiver.IsWindowDestroyed()) { return; } - if (!can_swap) { - // Surface not available. +} + +void Window::OnPaint(bool force_paint) { + if (is_painting_) { return; } + is_painting_ = true; + if (presenter_surface_) { + presenter_->PaintFromUIThread(force_paint); + } + is_painting_ = false; +} - ImGui::NewFrame(); - - ForEachListener([e](auto listener) { listener->OnPainting(e); }); - on_painting(e); - ForEachListener([e](auto listener) { listener->OnPaint(e); }); - on_paint(e); - - // Flush ImGui buffers before we swap. - ImGui::Render(); - imgui_drawer_->RenderDrawLists(); - - ForEachListener([e](auto listener) { listener->OnPainted(e); }); - on_painted(e); - - context_->EndSwap(); - - // If animations are running, reinvalidate immediately. - if (kContinuousRepaint) { - Invalidate(); +void Window::OnFileDrop(FileDropEvent& e, + WindowDestructionReceiver& destruction_receiver) { + SendEventToListeners([&e](auto listener) { listener->OnFileDrop(e); }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; } } -void Window::OnFileDrop(FileDropEvent* e) { - on_file_drop(e); - ForEachListener([e](auto listener) { listener->OnFileDrop(e); }); +void Window::OnKeyDown(KeyEvent& e, + WindowDestructionReceiver& destruction_receiver) { + PropagateEventThroughInputListeners( + [&e](auto listener) { + listener->OnKeyDown(e); + return e.is_handled(); + }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } } -void Window::OnVisible(UIEvent* e) { - ForEachListener([e](auto listener) { listener->OnVisible(e); }); +void Window::OnKeyUp(KeyEvent& e, + WindowDestructionReceiver& destruction_receiver) { + PropagateEventThroughInputListeners( + [&e](auto listener) { + listener->OnKeyUp(e); + return e.is_handled(); + }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } } -void Window::OnHidden(UIEvent* e) { - ForEachListener([e](auto listener) { listener->OnHidden(e); }); +void Window::OnKeyChar(KeyEvent& e, + WindowDestructionReceiver& destruction_receiver) { + PropagateEventThroughInputListeners( + [&e](auto listener) { + listener->OnKeyChar(e); + return e.is_handled(); + }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } } -void Window::OnGotFocus(UIEvent* e) { - ForEachListener([e](auto listener) { listener->OnGotFocus(e); }); +void Window::OnMouseDown(MouseEvent& e, + WindowDestructionReceiver& destruction_receiver) { + PropagateEventThroughInputListeners( + [&e](auto listener) { + listener->OnMouseDown(e); + return e.is_handled(); + }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } } -void Window::OnLostFocus(UIEvent* e) { - modifier_shift_pressed_ = false; - modifier_cntrl_pressed_ = false; - modifier_alt_pressed_ = false; - modifier_super_pressed_ = false; - ForEachListener([e](auto listener) { listener->OnLostFocus(e); }); +void Window::OnMouseMove(MouseEvent& e, + WindowDestructionReceiver& destruction_receiver) { + PropagateEventThroughInputListeners( + [&e](auto listener) { + listener->OnMouseMove(e); + return e.is_handled(); + }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } } -void Window::OnKeyPress(KeyEvent* e, bool is_down, bool is_char) { - if (!is_char) { - switch (e->virtual_key()) { - case VirtualKey::kShift: - modifier_shift_pressed_ = is_down; - break; - case VirtualKey::kControl: - modifier_cntrl_pressed_ = is_down; - break; - case VirtualKey::kMenu: - modifier_alt_pressed_ = is_down; - break; - case VirtualKey::kLWin: - modifier_super_pressed_ = is_down; - break; - default: - break; +void Window::OnMouseUp(MouseEvent& e, + WindowDestructionReceiver& destruction_receiver) { + PropagateEventThroughInputListeners( + [&e](auto listener) { + listener->OnMouseUp(e); + return e.is_handled(); + }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } +} + +void Window::OnMouseWheel(MouseEvent& e, + WindowDestructionReceiver& destruction_receiver) { + PropagateEventThroughInputListeners( + [&e](auto listener) { + listener->OnMouseWheel(e); + return e.is_handled(); + }, + destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } +} + +void Window::SendEventToListeners( + std::function fn, + WindowDestructionReceiver& destruction_receiver) { + if (!CanSendEventsToListeners()) { + return; + } + ListenerIterationContext iteration_context( + innermost_listener_iteration_context_); + innermost_listener_iteration_context_ = &iteration_context; + while (iteration_context.next_index < listeners_.size()) { + // iteration_context.next_index may be changed during the execution of the + // listener if the list of the listeners is modified by it - don't assume + // that after the call iteration_context.next_index will be the same as + // iteration_context.next_index + 1 before it. + fn(listeners_[iteration_context.next_index++]); + if (destruction_receiver.IsWindowDestroyed()) { + // The window was destroyed by the listener, can't access anything in + // *this anymore, including innermost_listener_iteration_context_ which + // has to be left in an indeterminate state. + return; + } + if (!CanSendEventsToListeners()) { + // The listener has put the window in a state in which the window can't + // send events anymore. + break; } } + assert_true(innermost_listener_iteration_context_ == &iteration_context); + innermost_listener_iteration_context_ = + innermost_listener_iteration_context_->outer_context; + if (phase_ == Phase::kClosedLeavingListeners && + !innermost_listener_iteration_context_ && + !innermost_input_listener_iteration_context_) { + phase_ = Phase::kClosedOpenable; + } } -void Window::OnKeyDown(KeyEvent* e) { - on_key_down(e); - if (e->is_handled()) { +void Window::PropagateEventThroughInputListeners( + std::function fn, + WindowDestructionReceiver& destruction_receiver) { + if (!CanSendEventsToListeners()) { return; } - TryForEachListener([e](auto listener) { - listener->OnKeyDown(e); - return e->is_handled(); - }); - OnKeyPress(e, true, false); -} - -void Window::OnKeyUp(KeyEvent* e) { - on_key_up(e); - if (e->is_handled()) { - return; + InputListenerIterationContext iteration_context( + innermost_input_listener_iteration_context_, input_listeners_.crbegin()); + innermost_input_listener_iteration_context_ = &iteration_context; + while (iteration_context.next_iterator != input_listeners_.crend()) { + // The current iterator may be invalidated, and + // iteration_context.next_iterator may be changed, during the execution of + // the listener if the list of the listeners is modified by it - don't + // assume that after the call iteration_context.next_iterator will be the + // same as std::next(iteration_context.next_iterator) before it. + iteration_context.current_z_order = iteration_context.next_iterator->first; + bool event_handled = fn((iteration_context.next_iterator++)->second); + if (destruction_receiver.IsWindowDestroyed()) { + // The window was destroyed by the listener, can't access anything in + // *this anymore, including innermost_listener_iteration_context_ which + // has to be left in an indeterminate state. + return; + } + if (event_handled) { + break; + } + if (!CanSendEventsToListeners()) { + // The listener has put the window in a state in which the window can't + // send events anymore. + break; + } } - TryForEachListener([e](auto listener) { - listener->OnKeyUp(e); - return e->is_handled(); - }); - OnKeyPress(e, false, false); -} - -void Window::OnKeyChar(KeyEvent* e) { - OnKeyPress(e, true, true); - on_key_char(e); - ForEachListener([e](auto listener) { listener->OnKeyChar(e); }); - OnKeyPress(e, false, true); -} - -void Window::OnMouseDown(MouseEvent* e) { - on_mouse_down(e); - if (e->is_handled()) { - return; + assert_true(innermost_input_listener_iteration_context_ == + &iteration_context); + innermost_input_listener_iteration_context_ = + innermost_input_listener_iteration_context_->outer_context; + if (phase_ == Phase::kClosedLeavingListeners && + !innermost_listener_iteration_context_ && + !innermost_input_listener_iteration_context_) { + phase_ = Phase::kClosedOpenable; } - TryForEachListener([e](auto listener) { - listener->OnMouseDown(e); - return e->is_handled(); - }); -} - -void Window::OnMouseMove(MouseEvent* e) { - on_mouse_move(e); - if (e->is_handled()) { - return; - } - TryForEachListener([e](auto listener) { - listener->OnMouseMove(e); - return e->is_handled(); - }); -} - -void Window::OnMouseUp(MouseEvent* e) { - on_mouse_up(e); - if (e->is_handled()) { - return; - } - TryForEachListener([e](auto listener) { - listener->OnMouseUp(e); - return e->is_handled(); - }); -} - -void Window::OnMouseWheel(MouseEvent* e) { - on_mouse_wheel(e); - if (e->is_handled()) { - return; - } - TryForEachListener([e](auto listener) { - listener->OnMouseWheel(e); - return e->is_handled(); - }); } } // namespace ui diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index 2627c7ca0..564eaeaea 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,209 +10,711 @@ #ifndef XENIA_UI_WINDOW_H_ #define XENIA_UI_WINDOW_H_ +#include +#include +#include #include #include +#include -#include "xenia/base/delegate.h" #include "xenia/base/platform.h" -#include "xenia/ui/graphics_context.h" #include "xenia/ui/menu_item.h" +#include "xenia/ui/presenter.h" +#include "xenia/ui/surface.h" #include "xenia/ui/ui_event.h" +#include "xenia/ui/virtual_key.h" #include "xenia/ui/window_listener.h" #include "xenia/ui/windowed_app_context.h" namespace xe { namespace ui { -typedef void* NativePlatformHandle; -typedef void* NativeWindowHandle; - -class ImGuiDrawer; - class Window { public: - static std::unique_ptr Create(WindowedAppContext& app_context_, - const std::string& title); + // Transitions between these phases is sequential and looped (and >= and <= + // can be used for openness checks, for closedness checks ! of >= and <= is + // needed due to looping), with the exception of kDeleting that may be entered + // from any other state (with an assertion for that not being done during + // kOpening though as that's extremely dangerous and would require a lot of + // handling in OpenImpl on all platforms) as the Window object may be deleted + // externally at any moment, and the Window will have no control anymore when + // that happens. Another exception is that the window may be closed in the + // platform during kOpening - in this case, it will skip kOpenBeforeClosing + // and go directly to kClosing and beyond. + enum class Phase { + // The window hasn't been opened yet, or has been fully closed and can be + // reopened. + // No native window - external state updates change only the desired state, + // don't go to the implementation immediately. + // No actual state. + // Listeners are not called. + kClosedOpenable, + // OpenImpl is being invoked, the implementation is performing initial + // setup. + // External state changes are functionally near-impossible as OpenImpl + // mostly isn't able to communicate with external Xenia code that may have + // any effect on the Window, to avoid interference during the native window + // setup which may be pretty complex - and also, this is the phase in which + // the native window is being created, so there's no guarantee that the + // native window exists throughout this phase. + // However, it's still not strictly enforceable that OpenImpl will not cause + // any interaction with the Window - it may happen, for instance, if + // OpenImpl causes the OS to execute the pending functions in the + // WindowedAppContext that might have been, for example, left over from when + // the window was still open last time. Therefore, for the reason of state + // consistency within OpenImpl, all external state changes are simply + // dropped in this phase. + // However, the desired state may be updated by the implementation during + // OpenImpl still, but via the Update functions (if the implementation or + // the OS, for instance, clamps the size of the window during creation, or + // refuses to enter fullscreen). + // Actual state is first populated during this phase, and it's readable + // (with its level of completeness at the specific point in time) for the + // internal purposes of the implementation. + // Listeners are not called so the implementation can perform all the setup + // without outer interference (the common code calls some to let the + // listeners be aware of the initial state when the window enters kOpen + // anyway). + // Note: Closing may occur in the platform during kOpening - skip + // kOpenBeforeClosing in this case while closing. + kOpening, + // Fully interactive. + // The native window exists, external state changes are applied immediately + // (or at least immediately requested to be applied shortly after, but the + // implementation must make sure that, for instance, if SetFullscreen is + // successfully called, but the window will actually enter fullscreen only + // at the next platform event loop tick, IsFullscreen will start returning + // true immediately). + // The desired state can be updated as feedback from the implementation. + // Actual state can be retrieved. + // Listeners are called. + // The only state in which a non-null Surface can be created (it's destroyed + // after entering kClosing). + kOpen, + // OnBeforeClose is being invoked. + // The native window still exists, this is mostly like kOpen, but with no + // way of creating a Surface (it's becoming destroyed in this state so the + // native window can be destroyed safely in the next phase) or recursively + // requesting closing - for consistency during closing within the platform, + // state changes also behave like in kOpen. + // Listeners are still called as normal, primarily because this is where the + // OnClosing listener function is invoked, but reopening the window from a + // listener is not possible for state consistency during closing. + kOpenBeforeClosing, + // OnBeforeClose has completed, but OnAfterClose hasn't been called yet for + // the implementation to confirm that it has finished destroying the native + // window being closed. This state exists to prevent the situation in which + // the Window is somehow reopened in the middle of the implementation's + // internal work in closing. + // The implementation must detach from the native window and destroy it in + // this phase. + // The native window is being destroyed - external state updates change only + // the desired state, don't go to the implementation immediately. + // Actual state is still queryable for internal purposes of the + // implementation. + // Listeners are not called. + kClosing, + // OnBeforeClose has completed, but the close has occurred from a listener, + // and the call stack of listeners still hasn't been exited. + // This state exists to prevent the situation in which the Window is being + // closed and then reopened in the middle of the implementation's internal + // work in event handlers. For example, let's assume putting a window (HWND) + // in the fullscreen state requires removing window decorations + // (SetWindowLong) and resizing the window (SetWindowPos), both being able + // to invoke the resize handler (WM_SIZE) and thus the resize listener. In + // this case, consider the following situation: + // - SetWindowLong called for HWND. + // - WM_SIZE arrives. + // - Listener's OnResize called. + // - Listener's OnResize does RequestClose, destroying the current HWND. + // - Listener's OnResize does Open, creating a new HWND. + // - SetWindowPos called for HWND, which is different now. + // Here, SetWindowLong will be called for one native window, but then it + // will be replaced, and SetWindowPos will be called for a different one. + // No native window - external state updates change only the desired state, + // don't go to the implementation immediately. + // No actual state. + // Listeners are not called. + kClosedLeavingListeners, + // The destructor has been called - should transition into this before doing + // anything not only in the common, but in the implementation's destructor + // as well. The transition must be done regardless of the previous phase as + // there's no way the Window can stop its destruction from now on. This is + // mostly similar to kClosedLeavingListeners, except there's no way to leave + // this phase. + kDeleting, + }; + + enum class CursorVisibility { + kVisible, + // Temporarily revealed, hidden if not interacting with the mouse. + kAutoHidden, + kHidden, + }; + + static std::unique_ptr Create(WindowedAppContext& app_context, + const std::string_view title, + uint32_t desired_logical_width, + uint32_t desired_logical_height); virtual ~Window(); WindowedAppContext& app_context() const { return app_context_; } - virtual NativePlatformHandle native_platform_handle() const = 0; - virtual NativeWindowHandle native_handle() const = 0; - - MenuItem* main_menu() const { return main_menu_.get(); } - void set_main_menu(std::unique_ptr main_menu) { - main_menu_ = std::move(main_menu); - OnMainMenuChange(); + Phase phase() const { return phase_; } + bool HasActualState() const { + // kOpening - for internal use by the implementation. + // kOpen, kOpenBeforeClosing - for both external and internal use. + // kClosing - for internal use by the implementation. + return phase_ >= Phase::kOpening && phase_ <= Phase::kClosing; } - virtual void EnableMainMenu() = 0; - virtual void DisableMainMenu() = 0; + void AddListener(WindowListener* listener); + void RemoveListener(WindowListener* listener); + void AddInputListener(WindowInputListener* listener, size_t z_order); + void RemoveInputListener(WindowInputListener* listener); - const std::string& title() const { return title_; } - virtual bool set_title(const std::string_view title) { - if (title == title_) { - return false; + // `false` is returned only in case of an error while trying to perform the + // platform window opening. If the window is already open, or just can't be + // reopened in the current phase (as in this case it's assumed that the outer + // will handle this situation properly and won't, for instance, leave a + // process without windows - it will quit the application in OnBeforeClose of + // the window closing of which was initiated before or even during opening, + // for instance), `true` is returned. The functions of WindowListeners will be + // called only for a newly opened platform window - and the listeners may + // close or even destroy the window, in which case this function will still + // return `true` to differentiate from an actual error - if it's really + // necessary that the platform window is open after the call, check phase() + // after calling. + bool Open(); + // The call may or may not close the window immediately, depending on the + // platform (phase() may still return an open phase, and events may still be + // sent after the call). Use phase() to check if closing has actually + // happened immediately. + void RequestClose() { + // Don't allow external close requests during opening for state consistency + // inside OpenImpl (if an internal close happens during OpenImpl, the + // implementation will be aware of that at least), and don't allow closing + // twice. + if (phase_ != Phase::kOpen) { + return; } - title_ = title; - return true; + RequestCloseImpl(); + // Must not doing anything else with *this as callbacks might have been + // triggered during closing (if it has actually even happened), and the + // Window might have been deleted. } - virtual bool SetIcon(const void* buffer, size_t size) = 0; + // The `public` state setters are for calling from outside. + // The implementation must use the public getters to obtain the desired state + // while applying, but for updating the actual state, or for overriding the + // desired state, the `protected` On*Update functions must be used (overall + // the On* functions are for the implementation's feedback). + + virtual uint32_t GetMediumDpi() const { return 96; } + uint32_t GetDpi() const { + uint32_t dpi = GetLatestDpiImpl(); + return dpi ? dpi : GetMediumDpi(); + } + + // Round trips are not guaranteed to return the same results. + static constexpr uint32_t ConvertSizeDpi(uint32_t size, uint32_t new_dpi, + uint32_t old_dpi) { + // Always rounding up to prevent zero sizes (unless the input is zero) as + // well as gaps at the edge. + return uint32_t((uint64_t(size) * new_dpi + (old_dpi - 1)) / old_dpi); + } + uint32_t SizeToLogical(uint32_t size) const { + return ConvertSizeDpi(size, GetMediumDpi(), GetDpi()); + } + uint32_t SizeToPhysical(uint32_t size) const { + return ConvertSizeDpi(size, GetDpi(), GetMediumDpi()); + } + static constexpr int32_t ConvertPositionDpi(int32_t position, + uint32_t new_dpi, + uint32_t old_dpi) { + // Rounding to the nearest mostly similar to Windows MulDiv. + // Plus old_dpi / 2 for positive values, minus old_dpi / 2 for negative + // values for consistent rounding for both positive and negative values (as + // the `/` operator rounds towards zero). + // Example: + // (-3 - 1) / 3 == -1 + // (-2 - 1) / 3 == -1 + // (-1 - 1) / 3 == 0 + // --- + // (0 + 1) / 3 == 0 + // (1 + 1) / 3 == 0 + // (2 + 1) / 3 == 1 + return int32_t((int64_t(position) * new_dpi + + int32_t(old_dpi >> 1) * (position < 0 ? -1 : 1)) / + old_dpi); + } + int32_t PositionToLogical(int32_t position) const { + return ConvertPositionDpi(position, GetMediumDpi(), GetDpi()); + } + int32_t PositionToPhysical(int32_t position) const { + return ConvertPositionDpi(position, GetDpi(), GetMediumDpi()); + } + + // The desired logical size of the window when it's not maximized, regardless + // of the current state of the window (maximized, fullscreen, etc.) + // The implementation may update it, for instance, to clamp it, or when the + // user resizes a non-maximized window. + uint32_t GetDesiredLogicalWidth() const { return desired_logical_width_; } + uint32_t GetDesiredLogicalHeight() const { return desired_logical_height_; } + + // 0 width or height may be returned even in case of an open window with a + // valid non-zero-area surface depending on the platform. + uint32_t GetActualPhysicalWidth() const { + return HasActualState() ? actual_physical_width_ : 0; + } + uint32_t GetActualPhysicalHeight() const { + return HasActualState() ? actual_physical_height_ : 0; + } + uint32_t GetActualLogicalWidth() const { + return SizeToLogical(GetActualPhysicalWidth()); + } + uint32_t GetActualLogicalHeight() const { + return SizeToLogical(GetActualPhysicalHeight()); + } + + // Desired state stored by the common Window, modifiable both externally and + // by the implementation (including from SetFullscreen itself). + virtual bool IsFullscreen() const { return fullscreen_; } + void SetFullscreen(bool new_fullscreen); + + // Desired state stored by the common Window, externally modifiable, read-only + // in the implementation. + const std::string& GetTitle() const { return title_; } + void SetTitle(const std::string_view new_title); + + // Desired state stored in a platform-dependent way in the implementation, + // externally modifiable, read-only by the implementation unless from the + // LoadAndApplyIcon implementation. The icon is in Windows .ico format. + // Provide null buffer and / or zero size to reset the icon. + void SetIcon(const void* buffer, size_t size); void ResetIcon() { SetIcon(nullptr, 0); } - virtual bool CaptureMouse() = 0; - virtual bool ReleaseMouse() = 0; + // Desired state stored by the common Window, externally modifiable, read-only + // in the implementation. + void SetMainMenu(std::unique_ptr new_main_menu); + void CompleteMainMenuItemsUpdate(); + void SetMainMenuEnabled(bool enabled); - virtual bool is_fullscreen() const { return false; } - virtual void ToggleFullscreen(bool fullscreen) {} - - virtual bool is_bordered() const { return false; } - virtual void set_bordered(bool enabled) {} - - virtual int get_medium_dpi() const { return 96; } - virtual int get_dpi() const { return get_medium_dpi(); } - virtual float get_dpi_scale() const { - return float(get_dpi()) / float(get_medium_dpi()); + // Desired state stored by the common Window, externally modifiable, read-only + // in the implementation. + bool IsMouseCaptureRequested() const { + return mouse_capture_request_count_ != 0; } + void CaptureMouse(); + void ReleaseMouse(); - bool has_focus() const { return has_focus_; } - virtual void set_focus(bool value) { has_focus_ = value; } + // Desired state stored by the common Window, externally modifiable, read-only + // in the implementation. + CursorVisibility GetCursorVisibility() const { return cursor_visibility_; } + // Setting this to kAutoHidden from any _other_ visibility should hide the + // cursor immediately - for instance, if the external code wants to auto-hide + // the cursor in fullscreen, to allow going into the fullscreen mode to hide + // the cursor instantly. + void SetCursorVisibility(CursorVisibility new_cursor_visibility); - bool is_cursor_visible() const { return is_cursor_visible_; } - virtual void set_cursor_visible(bool value) { is_cursor_visible_ = value; } + bool HasFocus() const { return HasActualState() ? has_focus_ : false; } + // May be applied in a delayed way or dropped at all, HasFocus will not + // necessarily be true immediately. + void Focus(); - // TODO(Triang3l): Don't scale for guest output - use physical pixels. Use - // logical pixels only for the immediate drawer. - int32_t width() const { return width_; } - int32_t height() const { return height_; } - int32_t scaled_width() const { return int32_t(width_ * get_dpi_scale()); } - int32_t scaled_height() const { return int32_t(height_ * get_dpi_scale()); } + // TODO(Triang3l): A resize function, primarily for snapping externally to + // 1280x720, 1920x1080, and other 1:1 resolutions. It will need to resize the + // window (to a desired logical size - the actual physical size is entirely + // the feedback of the implementation) in the normal state, and possibly also + // un-maximize (and possibly un-fullscreen) it (but this choice will possibly + // need to be exposed to the caller). Because it's currently not needed, it's + // not implemented to avoid platform-specific complexities regarding + // maximization, DPI, etc. - virtual void Resize(int32_t width, int32_t height) { - width_ = width; - height_ = height; + void SetPresenter(Presenter* presenter); + + // Request repainting of the surface. Can be called from non-UI threads as + // long as they know the Surface exists and isn't in the middle of being + // changed to another (the synchronization of this fact between the UI thread + // and the caller thread must be done externally through OnSurfaceChanged). + void RequestPaint() { + if (presenter_surface_) { + RequestPaintImpl(); + } } - virtual void Resize(int32_t left, int32_t top, int32_t right, - int32_t bottom) { - width_ = right - left; - height_ = bottom - top; - } - - GraphicsContext* context() const { return context_.get(); } - ImGuiDrawer* imgui_drawer() const { return imgui_drawer_.get(); } - bool is_imgui_input_enabled() const { return is_imgui_input_enabled_; } - void set_imgui_input_enabled(bool value); - - void AttachListener(WindowListener* listener); - void DetachListener(WindowListener* listener); - - virtual bool Initialize() { return true; } - void set_context(std::unique_ptr context) { - context_ = std::move(context); - if (context_) { - MakeReady(); + void RequestPresenterUIPaintFromUIThread() { + if (presenter_) { + presenter_->RequestUIPaintFromUIThread(); } } - void Layout(); - virtual void Invalidate(); - - virtual void Close() = 0; - - public: - Delegate on_closing; - Delegate on_closed; - - Delegate on_painting; - Delegate on_paint; - Delegate on_painted; - Delegate on_context_lost; - - Delegate on_file_drop; - - Delegate on_key_down; - Delegate on_key_up; - Delegate on_key_char; - - Delegate on_mouse_down; - Delegate on_mouse_move; - Delegate on_mouse_up; - Delegate on_mouse_wheel; - protected: - Window(WindowedAppContext& app_context, const std::string& title); + // The receiver, which must never be instantiated in the Window object itself + // (rather, usually it should be created as a local variable, because only + // LIFO-ordered creation and deletion of these is supported), that allows + // deletion of the Window from within an event handler (which may invoke a + // WindowListener, and window listeners are allowed to destroy windows; also + // they may execute, for instance, the functions requested to be executed in + // the UI thread in the WindowedAppContext, which are also allowed to destroy + // windows - because if the former wasn't allowed, the latter would be + // required to destroy windows as a result of UI interaction) to be caught by + // functions inside the Window in order to stop interacting with `*this` and + // returning after this happens. + // Note that the receivers are signaled in the *end* of the destruction of the + // common Window, when truly nothing can be done with it anymore, so it's safe + // to assume that right after the creation of the WindowDestructionReceiver, + // it will still be in an unsignaled state even if it's used somewhere in the + // destructor. The reason is that the users of the WindowDestructionReceiver + // are expected to stop accessing the Window *immediately* once + // IsWindowDestroyed becomes `true`, and to leave it in a potentially + // indeterminate state - but the code executed subsequently in the destructor + // may still use that state meaningfully. + class WindowDestructionReceiver { + public: + explicit WindowDestructionReceiver(Window* window) : window_(window) { + if (window_) { + outer_receiver_ = window_->innermost_destruction_receiver_; + window_->innermost_destruction_receiver_ = this; + } + } - void ForEachListener(std::function fn); - void TryForEachListener(std::function fn); + ~WindowDestructionReceiver() { + // If the window is not null, removal from the stack must happen + // regardless of `phase_ == Phase::kDeleting`, because the window + // destructor iterates the receivers after EnterDestructor(), and if the + // receiver is not removed in this case, the destructor will do + // use-after-free. + if (window_) { + // Only LIFO order is supported (normally through RAII). + assert_true(window_->innermost_destruction_receiver_ == this); + window_->innermost_destruction_receiver_ = outer_receiver_; + } + } - virtual bool MakeReady(); + bool IsWindowDestroyed() const { return window_ == nullptr; } - virtual bool OnCreate(); - virtual void OnMainMenuChange(); - virtual void OnClose(); - virtual void OnDestroy(); + // Helper functions for common usages of the receiver. Unlike + // IsWindowDestroyed, these, however, may return false immediately on + // creation. + // Primarily for the implementation (most importantly its native event + // handler), to stop interacting with the native window given that it was + // possible before the function call it's guarded with. + bool IsWindowDestroyedOrClosed() const { + return IsWindowDestroyed() || window_->IsClosed(); + } + // For guarding Apply* calls if one state setter needs to make multiple of + // them (or just detecting if it's okay to call Apply*). + bool IsWindowDestroyedOrStateInapplicable() const { + return IsWindowDestroyed() || !window_->CanApplyState(); + } + bool IsWindowDestroyedOrListenersUncallable() const { + return IsWindowDestroyed() || !window_->CanSendEventsToListeners(); + } - virtual void OnDpiChanged(UIEvent* e); - virtual void OnResize(UIEvent* e); - virtual void OnLayout(UIEvent* e); - virtual void OnPaint(UIEvent* e); - virtual void OnFileDrop(FileDropEvent* e); + private: + // The Window must set window_ to nullptr in its destructor. + friend Window; + Window* window_; + WindowDestructionReceiver* outer_receiver_ = nullptr; + }; - virtual void OnVisible(UIEvent* e); - virtual void OnHidden(UIEvent* e); + // Like in the Windows Media Player. + // A more modern Windows example, Movies & TV in Windows 11 21H2, has 5000, + // but it's too long especially for highly dynamic games. + // Implementations may use different values according to the platform's UX + // conventions. + static constexpr uint32_t kDefaultCursorAutoHideMilliseconds = 3333; - virtual void OnGotFocus(UIEvent* e); - virtual void OnLostFocus(UIEvent* e); + Window(WindowedAppContext& app_context, const std::string_view title, + uint32_t desired_logical_width, uint32_t desired_logical_height); - virtual void OnKeyDown(KeyEvent* e); - virtual void OnKeyUp(KeyEvent* e); - virtual void OnKeyChar(KeyEvent* e); + // If implementation-specific destruction happens, should be called in the + // beginning of the implementation's destructor so the implementation can + // destroy the platform window without doing something unsafe in destruction. + void EnterDestructor() { + phase_ = Phase::kDeleting; + // Disconnect from the surface before destroying the window behind it. + OnSurfaceChanged(false); + } - virtual void OnMouseDown(MouseEvent* e); - virtual void OnMouseMove(MouseEvent* e); - virtual void OnMouseUp(MouseEvent* e); - virtual void OnMouseWheel(MouseEvent* e); + // For an open window, the implementation should return the current DPI for + // the window. For a non-open one, it should be the closest approximation, + // such as the last DPI from an existing window, the system DPI, or just the + // medium DPI (0 returned from it will also be treated as medium DPI). + virtual uint32_t GetLatestDpiImpl() const { return GetMediumDpi(); } - void OnKeyPress(KeyEvent* e, bool is_down, bool is_char); + // Deletion of the window may (and must) not happen in OpenImpl, the listeners + // are deferred, so there's no need to use WindowDestructionReceiver in it. + // In case of failure, the implementation must not leave itself in an + // indeterminate state, so another attempt to open the window can be made. + // The implementation must apply the following desired state if it needs it, + // directly (not via Set* methods as they will be dropped during OpenImpl + // since the window is not fully open yet): + // - Title (GetTitle()). + // - Icon (from the last LoadAndApplyIcon call). + // - Main menu (GetMainMenu()) and its enablement. + // - Desired logical size (GetDesiredLogicalWidth() / Height(), taking into + // account that the main menu, during the initial opening, should not be + // included in this size - it specifies the client area that painting will + // be done to), within the capabilities of the platform (may be clamped by + // the OS, for instance - in this case, OnDesiredLogicalSizeUpdate may be + // called from within OpenImpl to store the clamped size for later). + // - Fullscreen (GetFullscreen()) - however, if possible, the calculations + // that would normally be done for a non-fullscreen window in OpenImpl + // should also be done if entering fullscreen, including the menu-related + // ones - first, the usual windowed geometry calculations should be done, + // and then fullscreen should be entered; but preferably still entering + // fullscreen before actually showing a visible window to the user for a + // seamless transition. + // - Mouse capture (IsMouseCaptureRequested()). + // - Cursor visibility (GetCursorVisibility()). + // Also, as a result of the OpenImpl call, these function should be called + // (immediately from within OpenImpl directly or indirectly through the native + // event handling, or shortly after during the UI main loop) to provide the + // initial actual state to the common Window code so it returns the correct + // values: + // - OnActualSizeUpdate, at least if the window isn't opened as not yet + // visible on screen (otherwise the size will be assumed to be 0x0 until the + // next size update caused by something likely requiring user interaction). + // - OnFocusUpdate, at least if the focus has been obtained (otherwise the + // window will be assumed to be not in focus until it goes into the focus + // again). + // Also, if some of the desired state that may be updated by the + // implementation could not be applied (such as the fullscreen mode), and + // certain values of the implementation state are either normally + // differentiated within the implementation or are just meaningful considering + // the platform's defaults (for instance, the platform inherently supports + // only fullscreen, possibly doesn't have a concept of windows at all), it's + // recommended to call the appropriate On*Update functions to update the + // desired state to the actual one. + virtual bool OpenImpl() = 0; + virtual void RequestCloseImpl() = 0; + + // Apply* are called only if CanApplyState() is true (unless the function + // should do more than just applying, such as also updating the desired state + // in a platform-dependent way - see each individual function). + // ApplyNew* means that the value has actually been changed to something + // different than it was previously. + virtual void ApplyNewFullscreen() {} + virtual void ApplyNewTitle() {} + // can_apply_state_in_current_phase whether the window is in a life cycle + // phase that would normally accept Apply calls (the native window surely + // exists), since this function may be called in closed states too to update + // the desired icon (since it's stored in the implementation) - the + // implementation may, however, ignore it and use a more granular check of the + // existence of the native window and the safety of updating the icon for + // better internal state consistency. The icon is in Windows .ico format. If + // the buffer is null or the size is 0, the icon should be reset to the + // default one. Returns whether the icon has been updated successfully. + virtual void LoadAndApplyIcon(const void* buffer, size_t size, + bool can_apply_state_in_current_phase) {} + MenuItem* GetMainMenu() const { return main_menu_.get(); } + // May be called to add, replace or remove the main menu. + virtual void ApplyNewMainMenu(MenuItem* old_main_menu) {} + // If there's main menu, and state can be applied, will be called to make the + // implementation's state consistent with the new state of the MenuItems of + // the main menu after changes have been made to them. + virtual void CompleteMainMenuItemsUpdateImpl() {} + // Will be called even if capturing while the mouse is already assumed to be + // captured, in case something has released it in the OS. + virtual void ApplyNewMouseCapture() {} + virtual void ApplyNewMouseRelease() {} + virtual void ApplyNewCursorVisibility( + CursorVisibility old_cursor_visibility) {} + // If state can be applied, this is called to request bringing the window into + // focus (and once that's done by the OS, update the actual focus state). Does + // nothing otherwise (focus can't be requested before the window is open, a + // closed window is always assumed to be not in focus). + virtual void FocusImpl() {} + + Presenter* presenter() const { return presenter_; } + bool HasSurface() const { return presenter_surface_ != nullptr; } + // If new_surface_potentially_exists is false, creation of the new surface for + // the window won't be updated, and it may be called from the destructor (via + // EnterDestructor to destroy the surface before destroying what it depends + // on) as no virtual functions (including CreateSurface) will be called. + // This function is nonvirtual itself for this reason as well. + void OnSurfaceChanged(bool new_surface_potentially_exists); + // Called only for an open window. + virtual std::unique_ptr CreateSurfaceImpl( + Surface::TypeFlags allowed_types) = 0; + // Called only if the Surface exists. + virtual void RequestPaintImpl() = 0; + + // Will also disconnect the surface if needed. + void OnBeforeClose(WindowDestructionReceiver& destruction_receiver); + void OnAfterClose(); + + // These functions may usually also be called as part of the opening process + // from within OpenImpl (directly or through the platform event handler + // invoked during it) to actualize the state for the newly createad window, + // especially if it's different than the desired one. + void OnDpiChanged(UISetupEvent& e, + WindowDestructionReceiver& destruction_receiver); + void OnMonitorUpdate(MonitorUpdateEvent& e); + // For calling when the platform changes something in the non-maximized, + // non-fullscreen size of the window. + void OnDesiredLogicalSizeUpdate(uint32_t new_desired_logical_width, + uint32_t new_desired_logical_height) { + desired_logical_width_ = new_desired_logical_width; + desired_logical_height_ = new_desired_logical_height; + } + // If the size of the client area is the same as the currently assumed one + // (the desired / last size for a newly opened / reopened window, the last + // actual size for an already open window), does nothing and returns false. + // Otherwise, updates the size, notifies what depends on the size about the + // change, and returns true. Not storing the new size in the UISetupEvent + // because a resize listener may request another resize, in which case it will + // be outdated - listeners must query the new physical size from the window + // explicitly. + bool OnActualSizeUpdate(uint32_t new_physical_width, + uint32_t new_physical_height, + WindowDestructionReceiver& destruction_receiver); + void OnDesiredFullscreenUpdate(bool new_fullscreen) { + fullscreen_ = new_fullscreen; + } + void OnFocusUpdate(bool new_has_focus, + WindowDestructionReceiver& destruction_receiver); + + // Pass true as force_paint in case the platform can't retain the image from + // the previous paint so it won't be skipped if there are no content updates. + void OnPaint(bool force_paint = false); + + void OnFileDrop(FileDropEvent& e, + WindowDestructionReceiver& destruction_receiver); + + void OnKeyDown(KeyEvent& e, WindowDestructionReceiver& destruction_receiver); + void OnKeyUp(KeyEvent& e, WindowDestructionReceiver& destruction_receiver); + void OnKeyChar(KeyEvent& e, WindowDestructionReceiver& destruction_receiver); + + void OnMouseDown(MouseEvent& e, + WindowDestructionReceiver& destruction_receiver); + void OnMouseMove(MouseEvent& e, + WindowDestructionReceiver& destruction_receiver); + void OnMouseUp(MouseEvent& e, + WindowDestructionReceiver& destruction_receiver); + void OnMouseWheel(MouseEvent& e, + WindowDestructionReceiver& destruction_receiver); + + private: + struct ListenerIterationContext { + explicit ListenerIterationContext(ListenerIterationContext* outer_context, + size_t first_index = 0) + : outer_context(outer_context), next_index(first_index) {} + // To support nested listener calls, in case a listener does some + // interaction with the window that results in more events being triggered + // (such as calling Windows API functions that return a result from a + // message handled by a window, rather that simply enqueueing the message). + ListenerIterationContext* outer_context; + // Using indices, not iterators, because after the erasure, the adjustment + // must be done for the vector element indices that would be in the iterator + // range that would be invalidated. + size_t next_index; + }; + + struct InputListenerIterationContext { + explicit InputListenerIterationContext( + InputListenerIterationContext* outer_context, + std::multimap::const_reverse_iterator + first_iterator, + size_t first_z_order = SIZE_MAX) + : outer_context(outer_context), + next_iterator(first_iterator), + current_z_order(first_z_order) {} + // To support nested listener calls. + InputListenerIterationContext* outer_context; + // Reverse iterator because input handlers with a higher Z order index may + // correspond to what's displayed on top of what has a lower Z order index, + // so what's higher may consum the event. + std::multimap::const_reverse_iterator + next_iterator; + size_t current_z_order; + }; + + // If the window is closed, the platform native window is either being + // destroyed, or doesn't exist anymore, and thus it's in a non-interactive + // state. + bool IsClosed() const { + return phase_ < Phase::kOpening && phase_ > Phase::kOpenBeforeClosing; + } + + bool CanApplyState() const { + // In kOpening, OpenImpl itself pulls the desired state itself and applies + // it, the Apply* functions can't be called and are unsafe to call because + // the implementation is an incomplete state, with the platform window + // potentially not existing. In kOpenBeforeClosing, as the native window + // still hasn't been destroyed and it can receive new state, still allowing + // applying new state for more consistency between the desired and the + // actual state during the final listener invocation. + return phase_ >= Phase::kOpen && phase_ <= Phase::kOpenBeforeClosing; + } + + bool CanSendEventsToListeners() const { + return phase_ >= Phase::kOpen && phase_ <= Phase::kOpenBeforeClosing; + } + // The listeners may delete the Window - check the destruction receiver after + // calling and stop doing anything accessing *this if that happens. + void SendEventToListeners(std::function fn, + WindowDestructionReceiver& destruction_receiver); + void PropagateEventThroughInputListeners( + std::function fn, + WindowDestructionReceiver& destruction_receiver); + + std::unique_ptr CreateSurface(Surface::TypeFlags allowed_types) { + // If opening, surface creation is deferred until all the initial setup has + // completed. Destruction of the surface is also a part of the closing + // process in OnBeforeClose. + if (phase_ != Phase::kOpen) { + return nullptr; + } + return CreateSurfaceImpl(allowed_types); + } WindowedAppContext& app_context_; - std::unique_ptr main_menu_; - std::string title_; -#ifdef XE_PLATFORM_GNU_LINUX - // GTK must have a default value here that isn't 0 - // TODO(Triang3l): Cleanup and unify this. May need to get the first resize - // message on various platforms. - int32_t width_ = 1280; - int32_t height_ = 720; -#else - int32_t width_ = 0; - int32_t height_ = 0; -#endif - bool has_focus_ = true; - bool is_cursor_visible_ = true; - bool is_imgui_input_enabled_ = false; - std::unique_ptr context_; - std::unique_ptr imgui_drawer_; - - uint32_t frame_count_ = 0; - uint32_t fps_ = 0; - uint64_t fps_update_time_ticks_ = 0; - uint64_t fps_frame_count_ = 0; - uint64_t last_paint_time_ticks_ = 0; - - bool modifier_shift_pressed_ = false; - bool modifier_cntrl_pressed_ = false; - bool modifier_alt_pressed_ = false; - bool modifier_super_pressed_ = false; + Phase phase_ = Phase::kClosedOpenable; + WindowDestructionReceiver* innermost_destruction_receiver_ = nullptr; // All currently-attached listeners that get event notifications. - bool in_listener_loop_ = false; std::vector listeners_; - std::vector pending_listener_attaches_; - std::vector pending_listener_detaches_; + // Ordered by the Z order, and then by the time of addition (but executed in + // reverse order). + // Note: All the iteration logic involving this Z ordering must be the same as + // in drawing (in the UI drawers in the Presenter), but in reverse. + std::multimap input_listeners_; + // Linked list-based stacks of the contexts of the listener iterations + // currently being done, usually allocated on the stack. + ListenerIterationContext* innermost_listener_iteration_context_ = nullptr; + InputListenerIterationContext* innermost_input_listener_iteration_context_ = + nullptr; + + uint32_t desired_logical_width_ = 0; + uint32_t desired_logical_height_ = 0; + // Set by the implementation via OnActualSizeUpdate (from OpenImpl or from the + // platform resize handler). + uint32_t actual_physical_width_ = 0; + uint32_t actual_physical_height_ = 0; + + bool fullscreen_ = false; + + std::string title_; + + std::unique_ptr main_menu_; + + uint32_t mouse_capture_request_count_ = 0; + + CursorVisibility cursor_visibility_ = CursorVisibility::kVisible; + + bool has_focus_ = false; + + Presenter* presenter_ = nullptr; + std::unique_ptr presenter_surface_; + // Whether currently in InPaint to prevent recursive painting in case it's + // triggered somehow from within painting again, because painting is much more + // complex than just a small state update, and recursive painting is + // completely unsupported by the Presenter. + bool is_painting_ = false; }; } // namespace ui diff --git a/src/xenia/ui/window_android.cc b/src/xenia/ui/window_android.cc index cd5aaba80..c653df217 100644 --- a/src/xenia/ui/window_android.cc +++ b/src/xenia/ui/window_android.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -13,29 +13,22 @@ #include #include "xenia/base/assert.h" +#include "xenia/ui/surface_android.h" #include "xenia/ui/windowed_app_context_android.h" namespace xe { namespace ui { std::unique_ptr Window::Create(WindowedAppContext& app_context, - const std::string& title) { - // The window is a proxy between the main activity and Xenia, so there can be - // only one for an activity. - AndroidWindowedAppContext& android_app_context = - static_cast(app_context); - AndroidWindow* current_activity_window = - android_app_context.GetActivityWindow(); - assert_null(current_activity_window); - if (current_activity_window) { - return nullptr; - } - auto window = std::make_unique(app_context, title); - android_app_context.SetActivityWindow(window.get()); - return std::move(window); + const std::string_view title, + uint32_t desired_logical_width, + uint32_t desired_logical_height) { + return std::make_unique( + app_context, title, desired_logical_width, desired_logical_height); } AndroidWindow::~AndroidWindow() { + EnterDestructor(); AndroidWindowedAppContext& android_app_context = static_cast(app_context()); if (android_app_context.GetActivityWindow() == this) { @@ -43,6 +36,57 @@ AndroidWindow::~AndroidWindow() { } } +bool AndroidWindow::OpenImpl() { + // The window is a proxy between the main activity and Xenia, so there can be + // only one for an activity. + AndroidWindowedAppContext& android_app_context = + static_cast(app_context()); + AndroidWindow* previous_activity_window = + android_app_context.GetActivityWindow(); + assert_null(previous_activity_window); + if (previous_activity_window) { + // Don't detach the old window as it's assuming it's still attached while + // it's in an open phase. + return false; + } + android_app_context.SetActivityWindow(this); + return true; +} + +void AndroidWindow::RequestCloseImpl() { + // Finishing the Activity would cause the entire WindowedAppContext to quit, + // which is not the same behavior as on other platforms - the + // WindowedAppContext quit is done explicitly by the listeners during + // OnClosing if needed (the main Window is potentially being changed to a + // different one, for instance). Therefore, only detach the window from the + // app context. + + WindowDestructionReceiver destruction_receiver(this); + OnBeforeClose(destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } + OnAfterClose(); + + AndroidWindowedAppContext& android_app_context = + static_cast(app_context()); + if (android_app_context.GetActivityWindow() == this) { + android_app_context.SetActivityWindow(nullptr); + } +} + +std::unique_ptr AndroidWindow::CreateSurfaceImpl( + Surface::TypeFlags allowed_types) { + if (allowed_types & Surface::kTypeFlag_AndroidNativeWindow) { + // TODO(Triang3l): AndroidNativeWindowSurface for the ANativeWindow. + } + return nullptr; +} + +void AndroidWindow::RequestPaintImpl() { + // TODO(Triang3l): postInvalidate. +} + std::unique_ptr MenuItem::Create(Type type, const std::string& text, const std::string& hotkey, diff --git a/src/xenia/ui/window_android.h b/src/xenia/ui/window_android.h index 780c1d77d..74832576d 100644 --- a/src/xenia/ui/window_android.h +++ b/src/xenia/ui/window_android.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -18,31 +18,24 @@ namespace ui { class AndroidWindow : public Window { public: - // Many functions are left unimplemented because the layout is configured from - // XML and Java. + // Several state-related functions are left unimplemented because the layout + // is configured from XML and Java. - AndroidWindow(WindowedAppContext& app_context, const std::string& title) - : Window(app_context, title) {} + AndroidWindow(WindowedAppContext& app_context, const std::string_view title, + uint32_t desired_logical_width, uint32_t desired_logical_height) + : Window(app_context, title, desired_logical_width, + desired_logical_height) {} ~AndroidWindow(); - NativePlatformHandle native_platform_handle() const override { - return nullptr; - } - // TODO(Triang3l): ANativeWindow for Vulkan surface creation. - NativeWindowHandle native_handle() const override { return nullptr; } + uint32_t GetMediumDpi() const override { return 160; } - void EnableMainMenu() override {} - void DisableMainMenu() override {} + protected: + bool OpenImpl() override; + void RequestCloseImpl() override; - bool SetIcon(const void* buffer, size_t size) override { return false; } - - bool CaptureMouse() override { return false; } - bool ReleaseMouse() override { return false; } - - int get_medium_dpi() const override { return 160; } - - // TODO(Triang3l): Call the close event, which may finish the activity. - void Close() override {} + std::unique_ptr CreateSurfaceImpl( + Surface::TypeFlags allowed_types) override; + void RequestPaintImpl() override; }; // Dummy for the menu item - menus are controlled by the layout. @@ -53,9 +46,6 @@ class AndroidMenuItem final : public MenuItem { AndroidMenuItem(Type type, const std::string& text, const std::string& hotkey, std::function callback) : MenuItem(type, text, hotkey, callback) {} - - void EnableMenuItem(Window& window) override {} - void DisableMenuItem(Window& window) override {} }; } // namespace ui diff --git a/src/xenia/ui/window_demo.cc b/src/xenia/ui/window_demo.cc index 05d61611d..0d1e97a9a 100644 --- a/src/xenia/ui/window_demo.cc +++ b/src/xenia/ui/window_demo.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -17,6 +17,8 @@ #include "xenia/ui/graphics_provider.h" #include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/presenter.h" +#include "xenia/ui/ui_event.h" #include "xenia/ui/virtual_key.h" #include "xenia/ui/window.h" #include "xenia/ui/window_demo.h" @@ -30,26 +32,31 @@ bool WindowDemoApp::OnInitialize() { Profiler::Initialize(); Profiler::ThreadEnter("Main"); - // Create graphics provider that provides the context for the window. + // Create the graphics provider that provides the presenter for the window. graphics_provider_ = CreateGraphicsProvider(); if (!graphics_provider_) { + XELOGE("Failed to initialize the graphics provider"); return false; } + enum : size_t { + kZOrderImGui, + kZOrderProfiler, + kZOrderWindowDemoInput, + }; + // Create the window. - window_ = xe::ui::Window::Create(app_context(), GetName()); - if (!window_->Initialize()) { - XELOGE("Failed to initialize main window"); - return false; - } + window_ = xe::ui::Window::Create(app_context(), GetName(), 1920, 1200); + window_->AddListener(&window_listener_); + window_->AddInputListener(&window_listener_, kZOrderWindowDemoInput); // Main menu. auto main_menu = MenuItem::Create(MenuItem::Type::kNormal); auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, "&File"); { - file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, "&Close", - "Alt+F4", - [this]() { window_->Close(); })); + file_menu->AddChild( + MenuItem::Create(MenuItem::Type::kString, "&Close", "Alt+F4", + [this]() { window_->RequestClose(); })); } main_menu->AddChild(std::move(file_menu)); auto debug_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Debug"); @@ -62,51 +69,61 @@ bool WindowDemoApp::OnInitialize() { []() { Profiler::TogglePause(); })); } main_menu->AddChild(std::move(debug_menu)); - window_->set_main_menu(std::move(main_menu)); + window_->SetMainMenu(std::move(main_menu)); - // Initial size setting, done here so that it knows the menu exists. - window_->Resize(1920, 1200); + // Open the window once it's configured. + if (!window_->Open()) { + XELOGE("Failed to open the main window"); + return false; + } - // Create the graphics context for the window. The window will finish - // initialization with the context (loading resources, etc). - window_->set_context(graphics_provider_->CreateContext(window_.get())); + // Setup drawing to the window. - // Setup the profiler display. - GraphicsContextLock context_lock(window_->context()); - Profiler::set_window(window_.get()); + presenter_ = graphics_provider_->CreatePresenter(); + if (!presenter_) { + XELOGE("Failed to initialize the presenter"); + return false; + } - // Enable imgui input. - window_->set_imgui_input_enabled(true); + immediate_drawer_ = graphics_provider_->CreateImmediateDrawer(); + if (!immediate_drawer_) { + XELOGE("Failed to initialize the immediate drawer"); + return false; + } + immediate_drawer_->SetPresenter(presenter_.get()); - window_->on_closed.AddListener([this](xe::ui::UIEvent* e) { - XELOGI("User-initiated death!"); - app_context().QuitFromUIThread(); - }); + imgui_drawer_ = std::make_unique(window_.get(), kZOrderImGui); + imgui_drawer_->SetPresenterAndImmediateDrawer(presenter_.get(), + immediate_drawer_.get()); + demo_dialog_ = std::make_unique(imgui_drawer_.get()); - window_->on_key_down.AddListener([](xe::ui::KeyEvent* e) { - switch (e->virtual_key()) { - case VirtualKey::kF3: - Profiler::ToggleDisplay(); - break; - default: - break; - } - }); + Profiler::SetUserIO(kZOrderProfiler, window_.get(), presenter_.get(), + immediate_drawer_.get()); - window_->on_painting.AddListener([this](xe::ui::UIEvent* e) { - auto& io = window_->imgui_drawer()->GetIO(); - - ImGui::ShowDemoWindow(); - ImGui::ShowMetricsWindow(); - - Profiler::Flip(); - - // Continuous paint. - window_->Invalidate(); - }); + window_->SetPresenter(presenter_.get()); return true; } +void WindowDemoApp::WindowDemoWindowListener::OnClosing(UIEvent& e) { + app_context_.QuitFromUIThread(); +} + +void WindowDemoApp::WindowDemoWindowListener::OnKeyDown(KeyEvent& e) { + switch (e.virtual_key()) { + case VirtualKey::kF3: + Profiler::ToggleDisplay(); + break; + default: + return; + } + e.set_handled(true); +} + +void WindowDemoApp::WindowDemoDialog::OnDraw(ImGuiIO& io) { + ImGui::ShowDemoWindow(); + ImGui::ShowMetricsWindow(); +} + } // namespace ui } // namespace xe diff --git a/src/xenia/ui/window_demo.h b/src/xenia/ui/window_demo.h index 29b6bc1fa..f3942ea7c 100644 --- a/src/xenia/ui/window_demo.h +++ b/src/xenia/ui/window_demo.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -14,7 +14,12 @@ #include #include "xenia/ui/graphics_provider.h" +#include "xenia/ui/imgui_dialog.h" +#include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/immediate_drawer.h" +#include "xenia/ui/presenter.h" #include "xenia/ui/window.h" +#include "xenia/ui/window_listener.h" #include "xenia/ui/windowed_app.h" namespace xe { @@ -29,13 +34,41 @@ class WindowDemoApp : public WindowedApp { protected: explicit WindowDemoApp(WindowedAppContext& app_context, const std::string_view name) - : WindowedApp(app_context, name) {} + : WindowedApp(app_context, name), window_listener_(app_context) {} virtual std::unique_ptr CreateGraphicsProvider() const = 0; private: + class WindowDemoWindowListener final : public WindowListener, + public WindowInputListener { + public: + explicit WindowDemoWindowListener(WindowedAppContext& app_context) + : app_context_(app_context) {} + + void OnClosing(UIEvent& e) override; + + void OnKeyDown(KeyEvent& e) override; + + private: + WindowedAppContext& app_context_; + }; + + class WindowDemoDialog final : public ImGuiDialog { + public: + explicit WindowDemoDialog(ImGuiDrawer* imgui_drawer) + : ImGuiDialog(imgui_drawer) {} + + protected: + void OnDraw(ImGuiIO& io) override; + }; + + WindowDemoWindowListener window_listener_; std::unique_ptr graphics_provider_; std::unique_ptr window_; + std::unique_ptr presenter_; + std::unique_ptr immediate_drawer_; + std::unique_ptr imgui_drawer_; + std::unique_ptr demo_dialog_; }; } // namespace ui diff --git a/src/xenia/ui/window_gtk.cc b/src/xenia/ui/window_gtk.cc index b35f91305..6eb563667 100644 --- a/src/xenia/ui/window_gtk.cc +++ b/src/xenia/ui/window_gtk.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -11,11 +11,13 @@ #include #include +#include +#include #include "xenia/base/assert.h" -#include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/base/platform_linux.h" +#include "xenia/ui/surface_gnulinux.h" #include "xenia/ui/virtual_key.h" #include "xenia/ui/window_gtk.h" @@ -23,384 +25,407 @@ namespace xe { namespace ui { std::unique_ptr Window::Create(WindowedAppContext& app_context, - const std::string& title) { - return std::make_unique(app_context, title); + const std::string_view title, + uint32_t desired_logical_width, + uint32_t desired_logical_height) { + return std::make_unique(app_context, title, desired_logical_width, + desired_logical_height); } -GTKWindow::GTKWindow(WindowedAppContext& app_context, const std::string& title) - : Window(app_context, title) {} +GTKWindow::GTKWindow(WindowedAppContext& app_context, + const std::string_view title, + uint32_t desired_logical_width, + uint32_t desired_logical_height) + : Window(app_context, title, desired_logical_width, + desired_logical_height) {} GTKWindow::~GTKWindow() { - OnDestroy(); + EnterDestructor(); if (window_) { - if (GTK_IS_WIDGET(window_)) { - gtk_widget_destroy(window_); - } + // Set window_ to null to ignore events from now on since this ui::GTKWindow + // is entering an indeterminate state. + GtkWidget* window = window_; window_ = nullptr; + // Destroying the top-level window also destroys its children. + drawing_area_ = nullptr; + box_ = nullptr; + gtk_widget_destroy(window); } } -bool GTKWindow::Initialize() { return OnCreate(); } - -gboolean gtk_event_handler(GtkWidget* widget, GdkEvent* event, gpointer data) { - GTKWindow* window = reinterpret_cast(data); - switch (event->type) { - case GDK_OWNER_CHANGE: - window->HandleWindowOwnerChange(&(event->owner_change)); - break; - case GDK_VISIBILITY_NOTIFY: - window->HandleWindowVisibility(&(event->visibility)); - break; - case GDK_KEY_PRESS: - case GDK_KEY_RELEASE: - window->HandleKeyboard(&(event->key)); - break; - case GDK_SCROLL: - case GDK_MOTION_NOTIFY: - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - window->HandleMouse(&(event->any)); - break; - case GDK_FOCUS_CHANGE: - window->HandleWindowFocus(&(event->focus_change)); - break; - case GDK_CONFIGURE: - // Only handle the event for the drawing area so we don't save - // a width and height that includes the menu bar on the full window - if (event->configure.window == - gtk_widget_get_window(window->drawing_area_)) { - window->HandleWindowResize(&(event->configure)); - } - break; - default: - // Do nothing - break; - } - // Propagate the event to other handlers - return GDK_EVENT_PROPAGATE; -} - -gboolean draw_callback(GtkWidget* widget, GdkFrameClock* frame_clock, - gpointer data) { - GTKWindow* window = reinterpret_cast(data); - window->HandleWindowPaint(); - return G_SOURCE_CONTINUE; -} - -gboolean close_callback(GtkWidget* widget, gpointer data) { - GTKWindow* window = reinterpret_cast(data); - window->Close(); - return G_SOURCE_CONTINUE; -} - -bool GTKWindow::OnCreate() { - // GTK optionally allows passing argv and argc here for parsing gtk specific - // options. We won't bother +bool GTKWindow::OpenImpl() { window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_resizable(GTK_WINDOW(window_), true); - gtk_window_set_title(GTK_WINDOW(window_), title_.c_str()); - gtk_window_set_default_size(GTK_WINDOW(window_), width_, height_); - // Drawing area is where we will attach our vulkan/gl context - drawing_area_ = gtk_drawing_area_new(); - // tick callback is for the refresh rate of the window - gtk_widget_add_tick_callback(drawing_area_, draw_callback, - reinterpret_cast(this), nullptr); - // Attach our event handler to both the main window (for keystrokes) and the - // drawing area (for mouse input, resize event, etc) - g_signal_connect(G_OBJECT(drawing_area_), "event", - G_CALLBACK(gtk_event_handler), - reinterpret_cast(this)); - GdkDisplay* gdk_display = gtk_widget_get_display(window_); - assert(GDK_IS_X11_DISPLAY(gdk_display)); - connection_ = XGetXCBConnection(gdk_x11_display_get_xdisplay(gdk_display)); + gtk_window_set_title(GTK_WINDOW(window_), GetTitle().c_str()); - g_signal_connect(G_OBJECT(window_), "event", G_CALLBACK(gtk_event_handler), - reinterpret_cast(this)); - // When the window manager kills the window (ie, the user hits X) - g_signal_connect(G_OBJECT(window_), "destroy", G_CALLBACK(close_callback), - reinterpret_cast(this)); - // Enable only keyboard events (so no mouse) for the top window - gtk_widget_set_events(window_, GDK_KEY_PRESS | GDK_KEY_RELEASE); - // Enable all events for the drawing area - gtk_widget_add_events(drawing_area_, GDK_ALL_EVENTS_MASK); - // Place the drawing area in a container (which later will hold the menu) - // then let it fill the whole area + // Create the vertical box container for the main menu and the drawing area. box_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_end(GTK_BOX(box_), drawing_area_, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(window_), box_); + + // Add the main menu (even if fullscreen was requested, for the initial layout + // calculation). + const GTKMenuItem* main_menu = static_cast(GetMainMenu()); + GtkWidget* main_menu_widget = main_menu ? main_menu->handle() : nullptr; + if (main_menu_widget) { + gtk_box_pack_start(GTK_BOX(box_), main_menu_widget, FALSE, FALSE, 0); + } + + // Create the drawing area for creating the surface for, which will be the + // client area of the window occupying all the window space not taken by the + // main menu. + drawing_area_ = gtk_drawing_area_new(); + gtk_box_pack_end(GTK_BOX(box_), drawing_area_, TRUE, TRUE, 0); + // The desired size is the client (drawing) area size. Let GTK auto-size the + // entire window around it (as well as the width of the menu actually if it + // happens to be bigger - the desired size in the Window will be updated later + // to reflect that). + gtk_widget_set_size_request(drawing_area_, GetDesiredLogicalWidth(), + GetDesiredLogicalHeight()); + + // Attach the event handlers. + // Keyboard events are processed by the window, mouse events are processed + // within, and by, the drawing (client) area. + gtk_widget_set_events(window_, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | + GDK_FOCUS_CHANGE_MASK); + gtk_widget_set_events(drawing_area_, + GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL_MASK); + g_signal_connect(G_OBJECT(window_), "event", + G_CALLBACK(WindowEventHandlerThunk), + reinterpret_cast(this)); + g_signal_connect(G_OBJECT(drawing_area_), "event", + G_CALLBACK(DrawingAreaEventHandlerThunk), + reinterpret_cast(this)); + g_signal_connect(G_OBJECT(drawing_area_), "draw", G_CALLBACK(DrawHandler), + reinterpret_cast(this)); + + // Finally show all the widgets in the window, including the main menu. gtk_widget_show_all(window_); - return super::OnCreate(); -} + // Remove the size request after finishing the initial layout because it makes + // it impossible to make the window smaller. + gtk_widget_set_size_request(drawing_area_, -1, -1); -void GTKWindow::OnDestroy() { super::OnDestroy(); } - -void GTKWindow::OnClose() { - if (!closing_ && window_) { - closing_ = true; + // After setting up the initial layout for non-fullscreen, enter fullscreen if + // requested. + if (IsFullscreen()) { + if (main_menu_widget) { + gtk_container_remove(GTK_CONTAINER(box_), main_menu_widget); + } + gtk_window_fullscreen(GTK_WINDOW(window_)); } - super::OnClose(); -} -bool GTKWindow::set_title(const std::string_view title) { - if (!super::set_title(title)) { - return false; + // Make sure the initial state after opening is reported to the common Window + // class no matter how GTK sends the events. + { + WindowDestructionReceiver destruction_receiver(this); + + // TODO(Triang3l): Report the desired client area size. + + GtkAllocation drawing_area_allocation; + gtk_widget_get_allocation(drawing_area_, &drawing_area_allocation); + OnActualSizeUpdate(uint32_t(drawing_area_allocation.width), + uint32_t(drawing_area_allocation.height), + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return true; + } + + if (gtk_window_has_toplevel_focus(GTK_WINDOW(window_))) { + OnFocusUpdate(true, destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return true; + } + } } - std::string titlez(title); - gtk_window_set_title(GTK_WINDOW(window_), (gchar*)titlez.c_str()); + return true; } -bool GTKWindow::SetIcon(const void* buffer, size_t size) { - // TODO(dougvj) Set icon after changin buffer to the correct format. (the - // call is gtk_window_set_icon) - return false; -} +void GTKWindow::RequestCloseImpl() { gtk_window_close(GTK_WINDOW(window_)); } -bool GTKWindow::is_fullscreen() const { return fullscreen_; } +void GTKWindow::ApplyNewFullscreen() { + // Various functions here may trigger events that may result in the listeners + // being invoked, and potentially cause the destruction of the window or + // fullscreen being toggled from inside this function. + WindowDestructionReceiver destruction_receiver(this); -void GTKWindow::ToggleFullscreen(bool fullscreen) { - if (fullscreen == is_fullscreen()) { - return; - } + const GTKMenuItem* main_menu = static_cast(GetMainMenu()); + GtkWidget* main_menu_widget = main_menu ? main_menu->handle() : nullptr; - fullscreen_ = fullscreen; - if (fullscreen) { + // Changing the menu and the fullscreen state may change the size of the + // drawing area too, don't handle the resize multiple times (also potentially + // with the listeners changing the desired fullscreen if called from the + // handling of some event like GDK_CONFIGURE). + BeginBatchedSizeUpdate(); + + if (IsFullscreen()) { + if (main_menu_widget) { + gtk_container_remove(GTK_CONTAINER(box_), main_menu_widget); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } + } gtk_window_fullscreen(GTK_WINDOW(window_)); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } } else { gtk_window_unfullscreen(GTK_WINDOW(window_)); - } -} - -bool GTKWindow::is_bordered() const { - return gtk_window_get_decorated(GTK_WINDOW(window_)); -} - -void GTKWindow::set_bordered(bool enabled) { - if (is_fullscreen()) { - // Don't screw with the borders if we're fullscreen. - return; - } - gtk_window_set_decorated(GTK_WINDOW(window_), enabled); -} - -void GTKWindow::set_cursor_visible(bool value) { - if (is_cursor_visible_ == value) { - return; - } - if (value) { - // TODO(dougvj) Show and hide cursor - } else { - } -} - -void GTKWindow::set_focus(bool value) { - if (has_focus_ == value) { - return; - } - if (window_) { - if (value) { - gtk_window_activate_focus(GTK_WINDOW(window_)); - } else { - // TODO(dougvj) Check to see if we need to do something here to unset - // the focus. + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; } - } else { - has_focus_ = value; - } -} - -void GTKWindow::Resize(int32_t width, int32_t height) { - if (is_fullscreen()) { - // Cannot resize while in fullscreen. - return; - } - gtk_window_resize(GTK_WINDOW(window_), width, height); - super::Resize(width, height); -} - -void GTKWindow::Resize(int32_t left, int32_t top, int32_t right, - int32_t bottom) { - if (is_fullscreen()) { - // Cannot resize while in fullscreen. - return; - } - gtk_window_move(GTK_WINDOW(window_), left, top); - gtk_window_resize(GTK_WINDOW(window_), right - left, bottom - top); - super::Resize(left, top, right, bottom); -} - -void GTKWindow::OnResize(UIEvent* e) { super::OnResize(e); } - -void GTKWindow::Invalidate() { - // gtk_widget_queue_draw(drawing_area_); - super::Invalidate(); -} - -void GTKWindow::Close() { - if (closing_) { - return; - } - closing_ = true; - OnClose(); - gtk_widget_destroy(window_); - window_ = nullptr; -} - -void GTKWindow::OnMainMenuChange() { - // We need to store the old handle for detachment - static int count = 0; - auto main_menu = reinterpret_cast(main_menu_.get()); - if (main_menu && main_menu->handle()) { - if (!is_fullscreen()) { - gtk_box_pack_start(GTK_BOX(box_), main_menu->handle(), FALSE, FALSE, 0); - gtk_widget_show_all(window_); - } else { - gtk_container_remove(GTK_CONTAINER(box_), main_menu->handle()); + if (main_menu_widget) { + gtk_box_pack_start(GTK_BOX(box_), main_menu_widget, FALSE, FALSE, 0); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } + // If the new menu is used for the first time, it will be in the hidden + // state initially. The menu might have been changed while in fullscreen + // without having shown it. + gtk_widget_show_all(main_menu_widget); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } } } -} -bool GTKWindow::HandleWindowOwnerChange(GdkEventOwnerChange* event) { - if (event->type == GDK_OWNER_CHANGE) { - if (event->reason == GDK_OWNER_CHANGE_DESTROY) { - OnDestroy(); - } else if (event->reason == GDK_OWNER_CHANGE_CLOSE) { - closing_ = true; - Close(); - OnClose(); - } - return true; + EndBatchedSizeUpdate(destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; } - return false; } -bool GTKWindow::HandleWindowPaint() { - auto e = UIEvent(this); - OnPaint(&e); - return true; +void GTKWindow::ApplyNewTitle() { + gtk_window_set_title(GTK_WINDOW(window_), GetTitle().c_str()); } -bool GTKWindow::HandleWindowResize(GdkEventConfigure* event) { - if (event->type == GDK_CONFIGURE) { - int32_t width = event->width; - int32_t height = event->height; - auto e = UIEvent(this); - if (width != width_ || height != height_) { - width_ = width; - height_ = height; - Layout(); - } - OnResize(&e); - return true; +void GTKWindow::ApplyNewMainMenu(MenuItem* old_main_menu) { + if (IsFullscreen()) { + // The menu will be set when exiting fullscreen. + return; } - return false; -} + // The fullscreen state may have been changed by some callback invoked, such + // as the configure (resize) one, recheck it after making changes also. -bool GTKWindow::HandleWindowVisibility(GdkEventVisibility* event) { - // TODO(dougvj) The gdk docs say that this is deprecated because modern window - // managers composite everything and nothing is truly hidden. - if (event->type == GDK_VISIBILITY_NOTIFY) { - if (event->state == GDK_VISIBILITY_UNOBSCURED) { - auto e = UIEvent(this); - OnVisible(&e); - } else { - auto e = UIEvent(this); - OnHidden(&e); + WindowDestructionReceiver destruction_receiver(this); + + // Changing the menu may change the size of the drawing area too, and here the + // menu may be changed twice (to detach the old one and to attach the new), + // don't handle the resize multiple times. + BeginBatchedSizeUpdate(); + + if (old_main_menu) { + const GTKMenuItem& old_gtk_main_menu = + *static_cast(old_main_menu); + gtk_container_remove(GTK_CONTAINER(box_), old_gtk_main_menu.handle()); + if (destruction_receiver.IsWindowDestroyedOrClosed() || IsFullscreen()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; } - return true; } - return false; -} -bool GTKWindow::HandleWindowFocus(GdkEventFocus* event) { - if (event->type == GDK_FOCUS_CHANGE) { - if (!event->in) { - has_focus_ = false; - auto e = UIEvent(this); - OnLostFocus(&e); - } else { - has_focus_ = true; - auto e = UIEvent(this); - OnGotFocus(&e); + const GTKMenuItem* new_main_menu = + static_cast(GetMainMenu()); + if (!new_main_menu) { + EndBatchedSizeUpdate(destruction_receiver); + return; + } + GtkWidget* new_main_menu_widget = new_main_menu->handle(); + gtk_box_pack_start(GTK_BOX(box_), new_main_menu_widget, FALSE, FALSE, 0); + if (destruction_receiver.IsWindowDestroyedOrClosed() || IsFullscreen()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); } - return true; + return; + } + // If the new menu is used for the first time, it will be in the hidden state + // initially. + gtk_widget_show_all(new_main_menu_widget); + if (destruction_receiver.IsWindowDestroyedOrClosed() || IsFullscreen()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } + + EndBatchedSizeUpdate(destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; } - return false; } -bool GTKWindow::HandleMouse(GdkEventAny* event) { +void GTKWindow::FocusImpl() { gtk_window_activate_focus(GTK_WINDOW(window_)); } + +std::unique_ptr GTKWindow::CreateSurfaceImpl( + Surface::TypeFlags allowed_types) { + GdkDisplay* display = gtk_widget_get_display(window_); + GdkWindow* drawing_area_window = gtk_widget_get_window(drawing_area_); + bool type_known = false; + bool type_supported_by_display = false; + if (allowed_types & Surface::kTypeFlag_XcbWindow) { + type_known = true; + if (GDK_IS_X11_DISPLAY(display)) { + type_supported_by_display = true; + return std::make_unique( + XGetXCBConnection(gdk_x11_display_get_xdisplay(display)), + gdk_x11_window_get_xid(drawing_area_window)); + } + } + // TODO(Triang3l): Wayland surface. + if (type_known && !type_supported_by_display) { + XELOGE( + "GTKWindow: The window system of the GTK window is not supported by " + "Xenia"); + } + return nullptr; +} + +void GTKWindow::RequestPaintImpl() { gtk_widget_queue_draw(drawing_area_); } + +void GTKWindow::HandleSizeUpdate( + WindowDestructionReceiver& destruction_receiver) { + if (!drawing_area_) { + // Batched size update ended when the window has already been closed, for + // instance. + return; + } + + // TODO(Triang3l): Report the desired client area size. + + GtkAllocation drawing_area_allocation; + gtk_widget_get_allocation(drawing_area_, &drawing_area_allocation); + OnActualSizeUpdate(uint32_t(drawing_area_allocation.width), + uint32_t(drawing_area_allocation.height), + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; + } +} + +void GTKWindow::BeginBatchedSizeUpdate() { + // It's okay if batched_size_update_contained_* are not false when beginning + // a batched update, in case the new batched update was started by a window + // listener called from within EndBatchedSizeUpdate. + ++batched_size_update_depth_; +} + +void GTKWindow::EndBatchedSizeUpdate( + WindowDestructionReceiver& destruction_receiver) { + assert_not_zero(batched_size_update_depth_); + if (--batched_size_update_depth_) { + return; + } + // Resetting batched_size_update_contained_* in closing, not opening, because + // a listener may start a new batch, and finish it, and there won't be need to + // handle the deferred messages twice. + if (batched_size_update_contained_configure_) { + batched_size_update_contained_configure_ = false; + HandleSizeUpdate(destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } + } + if (batched_size_update_contained_draw_) { + batched_size_update_contained_draw_ = false; + RequestPaint(); + } +} + +bool GTKWindow::HandleMouse(GdkEvent* event, + WindowDestructionReceiver& destruction_receiver) { MouseEvent::Button button = MouseEvent::Button::kNone; - int32_t dx = 0; - int32_t dy = 0; int32_t x = 0; int32_t y = 0; + int32_t scroll_x = 0; + int32_t scroll_y = 0; switch (event->type) { - default: - // Double click/etc? - return true; + case GDK_MOTION_NOTIFY: { + auto motion_event = reinterpret_cast(event); + x = motion_event->x; + y = motion_event->y; + } break; case GDK_BUTTON_PRESS: case GDK_BUTTON_RELEASE: { - GdkEventButton* e = reinterpret_cast(event); - switch (e->button) { + auto button_event = reinterpret_cast(event); + switch (button_event->button) { case 1: button = MouseEvent::Button::kLeft; break; - case 3: - button = MouseEvent::Button::kRight; - break; case 2: button = MouseEvent::Button::kMiddle; break; + case 3: + button = MouseEvent::Button::kRight; + break; case 4: button = MouseEvent::Button::kX1; break; case 5: button = MouseEvent::Button::kX2; break; + default: + // Still handle the movement. + break; } - x = e->x; - y = e->y; - break; - } - case GDK_MOTION_NOTIFY: { - GdkEventMotion* e = reinterpret_cast(event); - x = e->x; - y = e->y; - break; - } + x = button_event->x; + y = button_event->y; + } break; case GDK_SCROLL: { - GdkEventScroll* e = reinterpret_cast(event); - x = e->x; - y = e->y; - dx = e->delta_x; - dy = e->delta_y; - break; - } - } - - auto e = MouseEvent(this, button, x, y, dx, dy); - switch (event->type) { - case GDK_BUTTON_PRESS: - OnMouseDown(&e); - break; - case GDK_BUTTON_RELEASE: - OnMouseUp(&e); - break; - case GDK_MOTION_NOTIFY: - OnMouseMove(&e); - break; - case GDK_SCROLL: - OnMouseWheel(&e); - break; + auto scroll_event = reinterpret_cast(event); + x = scroll_event->x; + y = scroll_event->y; + scroll_x = scroll_event->delta_x * MouseEvent::kScrollPerDetent; + // In GDK, positive is towards the bottom of the screen, not forward from + // the user. + scroll_y = -scroll_event->delta_y * MouseEvent::kScrollPerDetent; + } break; default: return false; } + + MouseEvent e(this, button, x, y, scroll_x, scroll_y); + switch (event->type) { + case GDK_MOTION_NOTIFY: + OnMouseMove(e, destruction_receiver); + break; + case GDK_BUTTON_PRESS: + OnMouseDown(e, destruction_receiver); + break; + case GDK_BUTTON_RELEASE: + OnMouseUp(e, destruction_receiver); + break; + case GDK_SCROLL: + OnMouseWheel(e, destruction_receiver); + break; + default: + break; + } + // Returning immediately anyway - no need to check + // destruction_receiver.IsWindowDestroyed(). return e.is_handled(); } -bool GTKWindow::HandleKeyboard(GdkEventKey* event) { +bool GTKWindow::HandleKeyboard( + GdkEventKey* event, WindowDestructionReceiver& destruction_receiver) { unsigned int modifiers = event->state; bool shift_pressed = modifiers & GDK_SHIFT_MASK; bool ctrl_pressed = modifiers & GDK_CONTROL_MASK; @@ -408,25 +433,154 @@ bool GTKWindow::HandleKeyboard(GdkEventKey* event) { bool super_pressed = modifiers & GDK_SUPER_MASK; uint32_t key_char = gdk_keyval_to_unicode(event->keyval); // TODO(Triang3l): event->hardware_keycode to VirtualKey translation. - auto e = KeyEvent(this, VirtualKey(event->hardware_keycode), 1, - event->type == GDK_KEY_RELEASE, shift_pressed, ctrl_pressed, - alt_pressed, super_pressed); + KeyEvent e(this, VirtualKey(event->hardware_keycode), 1, + event->type == GDK_KEY_RELEASE, shift_pressed, ctrl_pressed, + alt_pressed, super_pressed); switch (event->type) { case GDK_KEY_PRESS: - OnKeyDown(&e); + OnKeyDown(e, destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return e.is_handled(); + } if (key_char > 0) { - OnKeyChar(&e); + OnKeyChar(e, destruction_receiver); } break; case GDK_KEY_RELEASE: - OnKeyUp(&e); + OnKeyUp(e, destruction_receiver); break; default: - return false; + break; } + // Returning immediately anyway - no need to check + // destruction_receiver.IsWindowDestroyed(). return e.is_handled(); } +gboolean GTKWindow::WindowEventHandler(GdkEvent* event) { + switch (event->type) { + case GDK_DELETE: + // In case the widget was somehow forcibly destroyed without GDK_DELETE. + case GDK_DESTROY: { + WindowDestructionReceiver destruction_receiver(this); + OnBeforeClose(destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + break; + } + // Set window_ to null to ignore events from now on since this + // ui::GTKWindow is entering an indeterminate state - this should be done + // at some point in closing anyway. + GtkWidget* window = window_; + window_ = nullptr; + // Destroying the top-level window also destroys its children. + drawing_area_ = nullptr; + box_ = nullptr; + if (event->type != GDK_DESTROY) { + gtk_widget_destroy(window); + } + OnAfterClose(); + } break; + + case GDK_FOCUS_CHANGE: { + auto focus_event = reinterpret_cast(event); + WindowDestructionReceiver destruction_receiver(this); + OnFocusUpdate(bool(focus_event->in), destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } break; + + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: { + WindowDestructionReceiver destruction_receiver(this); + HandleKeyboard(reinterpret_cast(event), + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } break; + + default: + break; + } + + // The window might have been destroyed by the handlers, don't interact with + // *this in this function from now on. + + return GDK_EVENT_PROPAGATE; +} + +gboolean GTKWindow::WindowEventHandlerThunk(GtkWidget* widget, GdkEvent* event, + gpointer user_data) { + GTKWindow* window = reinterpret_cast(user_data); + if (!window || widget != window->window_ || + reinterpret_cast(event)->window != + gtk_widget_get_window(window->window_)) { + return GDK_EVENT_PROPAGATE; + } + return window->WindowEventHandler(event); +} + +gboolean GTKWindow::DrawingAreaEventHandler(GdkEvent* event) { + switch (event->type) { + case GDK_MOTION_NOTIFY: + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_SCROLL: { + WindowDestructionReceiver destruction_receiver(this); + HandleMouse(event, destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } break; + + case GDK_CONFIGURE: { + if (batched_size_update_depth_) { + batched_size_update_contained_configure_ = true; + } else { + WindowDestructionReceiver destruction_receiver(this); + HandleSizeUpdate(destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } + } break; + default: + break; + } + + // The window might have been destroyed by the handlers, don't interact with + // *this in this function from now on. + + return GDK_EVENT_PROPAGATE; +} + +gboolean GTKWindow::DrawingAreaEventHandlerThunk(GtkWidget* widget, + GdkEvent* event, + gpointer user_data) { + GTKWindow* window = reinterpret_cast(user_data); + if (!window || widget != window->drawing_area_ || + reinterpret_cast(event)->window != + gtk_widget_get_window(window->drawing_area_)) { + return GDK_EVENT_PROPAGATE; + } + return window->DrawingAreaEventHandler(event); +} + +gboolean GTKWindow::DrawHandler(GtkWidget* widget, cairo_t* cr, + gpointer user_data) { + GTKWindow* window = reinterpret_cast(user_data); + if (!window || widget != window->drawing_area_) { + return FALSE; + } + if (window->batched_size_update_depth_) { + window->batched_size_update_contained_draw_ = true; + } else { + window->OnPaint(); + } + return TRUE; +} + std::unique_ptr MenuItem::Create(Type type, const std::string& text, const std::string& hotkey, @@ -434,17 +588,10 @@ std::unique_ptr MenuItem::Create(Type type, return std::make_unique(type, text, hotkey, callback); } -static void _menu_activate_callback(GtkWidget* gtk_menu, gpointer data) { - GTKMenuItem* menu = reinterpret_cast(data); - menu->Activate(); -} - -void GTKMenuItem::Activate() { - try { - callback_(); - } catch (const std::bad_function_call& e) { - // Ignore - } +void GTKMenuItem::ActivateHandler(GtkWidget* menu_item, gpointer user_data) { + static_cast(user_data)->OnSelected(); + // The menu item might have been destroyed by its OnSelected, don't do + // anything with it here from now on. } GTKMenuItem::GTKMenuItem(Type type, const std::string& text, @@ -475,13 +622,20 @@ GTKMenuItem::GTKMenuItem(Type type, const std::string& text, menu_ = gtk_menu_item_new_with_mnemonic(gtk_label); break; } - if (GTK_IS_MENU_ITEM(menu_)) - g_signal_connect(menu_, "activate", G_CALLBACK(_menu_activate_callback), - (gpointer)this); + if (menu_) { + // Own the object because it may be detached from and re-attached to a + // Window. + g_object_ref_sink(menu_); + if (GTK_IS_MENU_ITEM(menu_)) { + g_signal_connect(menu_, "activate", G_CALLBACK(ActivateHandler), + reinterpret_cast(this)); + } + } } GTKMenuItem::~GTKMenuItem() { if (menu_) { + g_object_unref(menu_); } } diff --git a/src/xenia/ui/window_gtk.h b/src/xenia/ui/window_gtk.h index 6677f2fc7..a78cae29f 100644 --- a/src/xenia/ui/window_gtk.h +++ b/src/xenia/ui/window_gtk.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -28,72 +28,61 @@ class GTKWindow : public Window { using super = Window; public: - GTKWindow(WindowedAppContext& app_context, const std::string& title); + GTKWindow(WindowedAppContext& app_context, const std::string_view title, + uint32_t desired_logical_width, uint32_t desired_logical_height); ~GTKWindow() override; - NativePlatformHandle native_platform_handle() const override { - return connection_; - } - NativeWindowHandle native_handle() const override { return window_; } - GtkWidget* native_window_handle() const { return drawing_area_; } - - void EnableMainMenu() override {} - void DisableMainMenu() override {} - - bool set_title(const std::string_view title) override; - - bool SetIcon(const void* buffer, size_t size) override; - - // This seems to happen implicitly compared to Windows. - bool CaptureMouse() override { return true; }; - bool ReleaseMouse() override { return true; }; - - bool is_fullscreen() const override; - void ToggleFullscreen(bool fullscreen) override; - - bool is_bordered() const override; - void set_bordered(bool enabled) override; - - void set_cursor_visible(bool value) override; - void set_focus(bool value) override; - - void Resize(int32_t width, int32_t height) override; - void Resize(int32_t left, int32_t top, int32_t right, - int32_t bottom) override; - - bool Initialize() override; - void Invalidate() override; - void Close() override; + // Will be null if the window hasn't been successfully opened yet, or has been + // closed. + GtkWidget* window() const { return window_; } protected: - bool OnCreate() override; - void OnMainMenuChange() override; - void OnDestroy() override; - void OnClose() override; + bool OpenImpl() override; + void RequestCloseImpl() override; - void OnResize(UIEvent* e) override; + void ApplyNewFullscreen() override; + void ApplyNewTitle() override; + void ApplyNewMainMenu(MenuItem* old_main_menu) override; + // Mouse capture seems to happen implicitly compared to Windows. + void FocusImpl() override; + + std::unique_ptr CreateSurfaceImpl( + Surface::TypeFlags allowed_types) override; + void RequestPaintImpl() override; private: - GtkWidget* window_; - GtkWidget* box_; - GtkWidget* drawing_area_; - xcb_connection_t* connection_; + void HandleSizeUpdate(WindowDestructionReceiver& destruction_receiver); + // For updating multiple factors that may influence the window size at once, + // without handling the configure event multiple times (that may not only + // result in wasted handling, but also in the state potentially changed to an + // inconsistent one in the middle of a size update by the listeners). + void BeginBatchedSizeUpdate(); + void EndBatchedSizeUpdate(WindowDestructionReceiver& destruction_receiver); - // C Callback shims for GTK - friend gboolean gtk_event_handler(GtkWidget*, GdkEvent*, gpointer); - friend gboolean close_callback(GtkWidget*, gpointer); - friend gboolean draw_callback(GtkWidget*, GdkFrameClock*, gpointer); + // Handling events related to the whole window. + bool HandleMouse(GdkEvent* event, + WindowDestructionReceiver& destruction_receiver); + bool HandleKeyboard(GdkEventKey* event, + WindowDestructionReceiver& destruction_receiver); + gboolean WindowEventHandler(GdkEvent* event); + static gboolean WindowEventHandlerThunk(GtkWidget* widget, GdkEvent* event, + gpointer user_data); - bool HandleMouse(GdkEventAny* event); - bool HandleKeyboard(GdkEventKey* event); - bool HandleWindowResize(GdkEventConfigure* event); - bool HandleWindowFocus(GdkEventFocus* event); - bool HandleWindowVisibility(GdkEventVisibility* event); - bool HandleWindowOwnerChange(GdkEventOwnerChange* event); - bool HandleWindowPaint(); + // Handling events related specifically to the drawing (client) area. + gboolean DrawingAreaEventHandler(GdkEvent* event); + static gboolean DrawingAreaEventHandlerThunk(GtkWidget* widget, + GdkEvent* event, + gpointer user_data); + static gboolean DrawHandler(GtkWidget* widget, cairo_t* cr, gpointer data); - bool closing_ = false; - bool fullscreen_ = false; + // Non-owning (initially floating) references to the widgets. + GtkWidget* window_ = nullptr; + GtkWidget* box_ = nullptr; + GtkWidget* drawing_area_ = nullptr; + + uint32_t batched_size_update_depth_ = 0; + bool batched_size_update_contained_configure_ = false; + bool batched_size_update_contained_draw_ = false; }; class GTKMenuItem : public MenuItem { @@ -102,20 +91,16 @@ class GTKMenuItem : public MenuItem { std::function callback); ~GTKMenuItem() override; - GtkWidget* handle() { return menu_; } - using MenuItem::OnSelected; - void Activate(); - - void EnableMenuItem(Window& window) override {} - void DisableMenuItem(Window& window) override {} + GtkWidget* handle() const { return menu_; } protected: void OnChildAdded(MenuItem* child_item) override; void OnChildRemoved(MenuItem* child_item) override; - GTKMenuItem* parent_ = nullptr; - GTKMenuItem* child_ = nullptr; private: + static void ActivateHandler(GtkWidget* menu_item, gpointer user_data); + + // An owning reference because a menu may be transferred between windows. GtkWidget* menu_ = nullptr; }; diff --git a/src/xenia/ui/window_listener.h b/src/xenia/ui/window_listener.h index 08f7a5c7e..6dbf36c77 100644 --- a/src/xenia/ui/window_listener.h +++ b/src/xenia/ui/window_listener.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -15,38 +15,39 @@ namespace xe { namespace ui { -// Virtual interface for types that want to listen for Window events. -// Use Window::AttachListener and Window::DetachListener to manage active -// listeners. +// Virtual interfaces for types that want to listen for Window events. +// Use Window::Add[Input]Listener and Window::Remove[Input]Listener to manage +// active listeners. + class WindowListener { public: virtual ~WindowListener() = default; - virtual void OnMainMenuChange() {} - virtual void OnClosing(UIEvent* e) {} - virtual void OnClosed(UIEvent* e) {} + // OnOpened will be followed by various initial setup listeners. + virtual void OnOpened(UISetupEvent& e) {} + virtual void OnClosing(UIEvent& e) {} - virtual void OnResize(UIEvent* e) {} - virtual void OnLayout(UIEvent* e) {} - virtual void OnPainting(UIEvent* e) {} - virtual void OnPaint(UIEvent* e) {} - virtual void OnPainted(UIEvent* e) {} - virtual void OnFileDrop(UIEvent* e) {} + virtual void OnDpiChanged(UISetupEvent& e) {} + virtual void OnResize(UISetupEvent& e) {} - virtual void OnVisible(UIEvent* e) {} - virtual void OnHidden(UIEvent* e) {} + virtual void OnGotFocus(UISetupEvent& e) {} + virtual void OnLostFocus(UISetupEvent& e) {} - virtual void OnGotFocus(UIEvent* e) {} - virtual void OnLostFocus(UIEvent* e) {} + virtual void OnFileDrop(FileDropEvent& e) {} +}; - virtual void OnKeyDown(KeyEvent* e) {} - virtual void OnKeyUp(KeyEvent* e) {} - virtual void OnKeyChar(KeyEvent* e) {} +class WindowInputListener { + public: + virtual ~WindowInputListener() = default; - virtual void OnMouseDown(MouseEvent* e) {} - virtual void OnMouseMove(MouseEvent* e) {} - virtual void OnMouseUp(MouseEvent* e) {} - virtual void OnMouseWheel(MouseEvent* e) {} + virtual void OnKeyDown(KeyEvent& e) {} + virtual void OnKeyUp(KeyEvent& e) {} + virtual void OnKeyChar(KeyEvent& e) {} + + virtual void OnMouseDown(MouseEvent& e) {} + virtual void OnMouseMove(MouseEvent& e) {} + virtual void OnMouseUp(MouseEvent& e) {} + virtual void OnMouseWheel(MouseEvent& e) {} }; } // namespace ui diff --git a/src/xenia/ui/window_win.cc b/src/xenia/ui/window_win.cc index a6d2c68ae..39ea5b3da 100644 --- a/src/xenia/ui/window_win.cc +++ b/src/xenia/ui/window_win.cc @@ -2,71 +2,74 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/ui/window_win.h" -#include - +#include #include #include "xenia/base/assert.h" #include "xenia/base/filesystem.h" #include "xenia/base/logging.h" +#include "xenia/ui/surface_win.h" + +// Must be included before Windows headers for things like NOMINMAX. #include "xenia/base/platform_win.h" #include "xenia/ui/virtual_key.h" #include "xenia/ui/windowed_app_context_win.h" +// For per-monitor DPI awareness v1. +#include + namespace xe { namespace ui { std::unique_ptr Window::Create(WindowedAppContext& app_context, - const std::string& title) { - return std::make_unique(app_context, title); + const std::string_view title, + uint32_t desired_logical_width, + uint32_t desired_logical_height) { + return std::make_unique( + app_context, title, desired_logical_width, desired_logical_height); } Win32Window::Win32Window(WindowedAppContext& app_context, - const std::string& title) - : Window(app_context, title) {} + const std::string_view title, + uint32_t desired_logical_width, + uint32_t desired_logical_height) + : Window(app_context, title, desired_logical_width, desired_logical_height), + arrow_cursor_(LoadCursor(nullptr, IDC_ARROW)) { + dpi_ = GetCurrentSystemDpi(); +} Win32Window::~Win32Window() { - OnDestroy(); + EnterDestructor(); + if (cursor_auto_hide_timer_) { + DeleteTimerQueueTimer(nullptr, cursor_auto_hide_timer_, nullptr); + cursor_auto_hide_timer_ = nullptr; + } if (hwnd_) { - SetWindowLongPtr(hwnd_, GWLP_USERDATA, 0); - CloseWindow(hwnd_); + // Set hwnd_ to null to ignore events from now on since this Win32Window is + // entering an indeterminate state. + HWND hwnd = hwnd_; hwnd_ = nullptr; + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + DestroyWindow(hwnd); } if (icon_) { DestroyIcon(icon_); - icon_ = nullptr; } } -NativePlatformHandle Win32Window::native_platform_handle() const { - return static_cast(app_context()).hinstance(); -} +uint32_t Win32Window::GetMediumDpi() const { return USER_DEFAULT_SCREEN_DPI; } -bool Win32Window::Initialize() { return OnCreate(); } - -bool Win32Window::OnCreate() { - HINSTANCE hInstance = - static_cast(app_context()).hinstance(); - - // Per-monitor DPI awareness is expected to be enabled via the manifest, as - // that's the recommended way, which also doesn't require calling - // SetProcessDpiAwareness before doing anything that may depend on DPI - // awareness (so it's safe to use any Windows APIs before this code). - // TODO(Triang3l): Safe handling of per-monitor DPI awareness v2, with - // automatic scaling on DPI change. - if (!GetDpiForMonitor_) { - auto shcore = GetModuleHandleW(L"shcore.dll"); - if (shcore) { - GetDpiForMonitor_ = GetProcAddress(shcore, "GetDpiForMonitor"); - } - } +bool Win32Window::OpenImpl() { + const Win32WindowedAppContext& win32_app_context = + static_cast(app_context()); + HINSTANCE hinstance = win32_app_context.hinstance(); static bool has_registered_class = false; if (!has_registered_class) { @@ -76,11 +79,12 @@ bool Win32Window::OnCreate() { wcex.lpfnWndProc = Win32Window::WndProcThunk; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; - wcex.hInstance = hInstance; - wcex.hIcon = LoadIconW(hInstance, L"MAINICON"); - wcex.hIconSm = NULL; // LoadIconW(hInstance, L"MAINICON"); - wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hInstance = hinstance; + wcex.hIcon = LoadIconW(hinstance, L"MAINICON"); + wcex.hIconSm = nullptr; // LoadIconW(hinstance, L"MAINICON"); + wcex.hCursor = arrow_cursor_; + // Matches the black background color of the presenter's painting. + wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wcex.lpszMenuName = nullptr; wcex.lpszClassName = L"XeniaWindowClass"; if (!RegisterClassExW(&wcex)) { @@ -90,22 +94,92 @@ bool Win32Window::OnCreate() { has_registered_class = true; } - // Setup initial size. + const Win32MenuItem* main_menu = + static_cast(GetMainMenu()); + + // Setup the initial size for the non-fullscreen window. With per-monitor DPI, + // this is also done to be able to obtain the initial window rectangle (with + // CW_USEDEFAULT) to get the monitor for the window position, and then to + // adjust the normal window size to the new DPI. + // Save the initial desired size since it may be modified by the handler of + // the WM_SIZE sent during window creation - it's needed for the initial + // per-monitor DPI scaling. + uint32_t initial_desired_logical_width = GetDesiredLogicalWidth(); + uint32_t initial_desired_logical_height = GetDesiredLogicalHeight(); + const Win32WindowedAppContext::PerMonitorDpiV2Api* per_monitor_dpi_v2_api = + win32_app_context.per_monitor_dpi_v2_api(); + const Win32WindowedAppContext::PerMonitorDpiV1Api* per_monitor_dpi_v1_api = + win32_app_context.per_monitor_dpi_v1_api(); + // Even with per-monitor DPI, take the closest approximation (system DPI) to + // potentially more accurately determine the initial monitor. + dpi_ = GetCurrentSystemDpi(); DWORD window_style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; DWORD window_ex_style = WS_EX_APPWINDOW | WS_EX_CONTROLPARENT; - RECT rc = {0, 0, width_, height_}; - AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); - - // Create window. - hwnd_ = - CreateWindowExW(window_ex_style, L"XeniaWindowClass", - reinterpret_cast(xe::to_utf16(title_).c_str()), - window_style, rc.left, rc.top, rc.right - rc.left, - rc.bottom - rc.top, nullptr, nullptr, hInstance, this); + RECT window_size_rect; + window_size_rect.left = 0; + window_size_rect.top = 0; + window_size_rect.right = LONG(ConvertSizeDpi(initial_desired_logical_width, + dpi_, USER_DEFAULT_SCREEN_DPI)); + window_size_rect.bottom = LONG(ConvertSizeDpi(initial_desired_logical_height, + dpi_, USER_DEFAULT_SCREEN_DPI)); + AdjustWindowRectangle(window_size_rect, window_style, + BOOL(main_menu != nullptr), window_ex_style, dpi_); + // Create the window. Though WM_NCCREATE will assign to `hwnd_` too, still do + // the assignment here to handle the case of a failure after WM_NCCREATE, for + // instance. + hwnd_ = CreateWindowExW( + window_ex_style, L"XeniaWindowClass", + reinterpret_cast(xe::to_utf16(GetTitle()).c_str()), window_style, + CW_USEDEFAULT, CW_USEDEFAULT, + window_size_rect.right - window_size_rect.left, + window_size_rect.bottom - window_size_rect.top, nullptr, nullptr, + hinstance, this); if (!hwnd_) { - XELOGE("CreateWindow failed"); + XELOGE("CreateWindowExW failed"); return false; } + // For per-monitor DPI, obtain the DPI of the monitor the window was created + // on, and adjust the initial normal size for it. If as a result of this + // resizing, the window is moved to a different monitor, the WM_DPICHANGED + // handler will do the needed correction. + uint32_t initial_monitor_dpi = dpi_; + if (per_monitor_dpi_v2_api) { + initial_monitor_dpi = per_monitor_dpi_v2_api->get_dpi_for_window(hwnd_); + } else if (per_monitor_dpi_v1_api) { + HMONITOR monitor = MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST); + UINT monitor_dpi_x, monitor_dpi_y; + if (monitor && SUCCEEDED(per_monitor_dpi_v1_api->get_dpi_for_monitor( + monitor, MDT_DEFAULT, &monitor_dpi_x, &monitor_dpi_y))) { + initial_monitor_dpi = monitor_dpi_x; + } + } + if (dpi_ != initial_monitor_dpi) { + dpi_ = initial_monitor_dpi; + WINDOWPLACEMENT initial_dpi_placement; + // Note that WINDOWPLACEMENT contains workspace coordinates, which are + // adjusted to exclude toolbars such as the taskbar - the positions and + // rectangle origins there can't be mixed with origins of rectangles in + // virtual screen coordinates such as those involved in functions like + // GetWindowRect. + initial_dpi_placement.length = sizeof(initial_dpi_placement); + if (GetWindowPlacement(hwnd_, &initial_dpi_placement)) { + window_size_rect.left = 0; + window_size_rect.top = 0; + window_size_rect.right = LONG(ConvertSizeDpi( + initial_desired_logical_width, dpi_, USER_DEFAULT_SCREEN_DPI)); + window_size_rect.bottom = LONG(ConvertSizeDpi( + initial_desired_logical_height, dpi_, USER_DEFAULT_SCREEN_DPI)); + AdjustWindowRectangle(window_size_rect, window_style, + BOOL(main_menu != nullptr), window_ex_style, dpi_); + initial_dpi_placement.rcNormalPosition.right = + initial_dpi_placement.rcNormalPosition.left + + (window_size_rect.right - window_size_rect.left); + initial_dpi_placement.rcNormalPosition.bottom = + initial_dpi_placement.rcNormalPosition.top + + (window_size_rect.bottom - window_size_rect.top); + SetWindowPlacement(hwnd_, &initial_dpi_placement); + } + } // Disable flicks. ATOM atom = GlobalAddAtomW(L"MicrosoftTabletPenServiceProperty"); @@ -123,533 +197,617 @@ bool Win32Window::OnCreate() { reinterpret_cast(dwHwndTabletProperty)); GlobalDeleteAtom(atom); - // Enable DWM elevation. - EnableMMCSS(); // Enable file dragging from external sources DragAcceptFiles(hwnd_, true); - ShowWindow(hwnd_, SW_SHOWNORMAL); - UpdateWindow(hwnd_); + // Apply the initial state from the Window that the window shouldn't be + // visibly transitioned to. - arrow_cursor_ = LoadCursor(nullptr, IDC_ARROW); - - // Initial state. - if (!is_cursor_visible_) { - ShowCursor(FALSE); - } - if (has_focus_) { - SetFocus(hwnd_); - } - - return super::OnCreate(); -} - -void Win32Window::EnableMMCSS() { - HMODULE hLibrary = LoadLibraryW(L"DWMAPI.DLL"); - if (!hLibrary) { - return; - } - - typedef HRESULT(__stdcall * PDwmEnableMMCSS)(BOOL); - PDwmEnableMMCSS pDwmEnableMMCSS = - (PDwmEnableMMCSS)GetProcAddress(hLibrary, "DwmEnableMMCSS"); - if (pDwmEnableMMCSS) { - pDwmEnableMMCSS(TRUE); - } - - typedef HRESULT(__stdcall * PDwmSetPresentParameters)( - HWND, DWM_PRESENT_PARAMETERS*); - PDwmSetPresentParameters pDwmSetPresentParameters = - (PDwmSetPresentParameters)GetProcAddress(hLibrary, - "DwmSetPresentParameters"); - if (pDwmSetPresentParameters) { - DWM_PRESENT_PARAMETERS pp; - std::memset(&pp, 0, sizeof(DWM_PRESENT_PARAMETERS)); - pp.cbSize = sizeof(DWM_PRESENT_PARAMETERS); - pp.fQueue = FALSE; - pp.cBuffer = 2; - pp.fUseSourceRate = FALSE; - pp.cRefreshesPerFrame = 1; - pp.eSampling = DWM_SOURCE_FRAME_SAMPLING_POINT; - pDwmSetPresentParameters(hwnd_, &pp); - } - - FreeLibrary(hLibrary); -} - -void Win32Window::OnDestroy() { super::OnDestroy(); } - -void Win32Window::OnClose() { - if (!closing_ && hwnd_) { - closing_ = true; - } - super::OnClose(); -} - -void Win32Window::EnableMainMenu() { - if (main_menu_) { - main_menu_->EnableMenuItem(*this); - } -} - -void Win32Window::DisableMainMenu() { - if (main_menu_) { - main_menu_->DisableMenuItem(*this); - } -} - -bool Win32Window::set_title(const std::string_view title) { - if (!super::set_title(title)) { - return false; - } - auto wide_title = xe::to_utf16(title); - SetWindowTextW(hwnd_, reinterpret_cast(wide_title.c_str())); - return true; -} - -bool Win32Window::SetIcon(const void* buffer, size_t size) { - if (icon_ != nullptr) { - DestroyIcon(icon_); - icon_ = nullptr; - } - - // Reset icon to default. - auto default_icon = LoadIconW( - static_cast(app_context()).hinstance(), - L"MAINICON"); - SendMessageW(hwnd_, WM_SETICON, ICON_BIG, - reinterpret_cast(default_icon)); - SendMessageW(hwnd_, WM_SETICON, ICON_SMALL, - reinterpret_cast(default_icon)); - if (!buffer || !size) { - return true; - } - - // Create icon and set on window (if it's valid). - icon_ = CreateIconFromResourceEx( - reinterpret_cast(const_cast(buffer)), - static_cast(size), TRUE, 0x00030000, 0, 0, - LR_DEFAULTCOLOR | LR_DEFAULTSIZE); if (icon_) { SendMessageW(hwnd_, WM_SETICON, ICON_BIG, reinterpret_cast(icon_)); SendMessageW(hwnd_, WM_SETICON, ICON_SMALL, reinterpret_cast(icon_)); } - return false; -} - -bool Win32Window::CaptureMouse() { - if (GetCapture() != nullptr) { - return false; + if (IsFullscreen()) { + // Go fullscreen after setting up everything related to the placement of the + // non-fullscreen window. + WindowDestructionReceiver destruction_receiver(this); + ApplyFullscreenEntry(destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return true; + } + } else { + if (main_menu) { + SetMenu(hwnd_, main_menu->handle()); + } } - SetCapture(hwnd_); + + // Finally show the window. + ShowWindow(hwnd_, SW_SHOWNORMAL); + + // Report the initial actual state after opening, messages for which might + // have missed if they were processed during CreateWindowExW when the HWND was + // not yet attached to the Win32Window. + { + WindowDestructionReceiver destruction_receiver(this); + + // Report the desired logical size of the client area in the non-maximized + // state after the initial layout setup in Windows. + WINDOWPLACEMENT shown_placement; + shown_placement.length = sizeof(shown_placement); + if (GetWindowPlacement(hwnd_, &shown_placement)) { + // Get the size of the non-client area to subtract it from the size of the + // entire window in its non-maximized state, to get the client area. + RECT non_client_area_rect = {}; + AdjustWindowRectangle(non_client_area_rect); + OnDesiredLogicalSizeUpdate( + SizeToLogical(uint32_t( + (shown_placement.rcNormalPosition.right - + shown_placement.rcNormalPosition.left) - + (non_client_area_rect.right - non_client_area_rect.left))), + SizeToLogical(uint32_t( + (shown_placement.rcNormalPosition.bottom - + shown_placement.rcNormalPosition.top) - + (non_client_area_rect.bottom - non_client_area_rect.top)))); + } + + // Report the actual physical size in the current state. + RECT shown_client_rect; + if (GetClientRect(hwnd_, &shown_client_rect)) { + OnActualSizeUpdate( + uint32_t(shown_client_rect.right - shown_client_rect.left), + uint32_t(shown_client_rect.bottom - shown_client_rect.top), + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return true; + } + } + + OnFocusUpdate(GetFocus() == hwnd_, destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return true; + } + } + + // Apply the initial state from the Window that involves interaction with the + // user. + + if (IsMouseCaptureRequested()) { + SetCapture(hwnd_); + } + + cursor_currently_auto_hidden_ = false; + CursorVisibility cursor_visibility = GetCursorVisibility(); + if (cursor_visibility != CursorVisibility::kVisible) { + if (cursor_visibility == CursorVisibility::kAutoHidden) { + if (!GetCursorPos(&cursor_auto_hide_last_screen_pos_)) { + cursor_auto_hide_last_screen_pos_.x = LONG_MAX; + cursor_auto_hide_last_screen_pos_.y = LONG_MAX; + } + cursor_currently_auto_hidden_ = true; + } + // OnFocusUpdate needs to be done before this. + SetCursorIfFocusedOnClientArea(nullptr); + } + return true; } -bool Win32Window::ReleaseMouse() { - if (GetCapture() != hwnd_) { - return false; - } - return ReleaseCapture() != 0; +void Win32Window::RequestCloseImpl() { + // Note that CloseWindow doesn't close the window, rather, it only minimizes + // it - need to send WM_CLOSE to let the Win32Window WndProc perform all the + // shutdown. + SendMessageW(hwnd_, WM_CLOSE, 0, 0); + // The window might have been deleted by the close handler, don't do anything + // with *this anymore (if that's needed, use a WindowDestructionReceiver). } -bool Win32Window::is_fullscreen() const { return fullscreen_; } +uint32_t Win32Window::GetLatestDpiImpl() const { + // hwnd_ may be null in this function, but the latest DPI is stored in a + // variable anyway. + return dpi_; +} -void Win32Window::ToggleFullscreen(bool fullscreen) { - if (fullscreen == is_fullscreen()) { - return; - } - - DWORD style = GetWindowLong(hwnd_, GWL_STYLE); - if (fullscreen) { - // https://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx - MONITORINFO mi = {sizeof(mi)}; - if (GetWindowPlacement(hwnd_, &windowed_pos_) && - GetMonitorInfo(MonitorFromWindow(hwnd_, MONITOR_DEFAULTTOPRIMARY), - &mi)) { - // Remove the menubar and borders. - SetWindowLong(hwnd_, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); - ::SetMenu(hwnd_, NULL); - - // Resize the window to fullscreen. - auto& rc = mi.rcMonitor; - AdjustWindowRect(&rc, GetWindowLong(hwnd_, GWL_STYLE), false); - MoveWindow(hwnd_, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, - TRUE); +void Win32Window::ApplyNewFullscreen() { + // Various functions here may send messages that may result in the + // listeners being invoked, and potentially cause the destruction of the + // window or fullscreen being toggled from inside this function. + WindowDestructionReceiver destruction_receiver(this); + if (IsFullscreen()) { + ApplyFullscreenEntry(destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; } } else { - // Reinstate borders, resize to 1280x720 - SetWindowLong(hwnd_, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); - SetWindowPlacement(hwnd_, &windowed_pos_); + // Changing the style and the menu may change the size too, don't handle + // the resize multiple times (also potentially with the listeners changing + // the desired fullscreen if called from the handling of some message like + // WM_SIZE). + BeginBatchedSizeUpdate(); - auto main_menu = reinterpret_cast(main_menu_.get()); + // Reinstate the non-client area. + SetWindowLong(hwnd_, GWL_STYLE, + GetWindowLong(hwnd_, GWL_STYLE) | WS_OVERLAPPEDWINDOW); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } + const Win32MenuItem* main_menu = + static_cast(GetMainMenu()); if (main_menu) { - ::SetMenu(hwnd_, main_menu->handle()); - } - } - - fullscreen_ = fullscreen; - - // width_ and height_ will be updated by the WM_SIZE handler - - // windowed_pos_.rcNormalPosition is also not the correct source for them when - // switching from fullscreen to maximized. -} - -bool Win32Window::is_bordered() const { - DWORD style = GetWindowLong(hwnd_, GWL_STYLE); - return (style & WS_OVERLAPPEDWINDOW) == WS_OVERLAPPEDWINDOW; -} - -void Win32Window::set_bordered(bool enabled) { - if (is_fullscreen()) { - // Don't screw with the borders if we're fullscreen. - return; - } - - DWORD style = GetWindowLong(hwnd_, GWL_STYLE); - if (enabled) { - SetWindowLong(hwnd_, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); - } else { - SetWindowLong(hwnd_, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); - } -} - -int Win32Window::get_dpi() const { - // TODO(Triang3l): Cache until WM_DPICHANGED is received (which, with - // per-monitor awareness v2 will also receive the new suggested window size). - // According to MSDN, x and y are identical. - - if (!GetDpiForMonitor_) { - HDC screen_hdc = GetDC(nullptr); - if (!screen_hdc) { - return get_medium_dpi(); - } - int logical_pixels_x = GetDeviceCaps(screen_hdc, LOGPIXELSX); - ReleaseDC(nullptr, screen_hdc); - return logical_pixels_x; - } - - HMONITOR monitor = MonitorFromWindow(hwnd_, MONITOR_DEFAULTTOPRIMARY); - - UINT dpi_x, dpi_y; - auto gdfm = (decltype(&GetDpiForMonitor))GetDpiForMonitor_; - gdfm(monitor, MDT_DEFAULT, &dpi_x, &dpi_y); - return dpi_x; -} - -void Win32Window::set_cursor_visible(bool value) { - if (is_cursor_visible_ == value) { - return; - } - is_cursor_visible_ = value; - - if (value) { - ShowCursor(TRUE); - } else { - ShowCursor(FALSE); - } -} - -void Win32Window::set_focus(bool value) { - if (has_focus_ == value) { - return; - } - if (hwnd_) { - if (value) { - SetFocus(hwnd_); - } else { - SetFocus(nullptr); - } - } else { - has_focus_ = value; - } -} - -void Win32Window::Resize(int32_t width, int32_t height) { - if (is_fullscreen()) { - // Cannot resize while in fullscreen. - return; - } - - // Scale width and height - int32_t scaled_width, scaled_height; - float dpi_scale = get_dpi_scale(); - scaled_width = int32_t(width * dpi_scale); - scaled_height = int32_t(height * dpi_scale); - - RECT rc = {0, 0, 0, 0}; - GetWindowRect(hwnd_, &rc); - if (rc.top < 0) { - rc.top = 100; - } - if (rc.left < 0) { - rc.left = 100; - } - - rc.right = rc.left + scaled_width; - rc.bottom = rc.top + scaled_height; - - bool has_menu = !is_fullscreen() && (main_menu_ ? true : false); - AdjustWindowRect(&rc, GetWindowLong(hwnd_, GWL_STYLE), has_menu); - MoveWindow(hwnd_, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, - TRUE); - - super::Resize(width, height); -} - -void Win32Window::Resize(int32_t left, int32_t top, int32_t right, - int32_t bottom) { - if (is_fullscreen()) { - // Cannot resize while in fullscreen. - return; - } - - RECT rc = {left, top, right, bottom}; - - // Scale width and height - float dpi_scale = get_dpi_scale(); - rc.right = int32_t((right - left) * dpi_scale) + left; - rc.bottom = int32_t((bottom - top) * dpi_scale) + top; - - bool has_menu = !is_fullscreen() && (main_menu_ ? true : false); - AdjustWindowRect(&rc, GetWindowLong(hwnd_, GWL_STYLE), has_menu); - MoveWindow(hwnd_, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, - TRUE); - - super::Resize(left, top, right, bottom); -} - -void Win32Window::RawReposition(const RECT& rc) { - MoveWindow(hwnd_, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, - TRUE); -} - -void Win32Window::OnResize(UIEvent* e) { - RECT client_rect; - GetClientRect(hwnd_, &client_rect); - int32_t width = client_rect.right - client_rect.left; - int32_t height = client_rect.bottom - client_rect.top; - - // Rescale to base DPI. - float dpi_scale = get_dpi_scale(); - width = int32_t(width / dpi_scale); - height = int32_t(height / dpi_scale); - - if (width != width_ || height != height_) { - width_ = width; - height_ = height; - Layout(); - } - super::OnResize(e); -} - -void Win32Window::Invalidate() { - super::Invalidate(); - InvalidateRect(hwnd_, nullptr, FALSE); -} - -void Win32Window::Close() { - if (closing_) { - return; - } - closing_ = true; - OnClose(); - DestroyWindow(hwnd_); - hwnd_ = nullptr; -} - -void Win32Window::OnMainMenuChange() { - auto main_menu = reinterpret_cast(main_menu_.get()); - // Don't actually set the menu if we're fullscreen. We'll do that later. - if (main_menu && !is_fullscreen()) { - ::SetMenu(hwnd_, main_menu->handle()); - } -} - -LRESULT CALLBACK Win32Window::WndProcThunk(HWND hWnd, UINT message, - WPARAM wParam, LPARAM lParam) { - Win32Window* window = nullptr; - if (message == WM_NCCREATE) { - auto create_struct = reinterpret_cast(lParam); - window = reinterpret_cast(create_struct->lpCreateParams); - SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(LONG_PTR)window); - } else { - window = - reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); - } - if (window) { - return window->WndProc(hWnd, message, wParam, lParam); - } else { - return DefWindowProc(hWnd, message, wParam, lParam); - } -} - -LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, - LPARAM lParam) { - if (hwnd_ != nullptr && hWnd != hwnd_) { - return DefWindowProc(hWnd, message, wParam, lParam); - } - - if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) { - if (HandleMouse(message, wParam, lParam)) { - return 0; - } else { - return DefWindowProc(hWnd, message, wParam, lParam); - } - } else if (message >= WM_KEYFIRST && message <= WM_KEYLAST) { - if (HandleKeyboard(message, wParam, lParam)) { - return 0; - } else { - return DefWindowProc(hWnd, message, wParam, lParam); - } - } - - switch (message) { - case WM_DROPFILES: { - HDROP drop_handle = reinterpret_cast(wParam); - auto drop_count = DragQueryFileW(drop_handle, 0xFFFFFFFFu, nullptr, 0); - if (drop_count > 0) { - // Get required buffer size - UINT path_size = DragQueryFileW(drop_handle, 0, nullptr, 0); - if (path_size > 0 && path_size < 0xFFFFFFFFu) { - std::u16string path; - ++path_size; // Ensure space for the null terminator - path.resize(path_size); // Reserve space - // Only getting first file dropped (other files ignored) - path_size = - DragQueryFileW(drop_handle, 0, (LPWSTR)&path[0], path_size); - if (path_size > 0) { - path.resize(path_size); // Will drop the null terminator - auto e = FileDropEvent(this, xe::to_path(path)); - OnFileDrop(&e); - } + SetMenu(hwnd_, main_menu->handle()); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); } + return; } - DragFinish(drop_handle); - } break; - case WM_NCCREATE: { - // Tell Windows to automatically scale non-client areas on different DPIs. - auto en = (BOOL(WINAPI*)(HWND hwnd))GetProcAddress( - GetModuleHandleW(L"user32.dll"), "EnableNonClientDpiScaling"); - if (en) { - en(hWnd); - } - } break; - case WM_CREATE: - break; - case WM_DESTROY: - OnDestroy(); - break; - case WM_CLOSE: - OnClose(); - break; - - case WM_MOVING: - break; - case WM_MOVE: - break; - case WM_SIZING: - break; - case WM_SIZE: { - auto e = UIEvent(this); - OnResize(&e); - } break; - - case WM_PAINT: { - ValidateRect(hwnd_, nullptr); - static bool in_paint = false; - if (!in_paint) { - in_paint = true; - auto e = UIEvent(this); - OnPaint(&e); - in_paint = false; - } - return 0; // Ignored because of custom paint. - } - case WM_ERASEBKGND: - return 0; // Ignored because of custom paint. - case WM_DISPLAYCHANGE: - break; - case WM_DPICHANGED: { - LPRECT rect = (LPRECT)lParam; - if (rect) { - RawReposition(*rect); - } - - auto e = UIEvent(this); - OnDpiChanged(&e); - } break; - - case WM_ACTIVATEAPP: - case WM_SHOWWINDOW: { - if (wParam == TRUE) { - auto e = UIEvent(this); - OnVisible(&e); - } else { - auto e = UIEvent(this); - OnHidden(&e); - } - break; } - case WM_KILLFOCUS: { - has_focus_ = false; - auto e = UIEvent(this); - OnLostFocus(&e); - break; + // For some reason, WM_DPICHANGED is not sent when the window is borderless + // fullscreen with per-monitor DPI awareness v1 (on Windows versions since + // Windows 8.1 before Windows 10 1703) - refresh the current DPI explicitly. + dpi_ = GetCurrentDpi(); + if (dpi_ != pre_fullscreen_dpi_) { + // Rescale the pre-fullscreen non-maximized window size to the new DPI as + // WM_DPICHANGED with the new rectangle was received for the fullscreen + // window size, not the windowed one. Simulating the behavior of the + // automatic resizing when changing the scale in the Windows settings (as + // of Windows 11 21H2 at least), which keeps the physical top-left origin + // of the entire window including the non-client area, but rescales the + // size. + // Note that WINDOWPLACEMENT contains workspace coordinates, which are + // adjusted to exclude toolbars such as the taskbar - the positions and + // rectangle origins there can't be mixed with origins of rectangles in + // virtual screen coordinates such as those involved in functions like + // GetWindowRect. + RECT new_dpi_rect; + new_dpi_rect.left = 0; + new_dpi_rect.top = 0; + new_dpi_rect.right = LONG(ConvertSizeDpi( + pre_fullscreen_normal_client_width_, dpi_, pre_fullscreen_dpi_)); + new_dpi_rect.bottom = LONG(ConvertSizeDpi( + pre_fullscreen_normal_client_height_, dpi_, pre_fullscreen_dpi_)); + AdjustWindowRectangle(new_dpi_rect); + pre_fullscreen_placement_.rcNormalPosition.right = + pre_fullscreen_placement_.rcNormalPosition.left + + (new_dpi_rect.right - new_dpi_rect.left); + pre_fullscreen_placement_.rcNormalPosition.bottom = + pre_fullscreen_placement_.rcNormalPosition.top + + (new_dpi_rect.bottom - new_dpi_rect.top); } - case WM_SETFOCUS: { - has_focus_ = true; - auto e = UIEvent(this); - OnGotFocus(&e); - break; + SetWindowPlacement(hwnd_, &pre_fullscreen_placement_); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; } - case WM_TABLET_QUERYSYSTEMGESTURESTATUS: - return - // disables press and hold (right-click) gesture - TABLET_DISABLE_PRESSANDHOLD | - // disables UI feedback on pen up (waves) - TABLET_DISABLE_PENTAPFEEDBACK | - // disables UI feedback on pen button down (circle) - TABLET_DISABLE_PENBARRELFEEDBACK | - // disables pen flicks (back, forward, drag down, drag up) - TABLET_DISABLE_FLICKS | TABLET_DISABLE_TOUCHSWITCH | - TABLET_DISABLE_SMOOTHSCROLLING | TABLET_DISABLE_TOUCHUIFORCEON | - TABLET_ENABLE_MULTITOUCHDATA; + // https://devblogs.microsoft.com/oldnewthing/20131017-00/?p=2903 + SetWindowPos(hwnd_, nullptr, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | + SWP_FRAMECHANGED); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } - case WM_MENUCOMMAND: { - // TODO(benvanik): Redirect this to MenuItem's on_selected delegate. - MENUINFO menu_info = {0}; - menu_info.cbSize = sizeof(menu_info); - menu_info.fMask = MIM_MENUDATA; - GetMenuInfo(HMENU(lParam), &menu_info); - auto parent_item = reinterpret_cast(menu_info.dwMenuData); - auto child_item = - reinterpret_cast(parent_item->child(wParam)); - assert_not_null(child_item); - UIEvent e(this); - child_item->OnSelected(&e); - } break; + EndBatchedSizeUpdate(destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; + } } - - return DefWindowProc(hWnd, message, wParam, lParam); } -bool Win32Window::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam) { - int32_t x = GET_X_LPARAM(lParam); - int32_t y = GET_Y_LPARAM(lParam); - if (message == WM_MOUSEWHEEL) { - POINT pt = {x, y}; - ScreenToClient(hwnd_, &pt); - x = pt.x; - y = pt.y; +void Win32Window::ApplyNewTitle() { + SetWindowTextW(hwnd_, + reinterpret_cast(xe::to_utf16(GetTitle()).c_str())); +} + +void Win32Window::LoadAndApplyIcon(const void* buffer, size_t size, + bool can_apply_state_in_current_phase) { + bool reset = !buffer || !size; + + HICON new_icon, new_icon_small; + if (reset) { + if (!icon_) { + // The icon is already the default one. + return; + } + if (!hwnd_) { + // Don't need to get the actual icon from the class if there's nothing to + // set it for yet (and there's no HWND to get it from, and the class may + // have not been registered yet also). + DestroyIcon(icon_); + icon_ = nullptr; + return; + } + new_icon = reinterpret_cast(GetClassLongPtrW(hwnd_, GCLP_HICON)); + new_icon_small = + reinterpret_cast(GetClassLongPtrW(hwnd_, GCLP_HICONSM)); + // Not caring if it's null in the class, accepting anything the class + // specifies. + } else { + new_icon = CreateIconFromResourceEx( + static_cast(const_cast(buffer)), DWORD(size), TRUE, + 0x00030000, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE); + if (!new_icon) { + return; + } + new_icon_small = new_icon; + } + + if (hwnd_) { + SendMessageW(hwnd_, WM_SETICON, ICON_BIG, + reinterpret_cast(new_icon)); + SendMessageW(hwnd_, WM_SETICON, ICON_SMALL, + reinterpret_cast(new_icon_small)); + } + + // The old icon is not in use anymore, safe to destroy it now. + if (icon_) { + DestroyIcon(icon_); + icon_ = nullptr; + } + + if (!reset) { + assert_true(new_icon_small == new_icon); + icon_ = new_icon; + } +} + +void Win32Window::ApplyNewMainMenu(MenuItem* old_main_menu) { + if (IsFullscreen()) { + // The menu will be set when exiting fullscreen. + return; + } + const Win32MenuItem* main_menu = + static_cast(GetMainMenu()); + WindowDestructionReceiver destruction_receiver(this); + SetMenu(hwnd_, main_menu ? main_menu->handle() : nullptr); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; + } +} + +void Win32Window::CompleteMainMenuItemsUpdateImpl() { + if (IsFullscreen()) { + return; + } + DrawMenuBar(hwnd_); +} + +void Win32Window::ApplyNewMouseCapture() { + WindowDestructionReceiver destruction_receiver(this); + SetCapture(hwnd_); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; + } +} + +void Win32Window::ApplyNewMouseRelease() { + if (GetCapture() != hwnd_) { + return; + } + WindowDestructionReceiver destruction_receiver(this); + ReleaseCapture(); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; + } +} + +void Win32Window::ApplyNewCursorVisibility( + CursorVisibility old_cursor_visibility) { + CursorVisibility new_cursor_visibility = GetCursorVisibility(); + cursor_currently_auto_hidden_ = false; + if (new_cursor_visibility == CursorVisibility::kAutoHidden) { + if (!GetCursorPos(&cursor_auto_hide_last_screen_pos_)) { + cursor_auto_hide_last_screen_pos_.x = LONG_MAX; + cursor_auto_hide_last_screen_pos_.y = LONG_MAX; + } + cursor_currently_auto_hidden_ = true; + } else if (old_cursor_visibility == CursorVisibility::kAutoHidden) { + if (cursor_auto_hide_timer_) { + DeleteTimerQueueTimer(nullptr, cursor_auto_hide_timer_, nullptr); + cursor_auto_hide_timer_ = nullptr; + } + } + SetCursorIfFocusedOnClientArea( + new_cursor_visibility == CursorVisibility::kVisible ? arrow_cursor_ + : nullptr); +} + +void Win32Window::FocusImpl() { SetFocus(hwnd_); } + +std::unique_ptr Win32Window::CreateSurfaceImpl( + Surface::TypeFlags allowed_types) { + HINSTANCE hInstance = + static_cast(app_context()).hinstance(); + if (allowed_types & Surface::kTypeFlag_Win32Hwnd) { + return std::make_unique(hInstance, hwnd_); + } + return nullptr; +} + +void Win32Window::RequestPaintImpl() { InvalidateRect(hwnd_, nullptr, FALSE); } + +BOOL Win32Window::AdjustWindowRectangle(RECT& rect, DWORD style, BOOL menu, + DWORD ex_style, UINT dpi) const { + const Win32WindowedAppContext& win32_app_context = + static_cast(app_context()); + const Win32WindowedAppContext::PerMonitorDpiV2Api* per_monitor_dpi_v2_api = + win32_app_context.per_monitor_dpi_v2_api(); + if (per_monitor_dpi_v2_api) { + return per_monitor_dpi_v2_api->adjust_window_rect_ex_for_dpi( + &rect, style, menu, ex_style, dpi); + } + // Before per-monitor DPI v2, there was no rescaling of the non-client + // area at runtime at all, so throughout the execution of the process it will + // behave the same regardless of the DPI. + return AdjustWindowRectEx(&rect, style, menu, ex_style); +} + +BOOL Win32Window::AdjustWindowRectangle(RECT& rect) const { + if (!hwnd_) { + return FALSE; + } + return AdjustWindowRectangle(rect, GetWindowLong(hwnd_, GWL_STYLE), + BOOL(GetMainMenu() != nullptr), + GetWindowLong(hwnd_, GWL_EXSTYLE), dpi_); +} + +uint32_t Win32Window::GetCurrentSystemDpi() const { + const Win32WindowedAppContext& win32_app_context = + static_cast(app_context()); + const Win32WindowedAppContext::PerMonitorDpiV2Api* per_monitor_dpi_v2_api = + win32_app_context.per_monitor_dpi_v2_api(); + if (per_monitor_dpi_v2_api) { + return per_monitor_dpi_v2_api->get_dpi_for_system(); + } + + HDC screen_hdc = GetDC(nullptr); + if (!screen_hdc) { + return USER_DEFAULT_SCREEN_DPI; + } + // According to MSDN, x and y are identical. + int logical_pixels_x = GetDeviceCaps(screen_hdc, LOGPIXELSX); + ReleaseDC(nullptr, screen_hdc); + return uint32_t(logical_pixels_x); +} + +uint32_t Win32Window::GetCurrentDpi() const { + if (hwnd_) { + const Win32WindowedAppContext& win32_app_context = + static_cast(app_context()); + + const Win32WindowedAppContext::PerMonitorDpiV2Api* per_monitor_dpi_v2_api = + win32_app_context.per_monitor_dpi_v2_api(); + if (per_monitor_dpi_v2_api) { + return per_monitor_dpi_v2_api->get_dpi_for_window(hwnd_); + } + + const Win32WindowedAppContext::PerMonitorDpiV1Api* per_monitor_dpi_v1_api = + win32_app_context.per_monitor_dpi_v1_api(); + if (per_monitor_dpi_v1_api) { + HMONITOR monitor = MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST); + UINT monitor_dpi_x, monitor_dpi_y; + if (monitor && + SUCCEEDED(per_monitor_dpi_v1_api->get_dpi_for_monitor( + monitor, MDT_DEFAULT, &monitor_dpi_x, &monitor_dpi_y))) { + // According to MSDN, x and y are identical. + return monitor_dpi_x; + } + } + } + + return GetCurrentSystemDpi(); +} + +void Win32Window::ApplyFullscreenEntry( + WindowDestructionReceiver& destruction_receiver) { + if (!IsFullscreen()) { + return; + } + + // https://blogs.msdn.com/b/oldnewthing/archive/2010/04/12/9994016.aspx + // No reason to use MONITOR_DEFAULTTOPRIMARY instead of + // MONITOR_DEFAULTTONEAREST, however. + pre_fullscreen_dpi_ = dpi_; + pre_fullscreen_placement_.length = sizeof(pre_fullscreen_placement_); + HMONITOR monitor; + MONITORINFO monitor_info; + monitor_info.cbSize = sizeof(monitor_info); + if (!GetWindowPlacement(hwnd_, &pre_fullscreen_placement_) || + !(monitor = MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST)) || + !GetMonitorInfo(monitor, &monitor_info)) { + OnDesiredFullscreenUpdate(false); + return; + } + // Preserve values for DPI rescaling of the window in the non-maximized state + // if DPI is changed mid-fullscreen. + // Get the size of the non-client area to subtract it from the size of the + // entire window in its non-maximized state, to get the client area. + RECT non_client_area_rect = {}; + AdjustWindowRectangle(non_client_area_rect); + pre_fullscreen_normal_client_width_ = + uint32_t((pre_fullscreen_placement_.rcNormalPosition.right - + pre_fullscreen_placement_.rcNormalPosition.left) - + (non_client_area_rect.right - non_client_area_rect.left)); + pre_fullscreen_normal_client_height_ = + uint32_t((pre_fullscreen_placement_.rcNormalPosition.bottom - + pre_fullscreen_placement_.rcNormalPosition.top) - + (non_client_area_rect.bottom - non_client_area_rect.top)); + + // Changing the style and the menu may change the size too, don't handle the + // resize multiple times (also potentially with the listeners changing the + // desired fullscreen if called from the handling of some message like + // WM_SIZE). + BeginBatchedSizeUpdate(); + + // Remove the non-client area. + if (GetMainMenu()) { + SetMenu(hwnd_, nullptr); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } + } + SetWindowLong(hwnd_, GWL_STYLE, + GetWindowLong(hwnd_, GWL_STYLE) & ~DWORD(WS_OVERLAPPEDWINDOW)); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } + + // Resize the window to fullscreen. It is important that this is done _after_ + // disabling the decorations and the menu, to make sure that composition will + // not have to be done for the new size of the window at all, so independent, + // low-latency presentation is possible immediately - if the window was + // involved in composition, it may stay composed persistently until some other + // state change that sometimes helps, sometimes doesn't, even if it becomes + // borderless fullscreen again - this occurs sometimes at least on Windows 11 + // 21H2 on Nvidia GeForce GTX 1070 on driver version 472.12. + SetWindowPos(hwnd_, HWND_TOP, monitor_info.rcMonitor.left, + monitor_info.rcMonitor.top, + monitor_info.rcMonitor.right - monitor_info.rcMonitor.left, + monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + if (!destruction_receiver.IsWindowDestroyed()) { + EndBatchedSizeUpdate(destruction_receiver); + } + return; + } + + EndBatchedSizeUpdate(destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; + } +} + +void Win32Window::HandleSizeUpdate( + WindowDestructionReceiver& destruction_receiver) { + if (!hwnd_) { + // Batched size update ended when the window has already been closed, for + // instance. + return; + } + + { + MonitorUpdateEvent e(this, false); + OnMonitorUpdate(e); + } + + // For the desired size in the normal, not maximized and not fullscreen state. + if (!IsFullscreen()) { + WINDOWPLACEMENT window_placement; + window_placement.length = sizeof(window_placement); + if (GetWindowPlacement(hwnd_, &window_placement)) { + // window_placement.rcNormalPosition is the entire window's rectangle, not + // only the client area - convert to client. + // https://devblogs.microsoft.com/oldnewthing/20131017-00/?p=2903 + RECT non_client_rect = {}; + if (AdjustWindowRectangle(non_client_rect)) { + OnDesiredLogicalSizeUpdate( + SizeToLogical(uint32_t((window_placement.rcNormalPosition.right - + non_client_rect.right) - + (window_placement.rcNormalPosition.left - + non_client_rect.left))), + SizeToLogical(uint32_t((window_placement.rcNormalPosition.bottom - + non_client_rect.bottom) - + (window_placement.rcNormalPosition.top - + non_client_rect.top)))); + } + } + } + + // For the actual state. + RECT client_rect; + if (GetClientRect(hwnd_, &client_rect)) { + OnActualSizeUpdate(uint32_t(client_rect.right - client_rect.left), + uint32_t(client_rect.bottom - client_rect.top), + destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; + } + } +} + +void Win32Window::BeginBatchedSizeUpdate() { + // It's okay if batched_size_update_contained_* are not false when beginning + // a batched update, in case the new batched update was started by a window + // listener called from within EndBatchedSizeUpdate. + ++batched_size_update_depth_; +} + +void Win32Window::EndBatchedSizeUpdate( + WindowDestructionReceiver& destruction_receiver) { + assert_not_zero(batched_size_update_depth_); + if (--batched_size_update_depth_) { + return; + } + // Resetting batched_size_update_contained_* in closing, not opening, because + // a listener may start a new batch, and finish it, and there won't be need to + // handle the deferred messages twice. + if (batched_size_update_contained_wm_size_) { + batched_size_update_contained_wm_size_ = false; + HandleSizeUpdate(destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + return; + } + } + if (batched_size_update_contained_wm_paint_) { + batched_size_update_contained_wm_paint_ = false; + RequestPaint(); + } +} + +bool Win32Window::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam, + WindowDestructionReceiver& destruction_receiver) { + // Mouse messages usually contain the position in the client area in lParam, + // but WM_MOUSEWHEEL is an exception, it passes the screen position. + int32_t message_x = GET_X_LPARAM(lParam); + int32_t message_y = GET_Y_LPARAM(lParam); + bool message_pos_is_screen = message == WM_MOUSEWHEEL; + + POINT client_pos = {message_x, message_y}; + if (message_pos_is_screen) { + ScreenToClient(hwnd_, &client_pos); + } + + if (GetCursorVisibility() == CursorVisibility::kAutoHidden) { + POINT screen_pos = {message_x, message_y}; + if (message_pos_is_screen || ClientToScreen(hwnd_, &screen_pos)) { + if (screen_pos.x != cursor_auto_hide_last_screen_pos_.x || + screen_pos.y != cursor_auto_hide_last_screen_pos_.y) { + // WM_MOUSEMOVE messages followed by WM_SETCURSOR may be sent for + // reasons not always involving actual mouse movement performed by the + // user. They're sent when the position of the cursor relative to the + // client area has been changed, as well as other events related to + // window management (including when creating the window), even when not + // interacting with the OS. These should not be revealing the cursor. + // Only revealing it if the mouse has actually been moved. + cursor_currently_auto_hidden_ = false; + SetCursorAutoHideTimer(); + // There's no need to SetCursor here, mouse messages relevant to the + // cursor within the window are always followed by WM_SETCURSOR. + cursor_auto_hide_last_screen_pos_ = screen_pos; + } + } } MouseEvent::Button button = MouseEvent::Button::kNone; - int32_t dx = x - last_mouse_pos_.x; - int32_t dy = y - last_mouse_pos_.y; + int32_t scroll_y = 0; switch (message) { case WM_LBUTTONDOWN: case WM_LBUTTONUP: @@ -673,7 +831,8 @@ bool Win32Window::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam) { button = MouseEvent::Button::kX2; break; default: - return false; + // Still handle the movement. + break; } break; case WM_MOUSEMOVE: @@ -681,59 +840,412 @@ bool Win32Window::HandleMouse(UINT message, WPARAM wParam, LPARAM lParam) { break; case WM_MOUSEWHEEL: button = MouseEvent::Button::kNone; - dx = 0; // ? - dy = GET_WHEEL_DELTA_WPARAM(wParam); + static_assert( + MouseEvent::kScrollPerDetent == WHEEL_DELTA, + "Assuming the Windows scroll amount can be passed directly to " + "MouseEvent"); + scroll_y = GET_WHEEL_DELTA_WPARAM(wParam); break; default: - // Double click/etc? - return true; + return false; } - last_mouse_pos_ = {x, y}; - - auto e = MouseEvent(this, button, x, y, dx, dy); + MouseEvent e(this, button, client_pos.x, client_pos.y, 0, scroll_y); switch (message) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: - OnMouseDown(&e); + OnMouseDown(e, destruction_receiver); break; case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: - OnMouseUp(&e); + OnMouseUp(e, destruction_receiver); break; case WM_MOUSEMOVE: - OnMouseMove(&e); + OnMouseMove(e, destruction_receiver); break; case WM_MOUSEWHEEL: - OnMouseWheel(&e); + OnMouseWheel(e, destruction_receiver); + break; + default: break; } + // Returning immediately anyway - no need to check + // destruction_receiver.IsWindowDestroyed(). return e.is_handled(); } -bool Win32Window::HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam) { - auto e = KeyEvent( - this, VirtualKey(wParam), lParam & 0xFFFF0000, !!(lParam & 0x2), - !!(GetKeyState(VK_SHIFT) & 0x80), !!(GetKeyState(VK_CONTROL) & 0x80), - !!(GetKeyState(VK_MENU) & 0x80), !!(GetKeyState(VK_LWIN) & 0x80)); +bool Win32Window::HandleKeyboard( + UINT message, WPARAM wParam, LPARAM lParam, + WindowDestructionReceiver& destruction_receiver) { + KeyEvent e(this, VirtualKey(wParam), lParam & 0xFFFF, + !!(lParam & (LPARAM(1) << 30)), !!(GetKeyState(VK_SHIFT) & 0x80), + !!(GetKeyState(VK_CONTROL) & 0x80), + !!(GetKeyState(VK_MENU) & 0x80), !!(GetKeyState(VK_LWIN) & 0x80)); switch (message) { case WM_KEYDOWN: - OnKeyDown(&e); + OnKeyDown(e, destruction_receiver); break; case WM_KEYUP: - OnKeyUp(&e); + OnKeyUp(e, destruction_receiver); break; case WM_CHAR: - OnKeyChar(&e); + OnKeyChar(e, destruction_receiver); + break; + default: break; } + // Returning immediately anyway - no need to check + // destruction_receiver.IsWindowDestroyed(). return e.is_handled(); } +void Win32Window::SetCursorIfFocusedOnClientArea(HCURSOR cursor) const { + if (!HasFocus()) { + return; + } + POINT cursor_pos; + if (!GetCursorPos(&cursor_pos)) { + return; + } + if (WindowFromPoint(cursor_pos) == hwnd_ && + SendMessage(hwnd_, WM_NCHITTEST, 0, + MAKELONG(cursor_pos.x, cursor_pos.y)) == HTCLIENT) { + SetCursor(cursor); + } +} + +void Win32Window::SetCursorAutoHideTimer() { + // Reset the timer by deleting the old timer and creating the new one. + // ChangeTimerQueueTimer doesn't work if the timer has already expired. + if (cursor_auto_hide_timer_) { + DeleteTimerQueueTimer(nullptr, cursor_auto_hide_timer_, nullptr); + cursor_auto_hide_timer_ = nullptr; + } + // After making sure that the callback is not callable anymore + // (DeleteTimerQueueTimer waits for the completion of the callback if it has + // been called already, or cancels it if it's hasn't), update the most recent + // message revision. + last_cursor_auto_hide_queued = last_cursor_auto_hide_signaled + 1; + CreateTimerQueueTimer(&cursor_auto_hide_timer_, nullptr, + AutoHideCursorTimerCallback, this, + kDefaultCursorAutoHideMilliseconds, 0, + WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE); +} + +void Win32Window::AutoHideCursorTimerCallback(void* parameter, + BOOLEAN timer_or_wait_fired) { + if (!timer_or_wait_fired) { + // Not a timer callback. + return; + } + Win32Window& window = *static_cast(parameter); + window.last_cursor_auto_hide_signaled = window.last_cursor_auto_hide_queued; + SendMessage(window.hwnd_, kUserMessageAutoHideCursor, + window.last_cursor_auto_hide_signaled, 0); +} + +LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam) { + if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) { + WindowDestructionReceiver destruction_receiver(this); + // Returning immediately anyway - no need to check + // destruction_receiver.IsWindowDestroyed() afterwards. + return HandleMouse(message, wParam, lParam, destruction_receiver) + ? 0 + : DefWindowProc(hWnd, message, wParam, lParam); + } + if (message >= WM_KEYFIRST && message <= WM_KEYLAST) { + WindowDestructionReceiver destruction_receiver(this); + // Returning immediately anyway - no need to check + // destruction_receiver.IsWindowDestroyed() afterwards. + return HandleKeyboard(message, wParam, lParam, destruction_receiver) + ? 0 + : DefWindowProc(hWnd, message, wParam, lParam); + } + + switch (message) { + case WM_CLOSE: + // In case the Windows window was somehow forcibly destroyed without + // WM_CLOSE. + case WM_DESTROY: { + if (cursor_auto_hide_timer_) { + DeleteTimerQueueTimer(nullptr, cursor_auto_hide_timer_, nullptr); + cursor_auto_hide_timer_ = nullptr; + } + { + WindowDestructionReceiver destruction_receiver(this); + OnBeforeClose(destruction_receiver); + if (destruction_receiver.IsWindowDestroyed()) { + break; + } + } + // Set hwnd_ to null to ignore events from now on since this Win32Window + // is entering an indeterminate state - this should be done at some point + // in closing anyway. + hwnd_ = nullptr; + SetWindowLongPtr(hWnd, GWLP_USERDATA, 0); + if (message != WM_DESTROY) { + DestroyWindow(hWnd); + } + OnAfterClose(); + } break; + + case WM_DROPFILES: { + HDROP drop_handle = reinterpret_cast(wParam); + auto drop_count = DragQueryFileW(drop_handle, 0xFFFFFFFFu, nullptr, 0); + if (drop_count > 0) { + // Get required buffer size + UINT path_size = DragQueryFileW(drop_handle, 0, nullptr, 0); + if (path_size > 0 && path_size < 0xFFFFFFFFu) { + std::u16string path; + ++path_size; // Ensure space for the null terminator + path.resize(path_size); // Reserve space + // Only getting first file dropped (other files ignored) + path_size = + DragQueryFileW(drop_handle, 0, (LPWSTR)&path[0], path_size); + if (path_size > 0) { + path.resize(path_size); // Will drop the null terminator + FileDropEvent e(this, xe::to_path(path)); + WindowDestructionReceiver destruction_receiver(this); + OnFileDrop(e, destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + DragFinish(drop_handle); + break; + } + } + } + } + DragFinish(drop_handle); + } break; + + case WM_MOVE: { + OnMonitorUpdate(MonitorUpdateEvent(this, false)); + } break; + + case WM_SIZE: { + if (batched_size_update_depth_) { + batched_size_update_contained_wm_size_ = true; + } else { + WindowDestructionReceiver destruction_receiver(this); + HandleSizeUpdate(destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } + } break; + + case WM_PAINT: { + if (batched_size_update_depth_) { + // Avoid painting an outdated surface during a batched size update when + // WM_SIZE handling is deferred. + batched_size_update_contained_wm_paint_ = true; + } else { + ValidateRect(hwnd_, nullptr); + OnPaint(); + } + // Custom painting via OnPaint - don't pass to DefWindowProc. + return 0; + } break; + + case WM_ERASEBKGND: { + if (HasSurface()) { + // Don't erase between paints because painting may be dropped if nothing + // has changed since the last one. + return 0; + } + } break; + + case WM_DISPLAYCHANGE: { + OnMonitorUpdate(MonitorUpdateEvent(this, true)); + } break; + + case WM_DPICHANGED: { + // Note that for some reason, WM_DPICHANGED is not sent when the window is + // borderless fullscreen with per-monitor DPI awareness v1. + + dpi_ = GetCurrentDpi(); + + WindowDestructionReceiver destruction_receiver(this); + + { + UISetupEvent e(this); + OnDpiChanged(e, destruction_receiver); + // The window might have been closed by the handler, check hwnd_ too + // since it's needed below. + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } + + auto rect = reinterpret_cast(lParam); + if (rect) { + // SetWindowPos arguments according to WM_DPICHANGED MSDN documentation. + // https://docs.microsoft.com/en-us/windows/win32/hidpi/wm-dpichanged + // There's no need to handle the maximized state any special way (by + // updating the window placement instead of the window position in this + // case, for instance), as Windows (by design) restores the window when + // changing the DPI to a new one. + SetWindowPos(hwnd_, nullptr, int(rect->left), int(rect->top), + int(rect->right - rect->left), + int(rect->bottom - rect->top), + SWP_NOZORDER | SWP_NOACTIVATE); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } + } break; + + case WM_KILLFOCUS: { + WindowDestructionReceiver destruction_receiver(this); + OnFocusUpdate(false, destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } break; + + case WM_SETFOCUS: { + WindowDestructionReceiver destruction_receiver(this); + OnFocusUpdate(true, destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + break; + } + } break; + + case WM_SETCURSOR: { + if (reinterpret_cast(wParam) == hwnd_ && HasFocus() && + LOWORD(lParam) == HTCLIENT) { + switch (GetCursorVisibility()) { + case CursorVisibility::kAutoHidden: { + // Always revealing the cursor in case of events like clicking, but + // WM_MOUSEMOVE messages may be sent for reasons not always + // involving actual mouse movement performed by the user. Revealing + // the cursor in case of movement is done in HandleMouse instead. + if (HIWORD(lParam) != WM_MOUSEMOVE) { + cursor_currently_auto_hidden_ = false; + SetCursorAutoHideTimer(); + } + if (cursor_currently_auto_hidden_) { + SetCursor(nullptr); + return TRUE; + } + } break; + case CursorVisibility::kHidden: + SetCursor(nullptr); + return TRUE; + default: + break; + } + } + // For the non-client area, and for visible cursor, letting normal + // processing happen, setting the cursor to an arrow or to something + // specific to non-client parts of the window. + } break; + + case kUserMessageAutoHideCursor: { + // Recheck the cursor visibility - the callback might have been called + // before or while the timer is deleted. Also ignore messages from + // outdated mouse interactions. + if (GetCursorVisibility() == CursorVisibility::kAutoHidden && + wParam == last_cursor_auto_hide_queued) { + // The timer object is not needed anymore. + if (cursor_auto_hide_timer_) { + DeleteTimerQueueTimer(nullptr, cursor_auto_hide_timer_, nullptr); + cursor_auto_hide_timer_ = nullptr; + } + cursor_currently_auto_hidden_ = true; + SetCursorIfFocusedOnClientArea(nullptr); + } + return 0; + } break; + + case WM_TABLET_QUERYSYSTEMGESTURESTATUS: + return + // disables press and hold (right-click) gesture + TABLET_DISABLE_PRESSANDHOLD | + // disables UI feedback on pen up (waves) + TABLET_DISABLE_PENTAPFEEDBACK | + // disables UI feedback on pen button down (circle) + TABLET_DISABLE_PENBARRELFEEDBACK | + // disables pen flicks (back, forward, drag down, drag up) + TABLET_DISABLE_FLICKS | TABLET_DISABLE_TOUCHSWITCH | + TABLET_DISABLE_SMOOTHSCROLLING | TABLET_DISABLE_TOUCHUIFORCEON | + TABLET_ENABLE_MULTITOUCHDATA; + + case WM_MENUCOMMAND: { + MENUINFO menu_info = {0}; + menu_info.cbSize = sizeof(menu_info); + menu_info.fMask = MIM_MENUDATA; + GetMenuInfo(HMENU(lParam), &menu_info); + auto parent_item = reinterpret_cast(menu_info.dwMenuData); + auto child_item = + reinterpret_cast(parent_item->child(wParam)); + assert_not_null(child_item); + WindowDestructionReceiver destruction_receiver(this); + child_item->OnSelected(); + if (destruction_receiver.IsWindowDestroyed()) { + break; + } + // The menu item might have been destroyed by its OnSelected, don't do + // anything with it here from now on. + } break; + } + + // The window might have been destroyed by the handlers, don't interact with + // *this in this function from now on. + + // Passing the original hWnd argument rather than hwnd_ as the window might + // have been closed or destroyed by a handler, making hwnd_ null even though + // DefWindowProc still needs to be called to propagate the closing-related + // messages needed by Windows, or inaccessible (due to use-after-free) at all. + return DefWindowProc(hWnd, message, wParam, lParam); +} + +LRESULT CALLBACK Win32Window::WndProcThunk(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam) { + if (hWnd) { + Win32Window* window = nullptr; + if (message == WM_NCCREATE) { + auto create_struct = reinterpret_cast(lParam); + window = reinterpret_cast(create_struct->lpCreateParams); + SetWindowLongPtr(hWnd, GWLP_USERDATA, (__int3264)(LONG_PTR)window); + // Don't miss any messages (as they may have effect on the actual state + // stored in Win32Window) sent before the completion of CreateWindowExW + // dropped because `window->hwnd_ != hWnd`, when the result of + // CreateWindowExW still hasn't been assigned to `hwnd_` (however, don't + // reattach this window to a closed window if WM_NCCREATE was somehow sent + // to a window being closed). + if (window->phase() == Phase::kOpening) { + assert_true(!window->hwnd_ || window->hwnd_ == hWnd); + window->hwnd_ = hWnd; + } + // Enable non-client area DPI scaling for AdjustWindowRectExForDpi to work + // correctly between Windows 10 1607 (when AdjustWindowRectExForDpi and + // EnableNonClientDpiScaling were added) and 1703 (when per-monitor + // awareness version 2 was added with automatically enabled non-client + // area DPI scaling). + const Win32WindowedAppContext& win32_app_context = + static_cast(window->app_context()); + const Win32WindowedAppContext::PerMonitorDpiV2Api* + per_monitor_dpi_v2_api = win32_app_context.per_monitor_dpi_v2_api(); + if (per_monitor_dpi_v2_api) { + per_monitor_dpi_v2_api->enable_non_client_dpi_scaling(hWnd); + } + // Already fully handled, no need to call Win32Window::WndProc. + } else { + window = + reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); + if (window && window->hwnd_ == hWnd) { + return window->WndProc(hWnd, message, wParam, lParam); + } + } + } + return DefWindowProc(hWnd, message, wParam, lParam); +} + std::unique_ptr MenuItem::Create(Type type, const std::string& text, const std::string& hotkey, @@ -772,20 +1284,12 @@ Win32MenuItem::~Win32MenuItem() { } } -void Win32MenuItem::EnableMenuItem(Window& window) { - int i = 0; - for (auto iter = children_.begin(); iter != children_.end(); ++iter, i++) { - ::EnableMenuItem(handle_, i, MF_BYPOSITION | MF_ENABLED); +void Win32MenuItem::SetEnabled(bool enabled) { + UINT enable_flags = MF_BYPOSITION | (enabled ? MF_ENABLED : MF_GRAYED); + UINT i = 0; + for (auto iter = children_.begin(); iter != children_.end(); ++iter, ++i) { + EnableMenuItem(handle_, i, enable_flags); } - DrawMenuBar((HWND)window.native_handle()); -} - -void Win32MenuItem::DisableMenuItem(Window& window) { - int i = 0; - for (auto iter = children_.begin(); iter != children_.end(); ++iter, i++) { - ::EnableMenuItem(handle_, i, MF_BYPOSITION | MF_GRAYED); - } - DrawMenuBar((HWND)window.native_handle()); } void Win32MenuItem::OnChildAdded(MenuItem* generic_child_item) { diff --git a/src/xenia/ui/window_win.h b/src/xenia/ui/window_win.h index f4dceaeac..dd5d8b978 100644 --- a/src/xenia/ui/window_win.h +++ b/src/xenia/ui/window_win.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -13,10 +13,15 @@ #include #include -#include "xenia/base/platform_win.h" #include "xenia/ui/menu_item.h" #include "xenia/ui/window.h" +// Must be included before Windows headers for things like NOMINMAX. +#include "xenia/base/platform_win.h" + +#include +#include + namespace xe { namespace ui { @@ -24,73 +29,127 @@ class Win32Window : public Window { using super = Window; public: - Win32Window(WindowedAppContext& app_context, const std::string& title); + Win32Window(WindowedAppContext& app_context, const std::string_view title, + uint32_t desired_logical_width, uint32_t desired_logical_height); ~Win32Window() override; - NativePlatformHandle native_platform_handle() const override; - NativeWindowHandle native_handle() const override { return hwnd_; } + // Will be null if the window hasn't been successfully opened yet, or has been + // closed. HWND hwnd() const { return hwnd_; } - void EnableMainMenu() override; - void DisableMainMenu() override; - - bool set_title(const std::string_view title) override; - - bool SetIcon(const void* buffer, size_t size) override; - - bool CaptureMouse() override; - bool ReleaseMouse() override; - - bool is_fullscreen() const override; - void ToggleFullscreen(bool fullscreen) override; - - bool is_bordered() const override; - void set_bordered(bool enabled) override; - - int get_dpi() const override; - - void set_cursor_visible(bool value) override; - void set_focus(bool value) override; - - void Resize(int32_t width, int32_t height) override; - void Resize(int32_t left, int32_t top, int32_t right, - int32_t bottom) override; - - // (raw) Resize the window, no DPI scaling applied. - void RawReposition(const RECT& rc); - - bool Initialize() override; - void Invalidate() override; - void Close() override; + uint32_t GetMediumDpi() const override; protected: - bool OnCreate() override; - void OnMainMenuChange() override; - void OnDestroy() override; - void OnClose() override; + bool OpenImpl() override; + void RequestCloseImpl() override; - void OnResize(UIEvent* e) override; + uint32_t GetLatestDpiImpl() const override; + + void ApplyNewFullscreen() override; + void ApplyNewTitle() override; + void LoadAndApplyIcon(const void* buffer, size_t size, + bool can_apply_state_in_current_phase) override; + void ApplyNewMainMenu(MenuItem* old_main_menu) override; + void CompleteMainMenuItemsUpdateImpl() override; + void ApplyNewMouseCapture() override; + void ApplyNewMouseRelease() override; + void ApplyNewCursorVisibility( + CursorVisibility old_cursor_visibility) override; + void FocusImpl() override; + + std::unique_ptr CreateSurfaceImpl( + Surface::TypeFlags allowed_types) override; + void RequestPaintImpl() override; + + private: + enum : UINT { + kUserMessageAutoHideCursor = WM_USER, + }; + + BOOL AdjustWindowRectangle(RECT& rect, DWORD style, BOOL menu, DWORD ex_style, + UINT dpi) const; + BOOL AdjustWindowRectangle(RECT& rect) const; + + uint32_t GetCurrentSystemDpi() const; + uint32_t GetCurrentDpi() const; + + void ApplyFullscreenEntry(WindowDestructionReceiver& destruction_receiver); + + void HandleSizeUpdate(WindowDestructionReceiver& destruction_receiver); + // For updating multiple factors that may influence the window size at once, + // without handling WM_SIZE multiple times (that may not only result in wasted + // handling, but also in the state potentially changed to an inconsistent one + // in the middle of a size update by the listeners). + void BeginBatchedSizeUpdate(); + void EndBatchedSizeUpdate(WindowDestructionReceiver& destruction_receiver); + + bool HandleMouse(UINT message, WPARAM wParam, LPARAM lParam, + WindowDestructionReceiver& destruction_receiver); + bool HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam, + WindowDestructionReceiver& destruction_receiver); + + void SetCursorIfFocusedOnClientArea(HCURSOR cursor) const; + void SetCursorAutoHideTimer(); + static void NTAPI AutoHideCursorTimerCallback(void* parameter, + BOOLEAN timer_or_wait_fired); static LRESULT CALLBACK WndProcThunk(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + // This can't handle messages sent during CreateWindow (hwnd_ still not + // assigned to) or after nulling hwnd_ in closing / deleting. virtual LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - private: - void EnableMMCSS(); - bool HandleMouse(UINT message, WPARAM wParam, LPARAM lParam); - bool HandleKeyboard(UINT message, WPARAM wParam, LPARAM lParam); - - HWND hwnd_ = nullptr; - HICON icon_ = nullptr; - bool closing_ = false; - bool fullscreen_ = false; HCURSOR arrow_cursor_ = nullptr; - WINDOWPLACEMENT windowed_pos_ = {0}; - POINT last_mouse_pos_ = {0}; + HICON icon_ = nullptr; - void* GetDpiForMonitor_ = nullptr; + uint32_t dpi_ = USER_DEFAULT_SCREEN_DPI; + + // hwnd_ may be accessed by the cursor hiding timer callback from a separate + // thread, but the timer can be active only with a valid window anyway. + HWND hwnd_ = nullptr; + + uint32_t batched_size_update_depth_ = 0; + bool batched_size_update_contained_wm_size_ = false; + bool batched_size_update_contained_wm_paint_ = false; + + uint32_t pre_fullscreen_dpi_; + WINDOWPLACEMENT pre_fullscreen_placement_; + // The client area part of pre_fullscreen_placement_.rcNormalPosition, saved + // in case something effecting the behavior of AdjustWindowRectEx for the + // non-fullscreen state is changed mid-fullscreen (for instance, the menu is + // toggled), so a new AdjustWindowRectExForDpi call for the old DPI, but with + // the other state different than the old one, while exiting fullscreen, won't + // cause anomalies like negative size. + uint32_t pre_fullscreen_normal_client_width_; + uint32_t pre_fullscreen_normal_client_height_; + + // Must be the screen position, not the client position, so it's possible to + // immediately hide the cursor, for instance, when switching to fullscreen + // (and thus changing the client area top-left corner, resulting in + // WM_MOUSEMOVE being sent, which would instantly reveal the cursor because of + // that relative position change). + POINT cursor_auto_hide_last_screen_pos_ = {LONG_MAX, LONG_MAX}; + // Using a timer queue timer for hiding the cursor rather than WM_TIMER + // because the latter is a very low-priority message which is never received + // if WM_PAINT messages are sent continuously (invalidating the window right + // after painting). + HANDLE cursor_auto_hide_timer_ = nullptr; + // Last hiding case numbers for skipping of obsolete cursor hiding messages + // (both WM_MOUSEMOVE and the hiding message have been sent, for instance, and + // WM_MOUSEMOVE hasn't been handled yet, or the cursor visibility state has + // been changed). The queued index is read, and the signaled index is written, + // by the timer callback, which is executed outside the message thread, so + // before modifying the queued number, or reading the signaled number, in the + // message thread, delete the timer (deleting the timer awaits cancels the + // callback if it hasn't been invoked yet, or awaits it if it has). Use + // equality comparison for safe rollover handling. + WPARAM last_cursor_auto_hide_queued = 0; + WPARAM last_cursor_auto_hide_signaled = 0; + // Whether the cursor has been hidden after the expiration of the timer, and + // hasn't been revealed yet. + bool cursor_currently_auto_hidden_ = false; }; class Win32MenuItem : public MenuItem { @@ -99,10 +158,9 @@ class Win32MenuItem : public MenuItem { std::function callback); ~Win32MenuItem() override; - HMENU handle() { return handle_; } + HMENU handle() const { return handle_; } - void EnableMenuItem(Window& window) override; - void DisableMenuItem(Window& window) override; + void SetEnabled(bool enabled) override; using MenuItem::OnSelected; diff --git a/src/xenia/ui/windowed_app_context_android.h b/src/xenia/ui/windowed_app_context_android.h index 91cd10427..bc7583109 100644 --- a/src/xenia/ui/windowed_app_context_android.h +++ b/src/xenia/ui/windowed_app_context_android.h @@ -36,7 +36,8 @@ class AndroidWindowedAppContext final : public WindowedAppContext { // The single Window instance that will be receiving window callbacks. // Multiple windows cannot be created as one activity or fragment can have // only one layout. This window acts purely as a proxy between the activity - // and the Xenia logic. + // and the Xenia logic. May be done during Window destruction, so must not + // interact with it. AndroidWindow* GetActivityWindow() const { return activity_window_; } void SetActivityWindow(AndroidWindow* window) { activity_window_ = window; } diff --git a/src/xenia/ui/windowed_app_context_win.cc b/src/xenia/ui/windowed_app_context_win.cc index 399a2afd4..f9371304a 100644 --- a/src/xenia/ui/windowed_app_context_win.cc +++ b/src/xenia/ui/windowed_app_context_win.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -22,10 +22,43 @@ Win32WindowedAppContext::~Win32WindowedAppContext() { if (pending_functions_hwnd_) { DestroyWindow(pending_functions_hwnd_); } + if (shcore_module_) { + FreeLibrary(shcore_module_); + } } bool Win32WindowedAppContext::Initialize() { // Logging possibly not initialized in this function yet. + + // Obtain function pointers that may be used for windows if available. + shcore_module_ = LoadLibraryW(L"SHCore.dll"); + if (shcore_module_) { + per_monitor_dpi_v1_api_available_ = true; + per_monitor_dpi_v1_api_available_ &= + (*reinterpret_cast( + &per_monitor_dpi_v1_api_.get_dpi_for_monitor) = + GetProcAddress(shcore_module_, "GetDpiForMonitor")) != nullptr; + per_monitor_dpi_v2_api_available_ = true; + per_monitor_dpi_v2_api_available_ &= + (*reinterpret_cast( + &per_monitor_dpi_v2_api_.adjust_window_rect_ex_for_dpi) = + GetProcAddress(shcore_module_, "AdjustWindowRectExForDpi")) != + nullptr; + per_monitor_dpi_v2_api_available_ &= + (*reinterpret_cast( + &per_monitor_dpi_v2_api_.enable_non_client_dpi_scaling) = + GetProcAddress(shcore_module_, "EnableNonClientDpiScaling")) != + nullptr; + per_monitor_dpi_v2_api_available_ &= + (*reinterpret_cast( + &per_monitor_dpi_v2_api_.get_dpi_for_system) = + GetProcAddress(shcore_module_, "GetDpiForSystem")) != nullptr; + per_monitor_dpi_v2_api_available_ &= + (*reinterpret_cast( + &per_monitor_dpi_v2_api_.get_dpi_for_window) = + GetProcAddress(shcore_module_, "GetDpiForWindow")) != nullptr; + } + // Create the message-only window for executing pending functions - using a // window instead of executing them between iterations so non-main message // loops, such as Windows modals, can execute pending functions too. @@ -51,6 +84,7 @@ bool Win32WindowedAppContext::Initialize() { if (!pending_functions_hwnd_) { return false; } + return true; } @@ -117,7 +151,7 @@ LRESULT CALLBACK Win32WindowedAppContext::PendingFunctionsWndProc( break; case kPendingFunctionsWindowClassMessageExecute: app_context->ExecutePendingFunctionsFromUIThread(); - break; + return 0; default: break; } diff --git a/src/xenia/ui/windowed_app_context_win.h b/src/xenia/ui/windowed_app_context_win.h index 1c01961ac..08e70e662 100644 --- a/src/xenia/ui/windowed_app_context_win.h +++ b/src/xenia/ui/windowed_app_context_win.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -15,11 +15,42 @@ #include "xenia/base/platform_win.h" #include "xenia/ui/windowed_app_context.h" +// For per-monitor DPI awareness v1. +#include + namespace xe { namespace ui { class Win32WindowedAppContext final : public WindowedAppContext { public: + // clang-format off + struct PerMonitorDpiV1Api { + HRESULT (STDAPICALLTYPE* get_dpi_for_monitor)( + HMONITOR hmonitor, MONITOR_DPI_TYPE dpi_type, UINT* dpi_x, UINT* dpi_y); + }; + struct PerMonitorDpiV2Api { + // Important note: Even though per-monitor DPI awareness version 2 + // inherently has automatic non-client area DPI scaling, PMv2 was added in + // Windows 10 1703. However, these functions were added earlier, in 1607. + // While we haven't tested the actual behavior on 1607 and this is just a + // guess, still, before using AdjustWindowRectExForDpi for the window DPI, + // make sure that EnableNonClientDpiScaling is called for the window in its + // WM_NCCREATE handler to enable automatic non-client DPI scaling on PMv1 on + // 1607, so AdjustWindowRectExForDpi (which doesn't have a window argument + // so it can't know whether automatic scaling is enabled for it) will likely + // return values that reflect the actual size of the non-client area of the + // window (otherwise, for instance, "un-adjusting" the actual window + // rectangle using AdjustWindowRectExForDpi may result in a negative client + // area size if the DPI passed to it is larger than the DPI actually used + // for the non-client area). + BOOL (WINAPI* adjust_window_rect_ex_for_dpi)( + LPRECT rect, DWORD style, BOOL menu, DWORD ex_style, UINT dpi); + BOOL (WINAPI* enable_non_client_dpi_scaling)(HWND hwnd); + UINT (WINAPI* get_dpi_for_system)(); + UINT (WINAPI* get_dpi_for_window)(HWND hwnd); + }; + // clang-format on + // Must call Initialize and check its result after creating to be able to // perform pending function calls. explicit Win32WindowedAppContext(HINSTANCE hinstance, int show_cmd) @@ -37,6 +68,23 @@ class Win32WindowedAppContext final : public WindowedAppContext { int RunMainMessageLoop(); + // Per-monitor DPI awareness version 2 is expected to be enabled via the + // manifest, as that's the recommended way, which also doesn't require calling + // SetProcessDpiAwareness before doing anything that may depend on DPI + // awareness (so it's safe to use any Windows APIs before what would be + // supposed to initialize it). + // Windows 8.1 per-monitor DPI awareness version 1. + const PerMonitorDpiV1Api* per_monitor_dpi_v1_api() const { + return per_monitor_dpi_v1_api_available_ ? &per_monitor_dpi_v1_api_ + : nullptr; + } + // Windows 10 1607 per-monitor DPI awareness API, also heavily used for + // per-monitor DPI awareness version 2 functionality added in Windows 10 1703. + const PerMonitorDpiV2Api* per_monitor_dpi_v2_api() const { + return per_monitor_dpi_v2_api_available_ ? &per_monitor_dpi_v2_api_ + : nullptr; + } + private: enum : UINT { kPendingFunctionsWindowClassMessageExecute = WM_USER, @@ -47,9 +95,15 @@ class Win32WindowedAppContext final : public WindowedAppContext { HINSTANCE hinstance_; int show_cmd_; - HWND pending_functions_hwnd_ = nullptr; + + HMODULE shcore_module_ = nullptr; + PerMonitorDpiV1Api per_monitor_dpi_v1_api_ = {}; + PerMonitorDpiV2Api per_monitor_dpi_v2_api_ = {}; + bool per_monitor_dpi_v1_api_available_ = false; + bool per_monitor_dpi_v2_api_available_ = false; static bool pending_functions_window_class_registered_; + HWND pending_functions_hwnd_ = nullptr; }; } // namespace ui diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index d27d37661..574501788 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -312,7 +312,8 @@ struct X_OBJECT_ATTRIBUTES { // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx typedef struct { - xe::be exception_code; + // Renamed due to a collision with exception_code from Windows excpt.h. + xe::be code; xe::be exception_flags; xe::be exception_record; xe::be exception_address; diff --git a/third_party/.clang-format b/third_party/.clang-format new file mode 100644 index 000000000..9d159247d --- /dev/null +++ b/third_party/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false diff --git a/third_party/FidelityFX-CAS b/third_party/FidelityFX-CAS new file mode 160000 index 000000000..9fabcc9a2 --- /dev/null +++ b/third_party/FidelityFX-CAS @@ -0,0 +1 @@ +Subproject commit 9fabcc9a2c45f958aff55ddfda337e74ef894b7f diff --git a/third_party/FidelityFX-FSR b/third_party/FidelityFX-FSR new file mode 160000 index 000000000..a21ffb8f6 --- /dev/null +++ b/third_party/FidelityFX-FSR @@ -0,0 +1 @@ +Subproject commit a21ffb8f6c13233ba336352bdff293894c706575 diff --git a/third_party/fxaa/FXAA3_11.h b/third_party/fxaa/FXAA3_11.h new file mode 100644 index 000000000..993966d10 --- /dev/null +++ b/third_party/fxaa/FXAA3_11.h @@ -0,0 +1,2065 @@ +//---------------------------------------------------------------------------------- +// File: es3-kepler\FXAA/FXAA3_11.h +// SDK Version: v2.11 +// Email: gameworks@nvidia.com +// Site: http://developer.nvidia.com/ +// +// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//---------------------------------------------------------------------------------- +/*============================================================================ + + + NVIDIA FXAA 3.11 by TIMOTHY LOTTES + +------------------------------------------------------------------------------ + INTEGRATION CHECKLIST +------------------------------------------------------------------------------ +(1.) +In the shader source, setup defines for the desired configuration. +When providing multiple shaders (for different presets), +simply setup the defines differently in multiple files. +Example, + + #define FXAA_PC 1 + #define FXAA_HLSL_5 1 + #define FXAA_QUALITY__PRESET 12 + +Or, + + #define FXAA_360 1 + +Or, + + #define FXAA_PS3 1 + +Etc. + +(2.) +Then include this file, + + #include "Fxaa3_11.h" + +(3.) +Then call the FXAA pixel shader from within your desired shader. +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. +As for FXAA 3.11 all inputs for all shaders are the same +to enable easy porting between platforms. + + return FxaaPixelShader(...); + +(4.) +Insure pass prior to FXAA outputs RGBL (see next section). +Or use, + + #define FXAA_GREEN_AS_LUMA 1 + +(5.) +Setup engine to provide the following constants +which are used in the FxaaPixelShader() inputs, + + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + +(6.) +Have FXAA vertex shader run as a full screen triangle, +and output "pos" and "fxaaConsolePosPos" +such that inputs in the pixel shader provide, + + // {xy} = center of pixel + FxaaFloat2 pos, + + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + +(7.) +Insure the texture sampler(s) used by FXAA are set to bilinear filtering. + + +------------------------------------------------------------------------------ + INTEGRATION - RGBL AND COLORSPACE +------------------------------------------------------------------------------ +FXAA3 requires RGBL as input unless the following is set, + + #define FXAA_GREEN_AS_LUMA 1 + +In which case the engine uses green in place of luma, +and requires RGB input is in a non-linear colorspace. + +RGB should be LDR (low dynamic range). +Specifically do FXAA after tonemapping. + +RGB data as returned by a texture fetch can be non-linear, +or linear when FXAA_GREEN_AS_LUMA is not set. +Note an "sRGB format" texture counts as linear, +because the result of a texture fetch is linear data. +Regular "RGBA8" textures in the sRGB colorspace are non-linear. + +If FXAA_GREEN_AS_LUMA is not set, +luma must be stored in the alpha channel prior to running FXAA. +This luma should be in a perceptual space (could be gamma 2.0). +Example pass before FXAA where output is gamma 2.0 encoded, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma + return color; + +Another example where output is linear encoded, +say for instance writing to an sRGB formated render target, +where the render target does the conversion back to sRGB after blending, + + color.rgb = ToneMap(color.rgb); // linear color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma + return color; + +Getting luma correct is required for the algorithm to work correctly. + + +------------------------------------------------------------------------------ + BEING LINEARLY CORRECT? +------------------------------------------------------------------------------ +Applying FXAA to a framebuffer with linear RGB color will look worse. +This is very counter intuitive, but happends to be true in this case. +The reason is because dithering artifacts will be more visiable +in a linear colorspace. + + +------------------------------------------------------------------------------ + COMPLEX INTEGRATION +------------------------------------------------------------------------------ +Q. What if the engine is blending into RGB before wanting to run FXAA? + +A. In the last opaque pass prior to FXAA, + have the pass write out luma into alpha. + Then blend into RGB only. + FXAA should be able to run ok + assuming the blending pass did not any add aliasing. + This should be the common case for particles and common blending passes. + +A. Or use FXAA_GREEN_AS_LUMA. + +============================================================================*/ + +/*============================================================================ + + INTEGRATION KNOBS + +============================================================================*/ +// +// FXAA_PS3 and FXAA_360 choose the console algorithm (FXAA3 CONSOLE). +// FXAA_360_OPT is a prototype for the new optimized 360 version. +// +// 1 = Use API. +// 0 = Don't use API. +// +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PS3 + #define FXAA_PS3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360 + #define FXAA_360 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360_OPT + #define FXAA_360_OPT 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_PC + // + // FXAA Quality + // The high quality PC algorithm. + // + #define FXAA_PC 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PC_CONSOLE + // + // The console algorithm for PC is included + // for developers targeting really low spec machines. + // Likely better to just run FXAA_PC, and use a really low preset. + // + #define FXAA_PC_CONSOLE 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + +/*============================================================================ + FXAA CONSOLE PS3 - TUNING KNOBS +============================================================================*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_SHARPNESS + // + // Consoles the sharpness of edges on PS3 only. + // Non-PS3 tuning is done with shader input. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 8.0 is sharper + // 4.0 is softer + // 2.0 is really soft (good for vector graphics inputs) + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 8.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 4.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 2.0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_THRESHOLD + // + // Only effects PS3. + // Non-PS3 tuning is done with shader input. + // + // The minimum amount of local contrast required to apply algorithm. + // The console setting has a different mapping than the quality setting. + // + // This only applies when FXAA_EARLY_EXIT is 1. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 0.25 and 0.125. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 0.125 leaves less aliasing, but is softer + // 0.25 leaves more aliasing, and is sharper + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.125 + #else + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.25 + #endif +#endif + +/*============================================================================ + FXAA QUALITY - TUNING KNOBS +------------------------------------------------------------------------------ +NOTE the other tuning knobs are now in the shader function inputs! +============================================================================*/ +#ifndef FXAA_QUALITY__PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY__PRESET 12 +#endif + + +/*============================================================================ + + FXAA QUALITY - PRESETS + +============================================================================*/ + +/*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 +#endif + +/*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + +/*============================================================================ + FXAA QUALITY - EXTREME QUALITY +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + + + +/*============================================================================ + + API PORTING + +============================================================================*/ +#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1) + #define FxaaInt2 float2 + #define FxaaTex sampler2D + #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) + #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_4 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) +#endif + + +/*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION +============================================================================*/ +#if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + + + + +/*============================================================================ + + FXAA3 QUALITY - PC + +============================================================================*/ +#if (FXAA_PC == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex fxaaConsole360TexExpBiasNegOne, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex fxaaConsole360TexExpBiasNegTwo, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat fxaaConsoleEdgeSharpness, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat fxaaConsoleEdgeThreshold, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaConsoleEdgeThresholdMin, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} +/*==========================================================================*/ +#endif + + + + +/*============================================================================ + + FXAA3 CONSOLE - PC VERSION + +------------------------------------------------------------------------------ +Instead of using this on PC, I'd suggest just using FXAA Quality with + #define FXAA_QUALITY__PRESET 10 +Or + #define FXAA_QUALITY__PRESET 20 +Either are higher qualilty and almost as fast as this on modern PC GPUs. +============================================================================*/ +#if (FXAA_PC_CONSOLE == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); + FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); + FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); + FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat lumaM = rgbyM.w; + #else + FxaaFloat lumaM = rgbyM.y; + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); + lumaNe += 1.0/384.0; + FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); + FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); + FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMinM = min(lumaMin, lumaM); + FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); + FxaaFloat lumaMaxM = max(lumaMax, lumaM); + FxaaFloat dirSwMinusNe = lumaSw - lumaNe; + FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; + FxaaFloat dirSeMinusNw = lumaSe - lumaNw; + if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir; + dir.x = dirSwMinusNe + dirSeMinusNw; + dir.y = dirSwMinusNe - dirSeMinusNw; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir1 = normalize(dir.xy); + FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); + FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; + FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw); + FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; + FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); +/*--------------------------------------------------------------------------*/ + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax); + #else + FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax); + #endif + if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5; + return rgbyB; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - 360 PIXEL SHADER + +------------------------------------------------------------------------------ +This optimized version thanks to suggestions from Andy Luedke. +Should be fully tex bound in all cases. +As of the FXAA 3.11 release, I have still not tested this code, +however I fixed a bug which was in both FXAA 3.9 and FXAA 3.10. +And note this is replacing the old unoptimized version. +If it does not work, please let me know so I can fix it. +============================================================================*/ +#if (FXAA_360 == 1) +/*--------------------------------------------------------------------------*/ +[reduceTempRegUsage(4)] +float4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + float4 lumaNwNeSwSe; + #if (FXAA_GREEN_AS_LUMA == 0) + asm { + tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #else + asm { + tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #endif +/*--------------------------------------------------------------------------*/ + lumaNwNeSwSe.y += 1.0/384.0; + float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y); + float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y); +/*--------------------------------------------------------------------------*/ + float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + float lumaMinM = min(lumaMin, rgbyM.w); + float lumaMaxM = max(lumaMax, rgbyM.w); + #else + float lumaMinM = min(lumaMin, rgbyM.y); + float lumaMaxM = max(lumaMax, rgbyM.y); + #endif + if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM; +/*--------------------------------------------------------------------------*/ + float2 dir; + dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx); + dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy); + dir = normalize(dir); +/*--------------------------------------------------------------------------*/ + float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw; +/*--------------------------------------------------------------------------*/ + float4 dir2; + float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness; + dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5); + dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw; +/*--------------------------------------------------------------------------*/ + float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0)); + float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0)); + float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0)); + float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ + float4 rgbyA = rgbyN1 + rgbyP1; + float4 rgbyB = rgbyN2 + rgbyP2 + rgbyA * 0.5; +/*--------------------------------------------------------------------------*/ + float4 rgbyR = ((FxaaLuma(rgbyB) - lumaMax) > 0.0) ? rgbyA : rgbyB; + rgbyR = ((FxaaLuma(rgbyB) - lumaMin) > 0.0) ? rgbyR : rgbyA; + return rgbyR; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (NO EARLY EXIT) + +============================================================================== +The code below does not exactly match the assembly. +I have a feeling that 12 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.z(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h0.w(TRUE), v5.xwxx, #0 + 6: addh h0.z(TRUE), -h2, h0.w + 7: texpkb h1.w(TRUE), v5, #0 + 9: addh h0.x(TRUE), h0.z, -h1.w + 10: addh h3.w(TRUE), h0.z, h1 + 11: texpkb h2.w(TRUE), v5.zwzz, #0 + 13: addh h0.z(TRUE), h3.w, -h2.w + 14: addh h0.x(TRUE), h2.w, h0 + 15: nrmh h1.xz(TRUE), h0_n + 16: minh_m8 h0.x(TRUE), |h1|, |h1.z| + 17: maxh h4.w(TRUE), h0, h1 + 18: divx h2.xy(TRUE), h1_n.xzzw, h0_n + 19: movr r1.zw(TRUE), v4.xxxy + 20: madr r2.xz(TRUE), -h1, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zzww, r1.zzww + 22: minh h5.w(TRUE), h0, h1 + 23: texpkb h0(TRUE), r2.xzxx, #0 + 25: madr r0.zw(TRUE), h1.xzxz, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w), r1 + 27: maxh h4.x(TRUE), h2.z, h2.w + 28: texpkb h1(TRUE), r0.zwzz, #0 + 30: addh_d2 h1(TRUE), h0, h1 + 31: madr r0.xy(TRUE), -h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 33: texpkb h0(TRUE), r0, #0 + 35: minh h4.z(TRUE), h2, h2.w + 36: fenct TRUE + 37: madr r1.xy(TRUE), h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 39: texpkb h2(TRUE), r1, #0 + 41: addh_d2 h0(TRUE), h0, h2 + 42: maxh h2.w(TRUE), h4, h4.x + 43: minh h2.x(TRUE), h5.w, h4.z + 44: addh_d2 h0(TRUE), h0, h1 + 45: slth h2.x(TRUE), h0.w, h2 + 46: sgth h2.w(TRUE), h0, h2 + 47: movh h0(TRUE), h0 + 48: addx.c0 rc(TRUE), h2, h2.w + 49: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB1 | add | 2: ADDh h2.z, h0.--w-, const.--x-; + | | | + 2 | SCT0/1 | mov | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB1 | add | 6: ADDh h0.z,-h2, h0.--w-; + | | | + 3 | SCT0/1 | mov | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 9: ADDh h0.x, h0.z---,-h1.w---; + | SCB1 | add | 10: ADDh h3.w, h0.---z, h1; + | | | + 4 | SCT0/1 | mov | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h2.w---, h0; + | SCB1 | add | 13: ADDh h0.z, h3.--w-,-h2.--w-; + | | | + 5 | SCT1 | mov | 15: NRMh h1.xz, h0; + | SRB | nrm | 15: NRMh h1.xz, h0; + | SCB0 | min | 16: MINh*8 h0.x, |h1|, |h1.z---|; + | SCB1 | max | 17: MAXh h4.w, h0, h1; + | | | + 6 | SCT0 | div | 18: DIVx h2.xy, h1.xz--, h0; + | SCT1 | mov | 19: MOVr r1.zw, g[TEX0].--xy; + | SCB0 | mad | 20: MADr r2.xz,-h1, const.z-w-, r1.z-w-; + | SCB1 | min | 22: MINh h5.w, h0, h1; + | | | + 7 | SCT0/1 | mov | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | TEX | txl | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | SCB0 | max | 27: MAXh h4.x, h2.z---, h2.w---; + | SCB1 | mad | 25: MADr r0.zw, h1.--xz, const, r1; + | | | + 8 | SCT0/1 | mov | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | TEX | txl | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | SCB0/1 | add | 30: ADDh/2 h1, h0, h1; + | | | + 9 | SCT0 | mad | 31: MADr r0.xy,-h2, const.xy--, r1.zw--; + | SCT1 | mov | 33: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 33: TXLr h0, r0, const.zzzz, TEX0; + | SCB1 | min | 35: MINh h4.z, h2, h2.--w-; + | | | + 10 | SCT0 | mad | 37: MADr r1.xy, h2, const.xy--, r1.zw--; + | SCT1 | mov | 39: TXLr h2, r1, const.zzzz, TEX0; + | TEX | txl | 39: TXLr h2, r1, const.zzzz, TEX0; + | SCB0/1 | add | 41: ADDh/2 h0, h0, h2; + | | | + 11 | SCT0 | min | 43: MINh h2.x, h5.w---, h4.z---; + | SCT1 | max | 42: MAXh h2.w, h4, h4.---x; + | SCB0/1 | add | 44: ADDh/2 h0, h0, h1; + | | | + 12 | SCT0 | set | 45: SLTh h2.x, h0.w---, h2; + | SCT1 | set | 46: SGTh h2.w, h0, h2; + | SCB0/1 | mul | 47: MOVh h0, h0; + | | | + 13 | SCT0 | mad | 48: ADDxc0_s rc, h2, h2.w---; + | SCB0/1 | mul | 49: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 0% 0% 50% + 6: 100% 0% 75% + 7: 0% 100% 75% + 8: 0% 100% 100% + 9: 0% 100% 25% + 10: 0% 100% 100% + 11: 50% 0% 100% + 12: 50% 0% 100% + 13: 25% 0% 100% + +MEAN: 17% 61% 67% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 0% 100% + 2: 0% 0% 100% 0% 100% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 0% 0% 0% 100% 100% + 6: 100% 100% 0% 100% 100% + 7: 0% 0% 100% 100% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 0% 100% + 10: 0% 0% 100% 100% 100% + 11: 100% 100% 0% 100% 100% + 12: 100% 100% 0% 100% 100% + 13: 100% 0% 0% 100% 100% + +MEAN: 30% 23% 61% 76% 100% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 13 cycles, 3 r regs, 923,076,923 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O3 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 dir; + half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + lumaNe.w += half(1.0/512.0); + dir.x = -lumaNe.w; + dir.z = -lumaNe.w; + #else + lumaNe.y += half(1.0/512.0); + dir.x = -lumaNe.y; + dir.z = -lumaNe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSw.w; + dir.z += lumaSw.w; + #else + dir.x += lumaSw.y; + dir.z += lumaSw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x -= lumaNw.w; + dir.z += lumaNw.w; + #else + dir.x -= lumaNw.y; + dir.z += lumaNw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSe.w; + dir.z -= lumaSe.w; + #else + dir.x += lumaSe.y; + dir.z -= lumaSe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half4 dir1_pos; + dir1_pos.xy = normalize(dir.xyz).xz; + half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (7) + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (8) + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (9) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (11) + // compilier moves these scalar ops up to other cycles + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w)); + half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w)); + #else + half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y)); + half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y)); + #endif + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (12) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif +/*--------------------------------------------------------------------------*/ +// (13) + if(twoTapLt || twoTapGt) rgby2 = rgby1; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (WITH EARLY EXIT) + +============================================================================== +The code mostly matches the assembly. +I have a feeling that 14 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +Use of FXAA_GREEN_AS_LUMA currently adds a cycle (16 clks). +Will look at fixing this for FXAA 3.12. +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.y(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h1.w(TRUE), v5.xwxx, #0 + 6: addh h0.x(TRUE), h1.w, -h2.y + 7: texpkb h2.w(TRUE), v5.zwzz, #0 + 9: minh h4.w(TRUE), h2.y, h2 + 10: maxh h5.x(TRUE), h2.y, h2.w + 11: texpkb h0.w(TRUE), v5, #0 + 13: addh h3.w(TRUE), -h0, h0.x + 14: addh h0.x(TRUE), h0.w, h0 + 15: addh h0.z(TRUE), -h2.w, h0.x + 16: addh h0.x(TRUE), h2.w, h3.w + 17: minh h5.y(TRUE), h0.w, h1.w + 18: nrmh h2.xz(TRUE), h0_n + 19: minh_m8 h2.w(TRUE), |h2.x|, |h2.z| + 20: divx h4.xy(TRUE), h2_n.xzzw, h2_n.w + 21: movr r1.zw(TRUE), v4.xxxy + 22: maxh h2.w(TRUE), h0, h1 + 23: fenct TRUE + 24: madr r0.xy(TRUE), -h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 26: texpkb h0(TRUE), r0, #0 + 28: maxh h5.x(TRUE), h2.w, h5 + 29: minh h5.w(TRUE), h5.y, h4 + 30: madr r1.xy(TRUE), h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 32: texpkb h2(TRUE), r1, #0 + 34: addh_d2 h2(TRUE), h0, h2 + 35: texpkb h1(TRUE), v4, #0 + 37: maxh h5.y(TRUE), h5.x, h1.w + 38: minh h4.w(TRUE), h1, h5 + 39: madr r0.xy(TRUE), -h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 41: texpkb h0(TRUE), r0, #0 + 43: addh_m8 h5.z(TRUE), h5.y, -h4.w + 44: madr r2.xy(TRUE), h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 46: texpkb h3(TRUE), r2, #0 + 48: addh_d2 h0(TRUE), h0, h3 + 49: addh_d2 h3(TRUE), h0, h2 + 50: movh h0(TRUE), h3 + 51: slth h3.x(TRUE), h3.w, h5.w + 52: sgth h3.w(TRUE), h3, h5.x + 53: addx.c0 rc(TRUE), h3.x, h3 + 54: slth.c0 rc(TRUE), h5.z, h5 + 55: movh h0(c0.NE.w), h2 + 56: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB0 | add | 2: ADDh h2.y, h0.-w--, const.-x--; + | | | + 2 | SCT0/1 | mov | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB0 | add | 6: ADDh h0.x, h1.w---,-h2.y---; + | | | + 3 | SCT0/1 | mov | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | max | 10: MAXh h5.x, h2.y---, h2.w---; + | SCB1 | min | 9: MINh h4.w, h2.---y, h2; + | | | + 4 | SCT0/1 | mov | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h0.w---, h0; + | SCB1 | add | 13: ADDh h3.w,-h0, h0.---x; + | | | + 5 | SCT0 | mad | 16: ADDh h0.x, h2.w---, h3.w---; + | SCT1 | mad | 15: ADDh h0.z,-h2.--w-, h0.--x-; + | SCB0 | min | 17: MINh h5.y, h0.-w--, h1.-w--; + | | | + 6 | SCT1 | mov | 18: NRMh h2.xz, h0; + | SRB | nrm | 18: NRMh h2.xz, h0; + | SCB1 | min | 19: MINh*8 h2.w, |h2.---x|, |h2.---z|; + | | | + 7 | SCT0 | div | 20: DIVx h4.xy, h2.xz--, h2.ww--; + | SCT1 | mov | 21: MOVr r1.zw, g[TEX0].--xy; + | SCB1 | max | 22: MAXh h2.w, h0, h1; + | | | + 8 | SCT0 | mad | 24: MADr r0.xy,-h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 26: TXLr h0, r0, const.xxxx, TEX0; + | TEX | txl | 26: TXLr h0, r0, const.xxxx, TEX0; + | SCB0 | max | 28: MAXh h5.x, h2.w---, h5; + | SCB1 | min | 29: MINh h5.w, h5.---y, h4; + | | | + 9 | SCT0 | mad | 30: MADr r1.xy, h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 32: TXLr h2, r1, const.xxxx, TEX0; + | TEX | txl | 32: TXLr h2, r1, const.xxxx, TEX0; + | SCB0/1 | add | 34: ADDh/2 h2, h0, h2; + | | | + 10 | SCT0/1 | mov | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | TEX | txl | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | SCB0 | max | 37: MAXh h5.y, h5.-x--, h1.-w--; + | SCB1 | min | 38: MINh h4.w, h1, h5; + | | | + 11 | SCT0 | mad | 39: MADr r0.xy,-h4, const.xy--, r1.zw--; + | SCT1 | mov | 41: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 41: TXLr h0, r0, const.zzzz, TEX0; + | SCB0 | mad | 44: MADr r2.xy, h4, const.xy--, r1.zw--; + | SCB1 | add | 43: ADDh*8 h5.z, h5.--y-,-h4.--w-; + | | | + 12 | SCT0/1 | mov | 46: TXLr h3, r2, const.xxxx, TEX0; + | TEX | txl | 46: TXLr h3, r2, const.xxxx, TEX0; + | SCB0/1 | add | 48: ADDh/2 h0, h0, h3; + | | | + 13 | SCT0/1 | mad | 49: ADDh/2 h3, h0, h2; + | SCB0/1 | mul | 50: MOVh h0, h3; + | | | + 14 | SCT0 | set | 51: SLTh h3.x, h3.w---, h5.w---; + | SCT1 | set | 52: SGTh h3.w, h3, h5.---x; + | SCB0 | set | 54: SLThc0 rc, h5.z---, h5; + | SCB1 | add | 53: ADDxc0_s rc, h3.---x, h3; + | | | + 15 | SCT0/1 | mul | 55: MOVh h0(NE0.wwww), h2; + | SCB0/1 | mul | 56: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 50% 0% 25% + 6: 0% 0% 25% + 7: 100% 0% 25% + 8: 0% 100% 50% + 9: 0% 100% 100% + 10: 0% 100% 50% + 11: 0% 100% 75% + 12: 0% 100% 100% + 13: 100% 0% 100% + 14: 50% 0% 50% + 15: 100% 0% 100% + +MEAN: 26% 60% 56% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 100% 0% + 2: 0% 0% 100% 100% 0% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 100% 100% 0% 100% 0% + 6: 0% 0% 0% 0% 100% + 7: 100% 100% 0% 0% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 100% 100% + 10: 0% 0% 100% 100% 100% + 11: 0% 0% 100% 100% 100% + 12: 0% 0% 100% 100% 100% + 13: 100% 100% 0% 100% 100% + 14: 100% 100% 0% 100% 100% + 15: 100% 100% 0% 100% 100% + +MEAN: 33% 33% 60% 86% 80% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 15 cycles, 3 r regs, 800,000,000 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O2 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaNe = rgbyNe.w + half(1.0/512.0); + #else + half lumaNe = rgbyNe.y + half(1.0/512.0); + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaSwNegNe = lumaSw.w - lumaNe; + #else + half lumaSwNegNe = lumaSw.y - lumaNe; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNwSw = max(lumaNw.w, lumaSw.w); + half lumaMinNwSw = min(lumaNw.w, lumaSw.w); + #else + half lumaMaxNwSw = max(lumaNw.y, lumaSw.y); + half lumaMinNwSw = min(lumaNw.y, lumaSw.y); + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half dirZ = lumaNw.w + lumaSwNegNe; + half dirX = -lumaNw.w + lumaSwNegNe; + #else + half dirZ = lumaNw.y + lumaSwNegNe; + half dirX = -lumaNw.y + lumaSwNegNe; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half3 dir; + dir.y = 0.0; + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x = lumaSe.w + dirX; + dir.z = -lumaSe.w + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.w); + #else + dir.x = lumaSe.y + dirX; + dir.z = -lumaSe.y + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir1_pos; + dir1_pos.xy = normalize(dir).xz; + half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (7) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNeSe = max(lumaNe, lumaSe.w); + #else + half lumaMaxNeSe = max(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (8) + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe); + half lumaMin = min(lumaMinNwSw, lumaMinNeSe); +/*--------------------------------------------------------------------------*/ +// (9) + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxM = max(lumaMax, rgbyM.w); + half lumaMinM = min(lumaMin, rgbyM.w); + #else + half lumaMaxM = max(lumaMax, rgbyM.y); + half lumaMinM = min(lumaMin, rgbyM.y); + #endif +/*--------------------------------------------------------------------------*/ +// (11) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE__PS3_EDGE_THRESHOLD; +/*--------------------------------------------------------------------------*/ +// (12) + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (13) + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (14) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif + bool earlyExit = lumaRangeM < lumaMax; + bool twoTap = twoTapLt || twoTapGt; +/*--------------------------------------------------------------------------*/ +// (15) + if(twoTap) rgby2 = rgby1; + if(earlyExit) rgby2 = rgbyM; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif diff --git a/third_party/vulkan/vk_mem_alloc.h b/third_party/vulkan/vk_mem_alloc.h index 71d78b795..cb3ff6070 100644 --- a/third_party/vulkan/vk_mem_alloc.h +++ b/third_party/vulkan/vk_mem_alloc.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2017-2021 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -23,701 +23,301 @@ #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H #define AMD_VULKAN_MEMORY_ALLOCATOR_H +/** \mainpage Vulkan Memory Allocator + +Version 3.0.0-development + +Copyright (c) 2017-2021 Advanced Micro Devices, Inc. All rights reserved. \n +License: MIT + +API documentation divided into groups: [Modules](modules.html) + +\section main_table_of_contents Table of contents + +- User guide + - \subpage quick_start + - [Project setup](@ref quick_start_project_setup) + - [Initialization](@ref quick_start_initialization) + - [Resource allocation](@ref quick_start_resource_allocation) + - \subpage choosing_memory_type + - [Usage](@ref choosing_memory_type_usage) + - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags) + - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types) + - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools) + - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations) + - \subpage memory_mapping + - [Mapping functions](@ref memory_mapping_mapping_functions) + - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory) + - [Cache flush and invalidate](@ref memory_mapping_cache_control) + - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable) + - \subpage staying_within_budget + - [Querying for budget](@ref staying_within_budget_querying_for_budget) + - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage) + - \subpage resource_aliasing + - \subpage custom_memory_pools + - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex) + - [Linear allocation algorithm](@ref linear_algorithm) + - [Free-at-once](@ref linear_algorithm_free_at_once) + - [Stack](@ref linear_algorithm_stack) + - [Double stack](@ref linear_algorithm_double_stack) + - [Ring buffer](@ref linear_algorithm_ring_buffer) + - [Buddy allocation algorithm](@ref buddy_algorithm) + - \subpage defragmentation + - [Defragmenting CPU memory](@ref defragmentation_cpu) + - [Defragmenting GPU memory](@ref defragmentation_gpu) + - [Additional notes](@ref defragmentation_additional_notes) + - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm) + - \subpage statistics + - [Numeric statistics](@ref statistics_numeric_statistics) + - [JSON dump](@ref statistics_json_dump) + - \subpage allocation_annotation + - [Allocation user data](@ref allocation_user_data) + - [Allocation names](@ref allocation_names) + - \subpage virtual_allocator + - \subpage debugging_memory_usage + - [Memory initialization](@ref debugging_memory_usage_initialization) + - [Margins](@ref debugging_memory_usage_margins) + - [Corruption detection](@ref debugging_memory_usage_corruption_detection) + - \subpage opengl_interop +- \subpage usage_patterns + - [Common mistakes](@ref usage_patterns_common_mistakes) + - [Simple patterns](@ref usage_patterns_simple) + - [Advanced patterns](@ref usage_patterns_advanced) +- \subpage configuration + - [Pointers to Vulkan functions](@ref config_Vulkan_functions) + - [Custom host memory allocator](@ref custom_memory_allocator) + - [Device memory allocation callbacks](@ref allocation_callbacks) + - [Device heap memory limit](@ref heap_memory_limit) + - \subpage vk_khr_dedicated_allocation + - \subpage enabling_buffer_device_address + - \subpage vk_amd_device_coherent_memory +- \subpage general_considerations + - [Thread safety](@ref general_considerations_thread_safety) + - [Validation layer warnings](@ref general_considerations_validation_layer_warnings) + - [Allocation algorithm](@ref general_considerations_allocation_algorithm) + - [Features not supported](@ref general_considerations_features_not_supported) + +\section main_see_also See also + +- [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/) +- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) + +\defgroup group_init Library initialization + +\brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object. + +\defgroup group_alloc Memory allocation + +\brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images. +Most basic ones being: vmaCreateBuffer(), vmaCreateImage(). + +\defgroup group_virtual Virtual allocator + +\brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm +for user-defined purpose without allocating any real GPU memory. + +\defgroup group_stats Statistics + +\brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format. +*/ + + #ifdef __cplusplus extern "C" { #endif -/** \mainpage Vulkan Memory Allocator - -Version 2.0.0-alpha.6 (2017-11-13) - -Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved. \n -License: MIT - -Documentation of all members: vk_mem_alloc.h - -Table of contents: - -- User guide - - \subpage quick_start - - \subpage choosing_memory_type - - \subpage memory_mapping - - \subpage custom_memory_pools - - \subpage defragmentation - - \subpage lost_allocations - - \subpage allocation_annotation -- \subpage configuration - - \subpage vk_khr_dedicated_allocation -- \subpage thread_safety - -See also: - -- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) -- [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/) - - - - -\page quick_start Quick start - -\section project_setup Project setup - -In your project code: - --# Include "vk_mem_alloc.h" file wherever you want to use the library. --# In exacly one C++ file define following macro before include to build library - implementation. - -\code -#define VMA_IMPLEMENTATION -#include "vk_mem_alloc.h" -\endcode - -\section initialization Initialization - -At program startup: - --# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object. --# Fill VmaAllocatorCreateInfo structure and create `VmaAllocator` object by - calling vmaCreateAllocator(). - -\code -VmaAllocatorCreateInfo allocatorInfo = {}; -allocatorInfo.physicalDevice = physicalDevice; -allocatorInfo.device = device; - -VmaAllocator allocator; -vmaCreateAllocator(&allocatorInfo, &allocator); -\endcode - -\section resource_allocation Resource allocation - -When you want to create a buffer or image: - --# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure. --# Fill VmaAllocationCreateInfo structure. --# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory - already allocated and bound to it. - -\code -VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; -bufferInfo.size = 65536; -bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - -VmaAllocationCreateInfo allocInfo = {}; -allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - -VkBuffer buffer; -VmaAllocation allocation; -vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); -\endcode - -Don't forget to destroy your objects when no longer needed: - -\code -vmaDestroyBuffer(allocator, buffer, allocation); -vmaDestroyAllocator(allocator); -\endcode - - -\page choosing_memory_type Choosing memory type - -Physical devices in Vulkan support various combinations of memory heaps and -types. Help with choosing correct and optimal memory type for your specific -resource is one of the key features of this library. You can use it by filling -appropriate members of VmaAllocationCreateInfo structure, as described below. -You can also combine multiple methods. - --# If you just want to find memory type index that meets your requirements, you - can use function vmaFindMemoryTypeIndex(). --# If you want to allocate a region of device memory without association with any - specific image or buffer, you can use function vmaAllocateMemory(). Usage of - this function is not recommended and usually not needed. --# If you already have a buffer or an image created, you want to allocate memory - for it and then you will bind it yourself, you can use function - vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(). --# If you want to create a buffer or an image, allocate memory for it and bind - them together, all in one call, you can use function vmaCreateBuffer(), - vmaCreateImage(). This is the recommended way to use this library. - -When using 3. or 4., the library internally queries Vulkan for memory types -supported for that buffer or image (function `vkGetBufferMemoryRequirements()`) -and uses only one of these types. - -If no memory type can be found that meets all the requirements, these functions -return `VK_ERROR_FEATURE_NOT_PRESENT`. - -You can leave VmaAllocationCreateInfo structure completely filled with zeros. -It means no requirements are specified for memory type. -It is valid, although not very useful. - -\section choosing_memory_type_usage Usage - -The easiest way to specify memory requirements is to fill member -VmaAllocationCreateInfo::usage using one of the values of enum `VmaMemoryUsage`. -It defines high level, common usage types. - -For example, if you want to create a uniform buffer that will be filled using -transfer only once or infrequently and used for rendering every frame, you can -do it using following code: - -\code -VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; -bufferInfo.size = 65536; -bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - -VmaAllocationCreateInfo allocInfo = {}; -allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - -VkBuffer buffer; -VmaAllocation allocation; -vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); -\endcode - -\section choosing_memory_type_required_preferred_flags Required and preferred flags - -You can specify more detailed requirements by filling members -VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags -with a combination of bits from enum `VkMemoryPropertyFlags`. For example, -if you want to create a buffer that will be persistently mapped on host (so it -must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`, -use following code: - -\code -VmaAllocationCreateInfo allocInfo = {}; -allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; -allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; -allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - -VkBuffer buffer; -VmaAllocation allocation; -vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); -\endcode - -A memory type is chosen that has all the required flags and as many preferred -flags set as possible. - -If you use VmaAllocationCreateInfo::usage, it is just internally converted to -a set of required and preferred flags. - -\section choosing_memory_type_explicit_memory_types Explicit memory types - -If you inspected memory types available on the physical device and you have -a preference for memory types that you want to use, you can fill member -VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set -means that a memory type with that index is allowed to be used for the -allocation. Special value 0, just like UINT32_MAX, means there are no -restrictions to memory type index. - -Please note that this member is NOT just a memory type index. -Still you can use it to choose just one, specific memory type. -For example, if you already determined that your buffer should be created in -memory type 2, use following code: - -\code -uint32_t memoryTypeIndex = 2; - -VmaAllocationCreateInfo allocInfo = {}; -allocInfo.memoryTypeBits = 1u << memoryTypeIndex; - -VkBuffer buffer; -VmaAllocation allocation; -vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); -\endcode - -\section choosing_memory_type_custom_memory_pools Custom memory pools - -If you allocate from custom memory pool, all the ways of specifying memory -requirements described above are not applicable and the aforementioned members -of VmaAllocationCreateInfo structure are ignored. Memory type is selected -explicitly when creating the pool and then used to make all the allocations from -that pool. For further details, see \ref custom_memory_pools. - - -\page memory_mapping Memory mapping - -\section persistently_mapped_memory Persistently mapped memory - -If you need to map memory on host, it may happen that two allocations are -assigned to the same `VkDeviceMemory` block, so if you map them both at the same -time, it will cause error because mapping single memory block multiple times is -illegal in Vulkan. - -TODO update this... - -It is safer, more convenient and more efficient to use special feature designed -for that: persistently mapped memory. Allocations made with -`VMA_ALLOCATION_CREATE_MAPPED_BIT` flag set in -VmaAllocationCreateInfo::flags are returned from device memory -blocks that stay mapped all the time, so you can just access CPU pointer to it. -VmaAllocationInfo::pMappedData pointer is already offseted to the beginning of -particular allocation. Example: - -\code -VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; -bufCreateInfo.size = 1024; -bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - -VmaAllocationCreateInfo allocCreateInfo = {}; -allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; -allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - -VkBuffer buf; -VmaAllocation alloc; -VmaAllocationInfo allocInfo; -vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); - -// Buffer is immediately mapped. You can access its memory. -memcpy(allocInfo.pMappedData, myData, 1024); -\endcode - -Memory in Vulkan doesn't need to be unmapped before using it e.g. for transfers, -but if you are not sure whether it's `HOST_COHERENT` (here is surely is because -it's created with `VMA_MEMORY_USAGE_CPU_ONLY`), you should check it. If it's -not, you should call `vkInvalidateMappedMemoryRanges()` before reading and -`vkFlushMappedMemoryRanges()` after writing to mapped memory on CPU. Example: - -\code -VkMemoryPropertyFlags memFlags; -vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags); -if((memFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) -{ - VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; - memRange.memory = allocInfo.deviceMemory; - memRange.offset = allocInfo.offset; - memRange.size = allocInfo.size; - vkFlushMappedMemoryRanges(device, 1, &memRange); -} -\endcode - -\section amd_perf_note Note on performance - -There is a situation that you should be careful about. It happens only if all of -following conditions are met: - --# You use AMD GPU. --# You use the memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` - (used when you specify `VMA_MEMORY_USAGE_CPU_TO_GPU`). --# Operating system is Windows 7 or 8.x (Windows 10 is not affected because it - uses WDDM2). - -Then whenever a `VkDeviceMemory` block allocated from this memory type is mapped -for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this -block is migrated by WDDM to system RAM, which degrades performance. It doesn't -matter if that particular memory block is actually used by the command buffer -being submitted. - -To avoid this problem, either make sure to unmap all allocations made from this -memory type before your Submit and Present, or use `VMA_MEMORY_USAGE_GPU_ONLY` -and transfer from a staging buffer in `VMA_MEMORY_USAGE_CPU_ONLY`, which can -safely stay mapped all the time. - -\page custom_memory_pools Custom memory pools - -The library automatically creates and manages default memory pool for each -memory type available on the device. A pool contains a number of -`VkDeviceMemory` blocks. You can create custom pool and allocate memory out of -it. It can be useful if you want to: - -- Keep certain kind of allocations separate from others. -- Enforce particular size of Vulkan memory blocks. -- Limit maximum amount of Vulkan memory allocated for that pool. - -To use custom memory pools: - --# Fill VmaPoolCreateInfo structure. --# Call vmaCreatePool() to obtain `VmaPool` handle. --# When making an allocation, set VmaAllocationCreateInfo::pool to this handle. - You don't need to specify any other parameters of this structure, like usage. - -Example: - -\code -// Create a pool that could have at most 2 blocks, 128 MB each. -VmaPoolCreateInfo poolCreateInfo = {}; -poolCreateInfo.memoryTypeIndex = ... -poolCreateInfo.blockSize = 128ull * 1024 * 1024; -poolCreateInfo.maxBlockCount = 2; - -VmaPool pool; -vmaCreatePool(allocator, &poolCreateInfo, &pool); - -// Allocate a buffer out of it. -VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; -bufCreateInfo.size = 1024; -bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - -VmaAllocationCreateInfo allocCreateInfo = {}; -allocCreateInfo.pool = pool; - -VkBuffer buf; -VmaAllocation alloc; -VmaAllocationInfo allocInfo; -vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); -\endcode - -You have to free all allocations made from this pool before destroying it. - -\code -vmaDestroyBuffer(allocator, buf, alloc); -vmaDestroyPool(allocator, pool); -\endcode - -\page defragmentation Defragmentation - -Interleaved allocations and deallocations of many objects of varying size can -cause fragmentation, which can lead to a situation where the library is unable -to find a continuous range of free memory for a new allocation despite there is -enough free space, just scattered across many small free ranges between existing -allocations. - -To mitigate this problem, you can use vmaDefragment(). Given set of allocations, -this function can move them to compact used memory, ensure more continuous free -space and possibly also free some `VkDeviceMemory`. It can work only on -allocations made from memory type that is `HOST_VISIBLE`. Allocations are -modified to point to the new `VkDeviceMemory` and offset. Data in this memory is -also `memmove`-ed to the new place. However, if you have images or buffers bound -to these allocations (and you certainly do), you need to destroy, recreate, and -bind them to the new place in memory. - -For further details and example code, see documentation of function -vmaDefragment(). - -\page lost_allocations Lost allocations - -If your game oversubscribes video memory, if may work OK in previous-generation -graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically -paged to system RAM. In Vulkan you can't do it because when you run out of -memory, an allocation just fails. If you have more data (e.g. textures) that can -fit into VRAM and you don't need it all at once, you may want to upload them to -GPU on demand and "push out" ones that are not used for a long time to make room -for the new ones, effectively using VRAM (or a cartain memory pool) as a form of -cache. Vulkan Memory Allocator can help you with that by supporting a concept of -"lost allocations". - -To create an allocation that can become lost, include `VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT` -flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to -such allocation in every new frame, you need to query it if it's not lost. To -check it: call vmaGetAllocationInfo() and see if VmaAllocationInfo::deviceMemory -is not `VK_NULL_HANDLE`. If the allocation is lost, you should not use it or -buffer/image bound to it. You mustn't forget to destroy this allocation and this -buffer/image. - -To create an allocation that can make some other allocations lost to make room -for it, use `VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT` flag. You will -usually use both flags `VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT` and -`VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT` at the same time. - -Warning! Current implementation uses quite naive, brute force algorithm, -which can make allocation calls that use `VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT` -flag quite slow. A new, more optimal algorithm and data structure to speed this -up is planned for the future. - -When interleaving creation of new allocations with usage of existing ones, -how do you make sure that an allocation won't become lost while it's used in the -current frame? - -It is ensured because vmaGetAllocationInfo() not only returns allocation -parameters and checks whether it's not lost, but when it's not, it also -atomically marks it as used in the current frame, which makes it impossible to -become lost in that frame. It uses lockless algorithm, so it works fast and -doesn't involve locking any internal mutex. - -What if my allocation may still be in use by the GPU when it's rendering a -previous frame while I already submit new frame on the CPU? - -You can make sure that allocations "touched" by vmaGetAllocationInfo() will not -become lost for a number of additional frames back from the current one by -specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default -memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool). - -How do you inform the library when new frame starts? - -You need to call function vmaSetCurrentFrameIndex(). - -Example code: - -\code -struct MyBuffer -{ - VkBuffer m_Buf = nullptr; - VmaAllocation m_Alloc = nullptr; - - // Called when the buffer is really needed in the current frame. - void EnsureBuffer(); -}; - -void MyBuffer::EnsureBuffer() -{ - // Buffer has been created. - if(m_Buf != VK_NULL_HANDLE) - { - // Check if its allocation is not lost + mark it as used in current frame. - VmaAllocationInfo allocInfo; - vmaGetAllocationInfo(allocator, m_Alloc, &allocInfo); - if(allocInfo.deviceMemory != VK_NULL_HANDLE) - { - // It's all OK - safe to use m_Buf. - return; - } - } - - // Buffer not yet exists or lost - destroy and recreate it. - - vmaDestroyBuffer(allocator, m_Buf, m_Alloc); - - VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - bufCreateInfo.size = 1024; - bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - - VmaAllocationCreateInfo allocCreateInfo = {}; - allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT | - VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT; - - vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr); -} -\endcode - -When using lost allocations, you may see some Vulkan validation layer warnings -about overlapping regions of memory bound to different kinds of buffers and -images. This is still valid as long as you implement proper handling of lost -allocations (like in the example above) and don't use them. - -The library uses following algorithm for allocation, in order: - --# Try to find free range of memory in existing blocks. --# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size. --# If failed, try to create such block with size/2 and size/4. --# If failed and `VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT` flag was - specified, try to find space in existing blocks, possilby making some other - allocations lost. --# If failed, try to allocate separate `VkDeviceMemory` for this allocation, - just like when you use `VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT`. --# If failed, choose other memory type that meets the requirements specified in - VmaAllocationCreateInfo and go to point 1. --# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. - - -\page allocation_annotation Allocation names and user data - -\section allocation_user_data Allocation user data - -You can annotate allocations with your own information, e.g. for debugging purposes. -To do that, fill VmaAllocationCreateInfo::pUserData field when creating -an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer, -some handle, index, key, ordinal number or any other value that would associate -the allocation with your custom metadata. - -\code -VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; -// Fill bufferInfo... - -MyBufferMetadata* pMetadata = CreateBufferMetadata(); - -VmaAllocationCreateInfo allocCreateInfo = {}; -allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; -allocCreateInfo.pUserData = pMetadata; - -VkBuffer buffer; -VmaAllocation allocation; -vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr); -\endcode - -The pointer may be later retrieved as VmaAllocationInfo::pUserData: - -\code -VmaAllocationInfo allocInfo; -vmaGetAllocationInfo(allocator, allocation, &allocInfo); -MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData; -\endcode - -It can also be changed using function vmaSetAllocationUserData(). - -Values of (non-zero) allocations' `pUserData` are printed in JSON report created by -vmaBuildStatsString(), in hexadecimal form. - -\section allocation_names Allocation names - -There is alternative mode available where `pUserData` pointer is used to point to -a null-terminated string, giving a name to the allocation. To use this mode, -set `VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT` flag in VmaAllocationCreateInfo::flags. -Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to -vmaSetAllocationUserData() must be either null or pointer to a null-terminated string. -The library creates internal copy of the string, so the pointer you pass doesn't need -to be valid for whole lifetime of the allocation. You can free it after the call. - -\code -VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; -// Fill imageInfo... - -std::string imageName = "Texture: "; -imageName += fileName; - -VmaAllocationCreateInfo allocCreateInfo = {}; -allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; -allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT; -allocCreateInfo.pUserData = imageName.c_str(); - -VkImage image; -VmaAllocation allocation; -vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr); -\endcode - -The value of `pUserData` pointer of the allocation will be different than the one -you passed when setting allocation's name - pointing to a buffer managed -internally that holds copy of the string. - -\code -VmaAllocationInfo allocInfo; -vmaGetAllocationInfo(allocator, allocation, &allocInfo); -const char* imageName = (const char*)allocInfo.pUserData; -printf("Image name: %s\n", imageName); -\endcode - -That string is also printed in JSON report created by vmaBuildStatsString(). - -\page configuration Configuration - -Please check "CONFIGURATION SECTION" in the code to find macros that you can define -before each include of this file or change directly in this file to provide -your own implementation of basic facilities like assert, `min()` and `max()` functions, -mutex etc. C++ STL is used by default, but changing these allows you to get rid -of any STL usage if you want, as many game developers tend to do. - -\section config_Vulkan_functions Pointers to Vulkan functions - -The library uses Vulkan functions straight from the `vulkan.h` header by default. -If you want to provide your own pointers to these functions, e.g. fetched using -`vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`: - --# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`. --# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions. - -\section custom_memory_allocator Custom host memory allocator - -If you use custom allocator for CPU memory rather than default operator `new` -and `delete` from C++, you can make this library using your allocator as well -by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These -functions will be passed to Vulkan, as well as used by the library itself to -make any CPU-side allocations. - -\section allocation_callbacks Device memory allocation callbacks - -The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally. -You can setup callbacks to be informed about these calls, e.g. for the purpose -of gathering some statistics. To do it, fill optional member -VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. - -\section heap_memory_limit Device heap memory limit - -If you want to test how your program behaves with limited amount of Vulkan device -memory available without switching your graphics card to one that really has -smaller VRAM, you can use a feature of this library intended for this purpose. -To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit. - - - -\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation - -VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve -performance on some GPUs. It augments Vulkan API with possibility to query -driver whether it prefers particular buffer or image to have its own, dedicated -allocation (separate `VkDeviceMemory` block) for better efficiency - to be able -to do some internal optimizations. - -The extension is supported by this library. It will be used automatically when -enabled. To enable it: - -1 . When creating Vulkan device, check if following 2 device extensions are -supported (call `vkEnumerateDeviceExtensionProperties()`). -If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`). - -- VK_KHR_get_memory_requirements2 -- VK_KHR_dedicated_allocation - -If you enabled these extensions: - -2 . Use `VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT` flag when creating -your `VmaAllocator` to inform the library that you enabled required extensions -and you want the library to use them. - -\code -allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; - -vmaCreateAllocator(&allocatorInfo, &allocator); -\endcode - -That's all. The extension will be automatically used whenever you create a -buffer using vmaCreateBuffer() or image using vmaCreateImage(). - -When using the extension together with Vulkan Validation Layer, you will receive -warnings like this: - - vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer. - -It is OK, you should just ignore it. It happens because you use function -`vkGetBufferMemoryRequirements2KHR()` instead of standard -`vkGetBufferMemoryRequirements()`, while the validation layer seems to be -unaware of it. - -To learn more about this extension, see: - -- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation) -- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5) - - - -\page thread_safety Thread safety - -- The library has no global state, so separate `VmaAllocator` objects can be used - independently. -- By default, all calls to functions that take `VmaAllocator` as first parameter - are safe to call from multiple threads simultaneously because they are - synchronized internally when needed. -- When the allocator is created with `VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT` - flag, calls to functions that take such `VmaAllocator` object must be - synchronized externally. -- Access to a `VmaAllocation` object must be externally synchronized. For example, - you must not call vmaGetAllocationInfo() and vmaMapMemory() from different - threads at the same time if you pass the same `VmaAllocation` object to these - functions. - +#if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS + extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; + extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; + extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; + extern PFN_vkAllocateMemory vkAllocateMemory; + extern PFN_vkFreeMemory vkFreeMemory; + extern PFN_vkMapMemory vkMapMemory; + extern PFN_vkUnmapMemory vkUnmapMemory; + extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; + extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; + extern PFN_vkBindBufferMemory vkBindBufferMemory; + extern PFN_vkBindImageMemory vkBindImageMemory; + extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; + extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; + extern PFN_vkCreateBuffer vkCreateBuffer; + extern PFN_vkDestroyBuffer vkDestroyBuffer; + extern PFN_vkCreateImage vkCreateImage; + extern PFN_vkDestroyImage vkDestroyImage; + extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer; + #if VMA_VULKAN_VERSION >= 1001000 + extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2; + extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2; + extern PFN_vkBindBufferMemory2 vkBindBufferMemory2; + extern PFN_vkBindImageMemory2 vkBindImageMemory2; + extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2; + #endif // #if VMA_VULKAN_VERSION >= 1001000 +#endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES + +#ifndef VULKAN_H_ + #include +#endif + +#if !defined(VK_VERSION_1_2) + // This one is tricky. Vulkan specification defines this code as available since + // Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131. + // See pull request #207. + #define VK_ERROR_UNKNOWN ((VkResult)-13) +#endif + +// Define this macro to declare maximum supported Vulkan version in format AAABBBCCC, +// where AAA = major, BBB = minor, CCC = patch. +// If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion. +#if !defined(VMA_VULKAN_VERSION) + #if defined(VK_VERSION_1_2) + #define VMA_VULKAN_VERSION 1002000 + #elif defined(VK_VERSION_1_1) + #define VMA_VULKAN_VERSION 1001000 + #else + #define VMA_VULKAN_VERSION 1000000 + #endif +#endif + +#if !defined(VMA_DEDICATED_ALLOCATION) + #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation + #define VMA_DEDICATED_ALLOCATION 1 + #else + #define VMA_DEDICATED_ALLOCATION 0 + #endif +#endif + +#if !defined(VMA_BIND_MEMORY2) + #if VK_KHR_bind_memory2 + #define VMA_BIND_MEMORY2 1 + #else + #define VMA_BIND_MEMORY2 0 + #endif +#endif + +#if !defined(VMA_MEMORY_BUDGET) + #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000) + #define VMA_MEMORY_BUDGET 1 + #else + #define VMA_MEMORY_BUDGET 0 + #endif +#endif + +// Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers. +#if !defined(VMA_BUFFER_DEVICE_ADDRESS) + #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000 + #define VMA_BUFFER_DEVICE_ADDRESS 1 + #else + #define VMA_BUFFER_DEVICE_ADDRESS 0 + #endif +#endif + +// Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers. +#if !defined(VMA_MEMORY_PRIORITY) + #if VK_EXT_memory_priority + #define VMA_MEMORY_PRIORITY 1 + #else + #define VMA_MEMORY_PRIORITY 0 + #endif +#endif + +// Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers. +#if !defined(VMA_EXTERNAL_MEMORY) + #if VK_KHR_external_memory + #define VMA_EXTERNAL_MEMORY 1 + #else + #define VMA_EXTERNAL_MEMORY 0 + #endif +#endif + +// Define these macros to decorate all public functions with additional code, +// before and after returned type, appropriately. This may be useful for +// exporting the functions when compiling VMA as a separate library. Example: +// #define VMA_CALL_PRE __declspec(dllexport) +// #define VMA_CALL_POST __cdecl +#ifndef VMA_CALL_PRE + #define VMA_CALL_PRE +#endif +#ifndef VMA_CALL_POST + #define VMA_CALL_POST +#endif + +// Define this macro to decorate pointers with an attribute specifying the +// length of the array they point to if they are not null. +// +// The length may be one of +// - The name of another parameter in the argument list where the pointer is declared +// - The name of another member in the struct where the pointer is declared +// - The name of a member of a struct type, meaning the value of that member in +// the context of the call. For example +// VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"), +// this means the number of memory heaps available in the device associated +// with the VmaAllocator being dealt with. +#ifndef VMA_LEN_IF_NOT_NULL + #define VMA_LEN_IF_NOT_NULL(len) +#endif + +// The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang. +// see: https://clang.llvm.org/docs/AttributeReference.html#nullable +#ifndef VMA_NULLABLE + #ifdef __clang__ + #define VMA_NULLABLE _Nullable + #else + #define VMA_NULLABLE + #endif +#endif + +// The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang. +// see: https://clang.llvm.org/docs/AttributeReference.html#nonnull +#ifndef VMA_NOT_NULL + #ifdef __clang__ + #define VMA_NOT_NULL _Nonnull + #else + #define VMA_NOT_NULL + #endif +#endif + +// If non-dispatchable handles are represented as pointers then we can give +// then nullability annotations +#ifndef VMA_NOT_NULL_NON_DISPATCHABLE + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL + #else + #define VMA_NOT_NULL_NON_DISPATCHABLE + #endif +#endif + +#ifndef VMA_NULLABLE_NON_DISPATCHABLE + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE + #else + #define VMA_NULLABLE_NON_DISPATCHABLE + #endif +#endif + +#ifndef VMA_STATS_STRING_ENABLED + #define VMA_STATS_STRING_ENABLED 1 +#endif + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// INTERFACE +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE. +#ifndef _VMA_ENUM_DECLARATIONS + +/** +\addtogroup group_init +@{ */ -#include - -VK_DEFINE_HANDLE(VmaAllocator) - -/// Callback function called after successful vkAllocateMemory. -typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)( - VmaAllocator allocator, - uint32_t memoryType, - VkDeviceMemory memory, - VkDeviceSize size); -/// Callback function called before vkFreeMemory. -typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)( - VmaAllocator allocator, - uint32_t memoryType, - VkDeviceMemory memory, - VkDeviceSize size); - -/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`. - -Provided for informative purpose, e.g. to gather statistics about number of -allocations or total amount of memory allocated in Vulkan. - -Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. -*/ -typedef struct VmaDeviceMemoryCallbacks { - /// Optional, can be null. - PFN_vmaAllocateDeviceMemoryFunction pfnAllocate; - /// Optional, can be null. - PFN_vmaFreeDeviceMemoryFunction pfnFree; -} VmaDeviceMemoryCallbacks; - -/// Flags for created VmaAllocator. -typedef enum VmaAllocatorCreateFlagBits { +/// Flags for created #VmaAllocator. +typedef enum VmaAllocatorCreateFlagBits +{ /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you. Using this flag may increase performance because internal mutexes are not used. @@ -725,9 +325,12 @@ typedef enum VmaAllocatorCreateFlagBits { VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, /** \brief Enables usage of VK_KHR_dedicated_allocation extension. - Using this extenion will automatically allocate dedicated blocks of memory for + The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. + When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. + + Using this extension will automatically allocate dedicated blocks of memory for some buffers and images instead of suballocating place for them out of bigger - memory blocks (as if you explicitly used VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT + memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag) when it is recommended by the driver. It may improve performance on some GPUs. @@ -736,81 +339,643 @@ typedef enum VmaAllocatorCreateFlagBits { VmaAllocatorCreateInfo::device, and you want them to be used internally by this library: - - VK_KHR_get_memory_requirements2 - - VK_KHR_dedicated_allocation + - VK_KHR_get_memory_requirements2 (device extension) + - VK_KHR_dedicated_allocation (device extension) -When this flag is set, you can experience following warnings reported by Vulkan -validation layer. You can ignore them. + When this flag is set, you can experience following warnings reported by Vulkan + validation layer. You can ignore them. -> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer. + > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer. */ VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002, + /** + Enables usage of VK_KHR_bind_memory2 extension. + + The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. + When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. + + You may set this flag only if you found out that this device extension is supported, + you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + and you want it to be used internally by this library. + + The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`, + which allow to pass a chain of `pNext` structures while binding. + This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2(). + */ + VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004, + /** + Enables usage of VK_EXT_memory_budget extension. + + You may set this flag only if you found out that this device extension is supported, + you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + and you want it to be used internally by this library, along with another instance extension + VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted). + + The extension provides query for current memory usage and budget, which will probably + be more accurate than an estimation used by the library otherwise. + */ + VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008, + /** + Enables usage of VK_AMD_device_coherent_memory extension. + + You may set this flag only if you: + + - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device, + - want it to be used internally by this library. + + The extension and accompanying device feature provide access to memory types with + `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags. + They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR. + + When the extension is not enabled, such memory types are still enumerated, but their usage is illegal. + To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type, + returning `VK_ERROR_FEATURE_NOT_PRESENT`. + */ + VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010, + /** + Enables usage of "buffer device address" feature, which allows you to use function + `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader. + + You may set this flag only if you: + + 1. (For Vulkan version < 1.2) Found as available and enabled device extension + VK_KHR_buffer_device_address. + This extension is promoted to core Vulkan 1.2. + 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`. + + When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA. + The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to + allocated memory blocks wherever it might be needed. + + For more information, see documentation chapter \ref enabling_buffer_device_address. + */ + VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020, + /** + Enables usage of VK_EXT_memory_priority extension in the library. + + You may set this flag only if you found available and enabled this device extension, + along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`, + while creating Vulkan device passed as VmaAllocatorCreateInfo::device. + + When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority + are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored. + + A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. + Larger values are higher priority. The granularity of the priorities is implementation-dependent. + It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`. + The value to be used for default priority is 0.5. + For more details, see the documentation of the VK_EXT_memory_priority extension. + */ + VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040, VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VmaAllocatorCreateFlagBits; typedef VkFlags VmaAllocatorCreateFlags; +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/// \brief Intended usage of the allocated memory. +typedef enum VmaMemoryUsage +{ + /** No intended memory usage specified. + Use other members of VmaAllocationCreateInfo to specify your requirements. + */ + VMA_MEMORY_USAGE_UNKNOWN = 0, + /** Memory will be used on device only, so fast access from the device is preferred. + It usually means device-local GPU (video) memory. + No need to be mappable on host. + It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`. + + Usage: + + - Resources written and read by device, e.g. images used as attachments. + - Resources transferred from host once (immutable) or infrequently and read by + device multiple times, e.g. textures to be sampled, vertex buffers, uniform + (constant) buffers, and majority of other types of resources used on GPU. + + Allocation may still end up in `HOST_VISIBLE` memory on some implementations. + In such case, you are free to map it. + You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type. + */ + VMA_MEMORY_USAGE_GPU_ONLY = 1, + /** Memory will be mappable on host. + It usually means CPU (system) memory. + Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`. + CPU access is typically uncached. Writes may be write-combined. + Resources created in this pool may still be accessible to the device, but access to them can be slow. + It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`. + + Usage: Staging copy of resources used as transfer source. + */ + VMA_MEMORY_USAGE_CPU_ONLY = 2, + /** + Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU. + CPU access is typically uncached. Writes may be write-combined. + + Usage: Resources written frequently by host (dynamic), read by device. E.g. textures (with LINEAR layout), vertex buffers, uniform buffers updated every frame or every draw call. + */ + VMA_MEMORY_USAGE_CPU_TO_GPU = 3, + /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached. + It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`. + + Usage: + + - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping. + - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection. + */ + VMA_MEMORY_USAGE_GPU_TO_CPU = 4, + /** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`. + + Usage: Staging copy of resources moved from GPU memory to CPU memory as part + of custom paging/residency mechanism, to be moved back to GPU memory when needed. + */ + VMA_MEMORY_USAGE_CPU_COPY = 5, + /** Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`. + Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation. + + Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`. + + Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + */ + VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6, + + VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF +} VmaMemoryUsage; + +/// Flags to be passed as VmaAllocationCreateInfo::flags. +typedef enum VmaAllocationCreateFlagBits +{ + /** \brief Set this flag if the allocation should have its own memory block. + + Use it for special, big resources, like fullscreen images used as attachments. + */ + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001, + + /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block. + + If new allocation cannot be placed in any of the existing blocks, allocation + fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + + You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and + #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense. + + If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */ + VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002, + /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. + + Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData. + + It is valid to use this flag for allocation made from memory type that is not + `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is + useful if you need an allocation that is efficient to use on GPU + (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that + support it (e.g. Intel GPU). + */ + VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004, + /// \deprecated Removed. Do not use. + VMA_ALLOCATION_CREATE_RESERVED_1_BIT = 0x00000008, + /// \deprecated Removed. Do not use. + VMA_ALLOCATION_CREATE_RESERVED_2_BIT = 0x00000010, + /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a + null-terminated string. Instead of copying pointer value, a local copy of the + string is made and stored in allocation's `pUserData`. The string is automatically + freed together with the allocation. It is also used in vmaBuildStatsString(). + */ + VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020, + /** Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040, + /** Create both buffer/image and allocation, but don't bind them together. + It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions. + The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage(). + Otherwise it is ignored. + */ + VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080, + /** Create allocation only if additional device memory required for it, if any, won't exceed + memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + */ + VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100, + /** \brief Set this flag if the allocated memory will have aliasing resources. + * + Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified. + Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors. + */ + VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200, + + /** Allocation strategy that chooses smallest possible free range for the + allocation. + */ + VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000, + /** Allocation strategy that chooses biggest possible free range for the + allocation. + */ + VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000, + /** Allocation strategy that chooses first suitable free range for the + allocation. + + "First" doesn't necessarily means the one with smallest offset in memory, + but rather the one that is easiest and fastest to find. + */ + VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000, + + /** Allocation strategy that tries to minimize memory usage. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT, + /** Allocation strategy that tries to minimize allocation time. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT, + /** Allocation strategy that tries to minimize memory fragmentation. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT, + + /** A bit mask to extract only `STRATEGY` bits from entire set of flags. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MASK = + VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT, + + VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocationCreateFlagBits; +typedef VkFlags VmaAllocationCreateFlags; + +/// Flags to be passed as VmaPoolCreateInfo::flags. +typedef enum VmaPoolCreateFlagBits +{ + /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored. + + This is an optional optimization flag. + + If you always allocate using vmaCreateBuffer(), vmaCreateImage(), + vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator + knows exact type of your allocations so it can handle Buffer-Image Granularity + in the optimal way. + + If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(), + exact type of such allocations is not known, so allocator must be conservative + in handling Buffer-Image Granularity, which can lead to suboptimal allocation + (wasted memory). In that case, if you can make sure you always allocate only + buffers and linear images or only optimal images out of this pool, use this flag + to make allocator disregard Buffer-Image Granularity and so make allocations + faster and more optimal. + */ + VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002, + + /** \brief Enables alternative, linear allocation algorithm in this pool. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + + When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default). + */ + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004, + + /** \brief Enables alternative, buddy allocation algorithm in this pool. + + It operates on a tree of blocks, each having size that is a power of two and + a half of its parent's size. Comparing to default algorithm, this one provides + faster allocation and deallocation and decreased external fragmentation, + at the expense of more memory wasted (internal fragmentation). + For details, see documentation chapter \ref buddy_algorithm. + */ + VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008, + + /** \brief Enables alternative, Two-Level Segregated Fit (TLSF) allocation algorithm in this pool. + + This algorithm is based on 2-level lists dividing address space into smaller + chunks. The first level is aligned to power of two which serves as buckets for requested + memory to fall into, and the second level is lineary subdivided into lists of free memory. + This algorithm aims to achieve bounded response time even in the worst case scenario. + Allocation time can be sometimes slightly longer than compared to other algorithms + but in return the application can avoid stalls in case of fragmentation, giving + predictable results, suitable for real-time use cases. + */ + VMA_POOL_CREATE_TLSF_ALGORITHM_BIT = 0x00000010, + + /** Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_POOL_CREATE_ALGORITHM_MASK = + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT | + VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT | + VMA_POOL_CREATE_TLSF_ALGORITHM_BIT, + + VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaPoolCreateFlagBits; +/// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits. +typedef VkFlags VmaPoolCreateFlags; + +/// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use. +typedef enum VmaDefragmentationFlagBits +{ + VMA_DEFRAGMENTATION_FLAG_INCREMENTAL = 0x1, + VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaDefragmentationFlagBits; +typedef VkFlags VmaDefragmentationFlags; + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. +typedef enum VmaVirtualBlockCreateFlagBits +{ + /** \brief Enables alternative, linear allocation algorithm in this virtual block. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001, + + /** \brief Enables alternative, buddy allocation algorithm in this virtual block. + + It operates on a tree of blocks, each having size that is a power of two and + a half of its parent's size. Comparing to default algorithm, this one provides + faster allocation and deallocation and decreased external fragmentation, + at the expense of more memory wasted (internal fragmentation). + For details, see documentation chapter \ref buddy_algorithm. + */ + VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT = 0x00000002, + + /** \brief Enables alternative, TLSF allocation algorithm in virtual block. + + This algorithm is based on 2-level lists dividing address space into smaller + chunks. The first level is aligned to power of two which serves as buckets for requested + memory to fall into, and the second level is lineary subdivided into lists of free memory. + This algorithm aims to achieve bounded response time even in the worst case scenario. + Allocation time can be sometimes slightly longer than compared to other algorithms + but in return the application can avoid stalls in case of fragmentation, giving + predictable results, suitable for real-time use cases. + */ + VMA_VIRTUAL_BLOCK_CREATE_TLSF_ALGORITHM_BIT = 0x00000004, + + /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK = + VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT | + VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT | + VMA_VIRTUAL_BLOCK_CREATE_TLSF_ALGORITHM_BIT, + + VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaVirtualBlockCreateFlagBits; +/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits. +typedef VkFlags VmaVirtualBlockCreateFlags; + +/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. +typedef enum VmaVirtualAllocationCreateFlagBits +{ + /** \brief Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT, + /** \brief Allocation strategy that tries to minimize memory usage. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT, + /** \brief Allocation strategy that tries to minimize allocation time. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT, + /** \brief Allocation strategy that tries to minimize memory fragmentation. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT, + /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags. + + These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK, + + VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaVirtualAllocationCreateFlagBits; +/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits. +typedef VkFlags VmaVirtualAllocationCreateFlags; + +/** @} */ + +#endif // _VMA_ENUM_DECLARATIONS + +#ifndef _VMA_DATA_TYPES_DECLARATIONS + +/** +\addtogroup group_init +@{ */ + +/** \struct VmaAllocator +\brief Represents main object of this library initialized. + +Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it. +Call function vmaDestroyAllocator() to destroy it. + +It is recommended to create just one object of this type per `VkDevice` object, +right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed. +*/ +VK_DEFINE_HANDLE(VmaAllocator) + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \struct VmaPool +\brief Represents custom memory pool + +Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it. +Call function vmaDestroyPool() to destroy it. + +For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools). +*/ +VK_DEFINE_HANDLE(VmaPool) + +/** \struct VmaAllocation +\brief Represents single memory allocation. + +It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type +plus unique offset. + +There are multiple ways to create such object. +You need to fill structure VmaAllocationCreateInfo. +For more information see [Choosing memory type](@ref choosing_memory_type). + +Although the library provides convenience functions that create Vulkan buffer or image, +allocate memory for it and bind them together, +binding of the allocation to a buffer or an image is out of scope of the allocation itself. +Allocation object can exist without buffer/image bound, +binding can be done manually by the user, and destruction of it can be done +independently of destruction of the allocation. + +The object also remembers its size and some other information. +To retrieve this information, use function vmaGetAllocationInfo() and inspect +returned structure VmaAllocationInfo. +*/ +VK_DEFINE_HANDLE(VmaAllocation) + +/** \struct VmaDefragmentationContext +\brief Represents Opaque object that represents started defragmentation process. + +Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it. +Call function vmaDefragmentationEnd() to destroy it. +*/ +VK_DEFINE_HANDLE(VmaDefragmentationContext) + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \struct VmaVirtualAllocation +\brief Represents single memory allocation done inside VmaVirtualBlock. + +Use it as a unique identifier to virtual allocation within the single block. +*/ +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation); + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \struct VmaVirtualBlock +\brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory. + +Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it. +For more information, see documentation chapter \ref virtual_allocator. + +This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally. +*/ +VK_DEFINE_HANDLE(VmaVirtualBlock) + +/** @} */ + +/** +\addtogroup group_init +@{ +*/ + +/// Callback function called after successful vkAllocateMemory. +typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryType, + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory, + VkDeviceSize size, + void* VMA_NULLABLE pUserData); + +/// Callback function called before vkFreeMemory. +typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryType, + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory, + VkDeviceSize size, + void* VMA_NULLABLE pUserData); + +/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`. + +Provided for informative purpose, e.g. to gather statistics about number of +allocations or total amount of memory allocated in Vulkan. + +Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. +*/ +typedef struct VmaDeviceMemoryCallbacks +{ + /// Optional, can be null. + PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate; + /// Optional, can be null. + PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree; + /// Optional, can be null. + void* VMA_NULLABLE pUserData; +} VmaDeviceMemoryCallbacks; + /** \brief Pointers to some Vulkan functions - a subset used by the library. Used in VmaAllocatorCreateInfo::pVulkanFunctions. */ -typedef struct VmaVulkanFunctions { - PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; - PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; - PFN_vkAllocateMemory vkAllocateMemory; - PFN_vkFreeMemory vkFreeMemory; - PFN_vkMapMemory vkMapMemory; - PFN_vkUnmapMemory vkUnmapMemory; - PFN_vkBindBufferMemory vkBindBufferMemory; - PFN_vkBindImageMemory vkBindImageMemory; - PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; - PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; - PFN_vkCreateBuffer vkCreateBuffer; - PFN_vkDestroyBuffer vkDestroyBuffer; - PFN_vkCreateImage vkCreateImage; - PFN_vkDestroyImage vkDestroyImage; - PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR; - PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR; +typedef struct VmaVulkanFunctions +{ + /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS. + PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr; + /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS. + PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr; + PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties; + PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties; + PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory; + PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory; + PFN_vkMapMemory VMA_NULLABLE vkMapMemory; + PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory; + PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges; + PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges; + PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory; + PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory; + PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements; + PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements; + PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer; + PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer; + PFN_vkCreateImage VMA_NULLABLE vkCreateImage; + PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage; + PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer; +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension. + PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR; + /// Fetch "vkGetImageMemoryRequirements 2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension. + PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR; +#endif +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension. + PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR; + /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension. + PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR; +#endif +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR; +#endif } VmaVulkanFunctions; /// Description of a Allocator to be created. typedef struct VmaAllocatorCreateInfo { - /// Flags for created allocator. Use VmaAllocatorCreateFlagBits enum. + /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum. VmaAllocatorCreateFlags flags; /// Vulkan physical device. /** It must be valid throughout whole lifetime of created allocator. */ - VkPhysicalDevice physicalDevice; + VkPhysicalDevice VMA_NOT_NULL physicalDevice; /// Vulkan device. /** It must be valid throughout whole lifetime of created allocator. */ - VkDevice device; - /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps. - /** Set to 0 to use default, which is currently 256 MB. */ + VkDevice VMA_NOT_NULL device; + /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional. + /** Set to 0 to use default, which is currently 256 MiB. */ VkDeviceSize preferredLargeHeapBlockSize; - /// Preferred size of a single `VkDeviceMemory` block to be allocated from small heaps <= 512 MB. - /** Set to 0 to use default, which is currently 64 MB. */ - VkDeviceSize preferredSmallHeapBlockSize; - /// Custom CPU memory allocation callbacks. + /// Custom CPU memory allocation callbacks. Optional. /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */ - const VkAllocationCallbacks* pAllocationCallbacks; - /// Informative callbacks for vkAllocateMemory, vkFreeMemory. + const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks; + /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional. /** Optional, can be null. */ - const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks; - /** \brief Maximum number of additional frames that are in use at the same time as current frame. - - This value is used only when you make allocations with - VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become - lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount. - - For example, if you double-buffer your command buffers, so resources used for - rendering in previous frame may still be in use by the GPU at the moment you - allocate resources needed for the current frame, set this value to 1. - - If you want to allow any allocations other than used in the current frame to - become lost, set this value to 0. - */ - uint32_t frameInUseCount; - /** \brief Either NULL or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap. + const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks; + /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap. If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on @@ -831,78 +996,77 @@ typedef struct VmaAllocatorCreateInfo smaller amount of memory, because graphics driver doesn't necessary fail new allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is exceeded. It may return success and just silently migrate some device memory - blocks to system RAM. + blocks to system RAM. This driver behavior can also be controlled using + VK_AMD_memory_overallocation_behavior extension. */ - const VkDeviceSize* pHeapSizeLimit; - /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`. + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit; - If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section, - you can pass null as this member, because the library will fetch pointers to - Vulkan functions internally in a static way, like: + /** \brief Pointers to Vulkan functions. Can be null. - vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; - - Fill this member if you want to provide your own pointers to Vulkan functions, - e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`. + For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions). */ - const VmaVulkanFunctions* pVulkanFunctions; + const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions; + /** \brief Handle to Vulkan instance object. + + Starting from version 3.0.0 this member is no longer optional, it must be set! + */ + VkInstance VMA_NOT_NULL instance; + /** \brief Optional. The highest version of Vulkan that the application is designed to use. + + It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`. + The patch version number specified is ignored. Only the major and minor versions are considered. + It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`. + Only versions 1.0, 1.1, 1.2 are supported by the current implementation. + Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`. + */ + uint32_t vulkanApiVersion; +#if VMA_EXTERNAL_MEMORY + /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type. + + If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount` + elements, defining external memory handle types of particular Vulkan memory type, + to be passed using `VkExportMemoryAllocateInfoKHR`. + + Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type. + This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL. + */ + const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes; +#endif // #if VMA_EXTERNAL_MEMORY } VmaAllocatorCreateInfo; -/// Creates Allocator object. -VkResult vmaCreateAllocator( - const VmaAllocatorCreateInfo* pCreateInfo, - VmaAllocator* pAllocator); +/// Information about existing #VmaAllocator object. +typedef struct VmaAllocatorInfo +{ + /** \brief Handle to Vulkan instance object. -/// Destroys allocator object. -void vmaDestroyAllocator( - VmaAllocator allocator); + This is the same value as has been passed through VmaAllocatorCreateInfo::instance. + */ + VkInstance VMA_NOT_NULL instance; + /** \brief Handle to Vulkan physical device object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice. + */ + VkPhysicalDevice VMA_NOT_NULL physicalDevice; + /** \brief Handle to Vulkan device object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::device. + */ + VkDevice VMA_NOT_NULL device; +} VmaAllocatorInfo; + +/** @} */ /** -PhysicalDeviceProperties are fetched from physicalDevice by the allocator. -You can access it here, without fetching it again on your own. +\addtogroup group_stats +@{ */ -void vmaGetPhysicalDeviceProperties( - VmaAllocator allocator, - const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties); -/** -PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator. -You can access it here, without fetching it again on your own. -*/ -void vmaGetMemoryProperties( - VmaAllocator allocator, - const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties); - -/** -\brief Given Memory Type Index, returns Property Flags of this memory type. - -This is just a convenience function. Same information can be obtained using -vmaGetMemoryProperties(). -*/ -void vmaGetMemoryTypeProperties( - VmaAllocator allocator, - uint32_t memoryTypeIndex, - VkMemoryPropertyFlags* pFlags); - -/** \brief Sets index of the current frame. - -This function must be used if you make allocations with -`VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT` and -`VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT` flags to inform the allocator -when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot -become lost in the current frame. -*/ -void vmaSetCurrentFrameIndex( - VmaAllocator allocator, - uint32_t frameIndex); - -/** \brief Calculated statistics of memory usage in entire allocator. -*/ +/// Calculated statistics of memory usage in entire allocator. typedef struct VmaStatInfo { /// Number of `VkDeviceMemory` Vulkan memory blocks allocated. uint32_t blockCount; - /// Number of `VmaAllocation` allocation objects allocated. + /// Number of #VmaAllocation allocation objects allocated. uint32_t allocationCount; /// Number of free ranges of memory between allocations. uint32_t unusedRangeCount; @@ -922,160 +1086,68 @@ typedef struct VmaStats VmaStatInfo total; } VmaStats; -/// Retrieves statistics from current state of the Allocator. -void vmaCalculateStats( - VmaAllocator allocator, - VmaStats* pStats); - -#define VMA_STATS_STRING_ENABLED 1 - -#if VMA_STATS_STRING_ENABLED - -/// Builds and returns statistics as string in JSON format. -/** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function. -*/ -void vmaBuildStatsString( - VmaAllocator allocator, - char** ppStatsString, - VkBool32 detailedMap); - -void vmaFreeStatsString( - VmaAllocator allocator, - char* pStatsString); - -#endif // #if VMA_STATS_STRING_ENABLED - -VK_DEFINE_HANDLE(VmaPool) - -typedef enum VmaMemoryUsage +/// Statistics of current memory usage and available budget, in bytes, for specific memory heap. +typedef struct VmaBudget { - /** No intended memory usage specified. - Use other members of VmaAllocationCreateInfo to specify your requirements. + /** \brief Sum size of all `VkDeviceMemory` blocks allocated from particular heap, in bytes. */ - VMA_MEMORY_USAGE_UNKNOWN = 0, - /** Memory will be used on device only, so faster access from the device is preferred. - It usually means device-local GPU memory. - No need to be mappable on host. - Good e.g. for images to be used as attachments, images containing textures to be sampled, - buffers used as vertex buffer, index buffer, uniform buffer and majority of - other types of resources used by device. - You can still do transfers from/to such resource to/from host memory. + VkDeviceSize blockBytes; - The allocation may still end up in `HOST_VISIBLE` memory on some implementations. - In such case, you are free to map it. - You can also use `VMA_ALLOCATION_CREATE_MAPPED_BIT` with this usage type. + /** \brief Sum size of all allocations created in particular heap, in bytes. + + Usually less or equal than `blockBytes`. + Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused - + available for new allocations or wasted due to fragmentation. */ - VMA_MEMORY_USAGE_GPU_ONLY = 1, - /** Memory will be mapped and used on host. - It usually means CPU system memory. - Could be used for transfer to/from device. - Good e.g. for "staging" copy of buffers and images, used as transfer source or destination. - Resources created in this pool may still be accessible to the device, but access to them can be slower. - - Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`. + VkDeviceSize allocationBytes; + + /** \brief Estimated current memory usage of the program, in bytes. + + Fetched from system using `VK_EXT_memory_budget` extension if enabled. + + It might be different than `blockBytes` (usually higher) due to additional implicit objects + also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or + `VkDeviceMemory` blocks allocated outside of this library, if any. */ - VMA_MEMORY_USAGE_CPU_ONLY = 2, - /** Memory will be used for frequent (dynamic) updates from host and reads on device (upload). - Good e.g. for vertex buffers or uniform buffers updated every frame. + VkDeviceSize usage; - Guarantees to be `HOST_VISIBLE`. + /** \brief Estimated amount of memory available to the program, in bytes. + + Fetched from system using `VK_EXT_memory_budget` extension if enabled. + + It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors + external to the program, like other programs also consuming system resources. + Difference `budget - usage` is the amount of additional memory that can probably + be allocated without problems. Exceeding the budget may result in various problems. */ - VMA_MEMORY_USAGE_CPU_TO_GPU = 3, - /** Memory will be used for frequent writing on device and readback on host (download). + VkDeviceSize budget; +} VmaBudget; - Guarantees to be `HOST_VISIBLE`. - */ - VMA_MEMORY_USAGE_GPU_TO_CPU = 4, - VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF -} VmaMemoryUsage; +/** @} */ -/// Flags to be passed as VmaAllocationCreateInfo::flags. -typedef enum VmaAllocationCreateFlagBits { - /** \brief Set this flag if the allocation should have its own memory block. - - Use it for special, big resources, like fullscreen images used as attachments. - - This flag must also be used for host visible resources that you want to map - simultaneously because otherwise they might end up as regions of the same - `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times - simultaneously is illegal. - - You should not use this flag if VmaAllocationCreateInfo::pool is not null. - */ - VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001, - - /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block. - - If new allocation cannot be placed in any of the existing blocks, allocation - fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error. - - You should not use `VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT` and - `VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT` at the same time. It makes no sense. - - If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */ - VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002, - /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. - - Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData. - - Is it valid to use this flag for allocation made from memory type that is not - `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is - useful if you need an allocation that is efficient to use on GPU - (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that - support it (e.g. Intel GPU). - - You should not use this flag together with `VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT`. - */ - VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004, - /** Allocation created with this flag can become lost as a result of another - allocation with `VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT` flag, so you - must check it before use. - - To check if allocation is not lost, call vmaGetAllocationInfo() and check if - VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`. - - For details about supporting lost allocations, see Lost Allocations - chapter of User Guide on Main Page. - - You should not use this flag together with `VMA_ALLOCATION_CREATE_MAPPED_BIT`. - */ - VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008, - /** While creating allocation using this flag, other allocations that were - created with flag `VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT` can become lost. - - For details about supporting lost allocations, see Lost Allocations - chapter of User Guide on Main Page. - */ - VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010, - /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a - null-terminated string. Instead of copying pointer value, a local copy of the - string is made and stored in allocation's pUserData. The string is automatically - freed together with the allocation. It is also used in vmaBuildStatsString(). - */ - VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020, - - VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VmaAllocationCreateFlagBits; -typedef VkFlags VmaAllocationCreateFlags; +/** +\addtogroup group_alloc +@{ +*/ typedef struct VmaAllocationCreateInfo { - /// Use VmaAllocationCreateFlagBits enum. + /// Use #VmaAllocationCreateFlagBits enum. VmaAllocationCreateFlags flags; /** \brief Intended usage of memory. - - You can leave `VMA_MEMORY_USAGE_UNKNOWN` if you specify memory requirements in other way. \n + + You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n If `pool` is not null, this member is ignored. */ VmaMemoryUsage usage; /** \brief Flags that must be set in a Memory Type chosen for an allocation. - + Leave 0 if you specify memory requirements in other way. \n If `pool` is not null, this member is ignored.*/ VkMemoryPropertyFlags requiredFlags; /** \brief Flags that preferably should be set in a memory type chosen for an allocation. - - Set to 0 if no additional flags are prefered. \n + + Set to 0 if no additional flags are preferred. \n If `pool` is not null, this member is ignored. */ VkMemoryPropertyFlags preferredFlags; /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation. @@ -1091,17 +1163,468 @@ typedef struct VmaAllocationCreateInfo Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members: `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored. */ - VmaPool pool; - /** \brief Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). - - If `VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT` is used, it must be either + VmaPool VMA_NULLABLE pool; + /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). + + If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either null or pointer to a null-terminated string. The string will be then copied to internal buffer, so it doesn't need to be valid after allocation call. */ - void* pUserData; + void* VMA_NULLABLE pUserData; + /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. + + It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object + and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + Otherwise, it has the priority of a memory block where it is placed and this variable is ignored. + */ + float priority; } VmaAllocationCreateInfo; +/// Describes parameter of created #VmaPool. +typedef struct VmaPoolCreateInfo +{ + /** \brief Vulkan memory type index to allocate this pool from. + */ + uint32_t memoryTypeIndex; + /** \brief Use combination of #VmaPoolCreateFlagBits. + */ + VmaPoolCreateFlags flags; + /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional. + + Specify nonzero to set explicit, constant size of memory blocks used by this + pool. + + Leave 0 to use default and let the library manage block sizes automatically. + Sizes of particular blocks may vary. + In this case, the pool will also support dedicated allocations. + */ + VkDeviceSize blockSize; + /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty. + + Set to 0 to have no preallocated blocks and allow the pool be completely empty. + */ + size_t minBlockCount; + /** \brief Maximum number of blocks that can be allocated in this pool. Optional. + + Set to 0 to use default, which is `SIZE_MAX`, which means no limit. + + Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated + throughout whole lifetime of this pool. + */ + size_t maxBlockCount; + /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations. + + It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object. + Otherwise, this variable is ignored. + */ + float priority; + /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0. + + Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two. + It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough, + e.g. when doing interop with OpenGL. + */ + VkDeviceSize minAllocationAlignment; + /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional. + + Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`. + It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`. + Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool. + + Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`, + can be attached automatically by this library when using other, more convenient of its features. + */ + void* VMA_NULLABLE pMemoryAllocateNext; +} VmaPoolCreateInfo; + +/** @} */ + /** +\addtogroup group_stats +@{ +*/ + +/// Describes parameter of existing #VmaPool. +typedef struct VmaPoolStats +{ + /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes. + */ + VkDeviceSize size; + /** \brief Total number of bytes in the pool not used by any #VmaAllocation. + */ + VkDeviceSize unusedSize; + /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed. + */ + size_t allocationCount; + /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation. + */ + size_t unusedRangeCount; + /** \brief Number of `VkDeviceMemory` blocks allocated for this pool. + */ + size_t blockCount; +} VmaPoolStats; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/// Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). +typedef struct VmaAllocationInfo +{ + /** \brief Memory type index that this allocation was allocated from. + + It never changes. + */ + uint32_t memoryType; + /** \brief Handle to Vulkan memory object. + + Same memory object can be shared by multiple allocations. + + It can change after call to vmaDefragment() if this allocation is passed to the function. + */ + VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory; + /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation. + + You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function + vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image, + not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation + and apply this offset automatically. + + It can change after call to vmaDefragment() if this allocation is passed to the function. + */ + VkDeviceSize offset; + /** \brief Size of this allocation, in bytes. + + It never changes. + + \note Allocation size returned in this variable may be greater than the size + requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the + allocation is accessible for operations on memory e.g. using a pointer after + mapping with vmaMapMemory(), but operations on the resource e.g. using + `vkCmdCopyBuffer` must be limited to the size of the resource. + */ + VkDeviceSize size; + /** \brief Pointer to the beginning of this allocation as mapped data. + + If the allocation hasn't been mapped using vmaMapMemory() and hasn't been + created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null. + + It can change after call to vmaMapMemory(), vmaUnmapMemory(). + It can also change after call to vmaDefragment() if this allocation is passed to the function. + */ + void* VMA_NULLABLE pMappedData; + /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData(). + + It can change after call to vmaSetAllocationUserData() for this allocation. + */ + void* VMA_NULLABLE pUserData; +} VmaAllocationInfo; + +/** \brief Parameters for defragmentation. + +To be used with function vmaDefragmentationBegin(). +*/ +typedef struct VmaDefragmentationInfo2 +{ + /** \brief Reserved for future use. Should be 0. + */ + VmaDefragmentationFlags flags; + /** \brief Number of allocations in `pAllocations` array. + */ + uint32_t allocationCount; + /** \brief Pointer to array of allocations that can be defragmented. + + The array should have `allocationCount` elements. + The array should not contain nulls. + Elements in the array should be unique - same allocation cannot occur twice. + All allocations not present in this array are considered non-moveable during this defragmentation. + */ + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations; + /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation. + + The array should have `allocationCount` elements. + You can pass null if you are not interested in this information. + */ + VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged; + /** \brief Numer of pools in `pPools` array. + */ + uint32_t poolCount; + /** \brief Either null or pointer to array of pools to be defragmented. + + All the allocations in the specified pools can be moved during defragmentation + and there is no way to check if they were really moved as in `pAllocationsChanged`, + so you must query all the allocations in all these pools for new `VkDeviceMemory` + and offset using vmaGetAllocationInfo() if you might need to recreate buffers + and images bound to them. + + The array should have `poolCount` elements. + The array should not contain nulls. + Elements in the array should be unique - same pool cannot occur twice. + + Using this array is equivalent to specifying all allocations from the pools in `pAllocations`. + It might be more efficient. + */ + const VmaPool VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools; + /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`. + + `VK_WHOLE_SIZE` means no limit. + */ + VkDeviceSize maxCpuBytesToMove; + /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`. + + `UINT32_MAX` means no limit. + */ + uint32_t maxCpuAllocationsToMove; + /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`. + + `VK_WHOLE_SIZE` means no limit. + */ + VkDeviceSize maxGpuBytesToMove; + /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`. + + `UINT32_MAX` means no limit. + */ + uint32_t maxGpuAllocationsToMove; + /** \brief Optional. Command buffer where GPU copy commands will be posted. + + If not null, it must be a valid command buffer handle that supports Transfer queue type. + It must be in the recording state and outside of a render pass instance. + You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd(). + + Passing null means that only CPU defragmentation will be performed. + */ + VkCommandBuffer VMA_NULLABLE commandBuffer; +} VmaDefragmentationInfo2; + +typedef struct VmaDefragmentationPassMoveInfo +{ + VmaAllocation VMA_NOT_NULL allocation; + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory; + VkDeviceSize offset; +} VmaDefragmentationPassMoveInfo; + +/** \brief Parameters for incremental defragmentation steps. + +To be used with function vmaBeginDefragmentationPass(). +*/ +typedef struct VmaDefragmentationPassInfo +{ + uint32_t moveCount; + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves; +} VmaDefragmentationPassInfo; + +/** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment(). + +\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead. +*/ +typedef struct VmaDefragmentationInfo +{ + /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places. + + Default is `VK_WHOLE_SIZE`, which means no limit. + */ + VkDeviceSize maxBytesToMove; + /** \brief Maximum number of allocations that can be moved to different place. + + Default is `UINT32_MAX`, which means no limit. + */ + uint32_t maxAllocationsToMove; +} VmaDefragmentationInfo; + +/// Statistics returned by function vmaDefragment(). +typedef struct VmaDefragmentationStats +{ + /// Total number of bytes that have been copied while moving allocations to different places. + VkDeviceSize bytesMoved; + /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects. + VkDeviceSize bytesFreed; + /// Number of allocations that have been moved to different places. + uint32_t allocationsMoved; + /// Number of empty `VkDeviceMemory` objects that have been released to the system. + uint32_t deviceMemoryBlocksFreed; +} VmaDefragmentationStats; + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock(). +typedef struct VmaVirtualBlockCreateInfo +{ + /** \brief Total size of the virtual block. + + Sizes can be expressed in bytes or any units you want as long as you are consistent in using them. + For example, if you allocate from some array of structures, 1 can mean single instance of entire structure. + */ + VkDeviceSize size; + + /** \brief Use combination of #VmaVirtualBlockCreateFlagBits. + */ + VmaVirtualBlockCreateFlagBits flags; + + /** \brief Custom CPU memory allocation callbacks. Optional. + + Optional, can be null. When specified, they will be used for all CPU-side memory allocations. + */ + const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks; +} VmaVirtualBlockCreateInfo; + +/// Parameters of created virtual allocation to be passed to vmaVirtualAllocate(). +typedef struct VmaVirtualAllocationCreateInfo +{ + /** \brief Size of the allocation. + + Cannot be zero. + */ + VkDeviceSize size; + /** \brief Required alignment of the allocation. Optional. + + Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset. + */ + VkDeviceSize alignment; + /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits. + */ + VmaVirtualAllocationCreateFlags flags; + /** \brief Custom pointer to be associated with the allocation. Optional. + + It can be any value and can be used for user-defined purposes. It can be fetched or changed later. + */ + void* VMA_NULLABLE pUserData; +} VmaVirtualAllocationCreateInfo; + +/// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo(). +typedef struct VmaVirtualAllocationInfo +{ + /** \brief Offset of the allocation. + + Offset at which the allocation was made. + */ + VkDeviceSize offset; + /** \brief Size of the allocation. + + Same value as passed in VmaVirtualAllocationCreateInfo::size. + */ + VkDeviceSize size; + /** \brief Custom pointer associated with the allocation. + + Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData(). + */ + void* VMA_NULLABLE pUserData; +} VmaVirtualAllocationInfo; + +/** @} */ + +#endif // _VMA_DATA_TYPES_DECLARATIONS + +#ifndef _VMA_FUNCTION_HEADERS + +/** +\addtogroup group_init +@{ +*/ + +/// Creates #VmaAllocator object. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator( + const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator); + +/// Destroys allocator object. +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator( + VmaAllocator VMA_NULLABLE allocator); + +/** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc. + +It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to +`VkPhysicalDevice`, `VkDevice` etc. every time using this function. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo); + +/** +PhysicalDeviceProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties( + VmaAllocator VMA_NOT_NULL allocator, + const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties); + +/** +PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties); + +/** +\brief Given Memory Type Index, returns Property Flags of this memory type. + +This is just a convenience function. Same information can be obtained using +vmaGetMemoryProperties(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); + +/** \brief Sets index of the current frame. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t frameIndex); + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Retrieves statistics from current state of the Allocator. + +This function is called "calculate" not "get" because it has to traverse all +internal data structures, so it may be quite slow. For faster but more brief statistics +suitable to be called every frame or every allocation, use vmaGetHeapBudgets(). + +Note that when using allocator from multiple threads, returned information may immediately +become outdated. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats( + VmaAllocator VMA_NOT_NULL allocator, + VmaStats* VMA_NOT_NULL pStats); + +/** \brief Retrieves information about current memory budget for all memory heaps. + +\param allocator +\param[out] pBudgets Must point to array with number of elements at least equal to number of memory heaps in physical device used. + +This function is called "get" not "calculate" because it is very fast, suitable to be called +every frame or every allocation. For more detailed statistics use vmaCalculateStats(). + +Note that when using allocator from multiple threads, returned information may immediately +become outdated. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets( + VmaAllocator VMA_NOT_NULL allocator, + VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets); + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** +\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo. + This algorithm tries to find a memory type that: - Is allowed by memoryTypeBits. @@ -1115,276 +1638,277 @@ device doesn't support any memory type with requested features for the specific type of resource you want to use it for. Please check parameters of your resource, like image layout (OPTIMAL versus LINEAR) or mip level count. */ -VkResult vmaFindMemoryTypeIndex( - VmaAllocator allocator, +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex( + VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits, - const VmaAllocationCreateInfo* pAllocationCreateInfo, - uint32_t* pMemoryTypeIndex); + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); -/// Flags to be passed as VmaPoolCreateInfo::flags. -typedef enum VmaPoolCreateFlagBits { - /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored. +/** +\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo. - This is na optional optimization flag. +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy buffer that never has memory bound. +It is just a convenience function, equivalent to calling: - If you always allocate using vmaCreateBuffer(), vmaCreateImage(), - vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator - knows exact type of your allocations so it can handle Buffer-Image Granularity - in the optimal way. - - If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(), - exact type of such allocations is not known, so allocator must be conservative - in handling Buffer-Image Granularity, which can lead to suboptimal allocation - (wasted memory). In that case, if you can make sure you always allocate only - buffers and linear images or only optimal images out of this pool, use this flag - to make allocator disregard Buffer-Image Granularity and so make allocations - more optimal. - */ - VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002, - - VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VmaPoolCreateFlagBits; -typedef VkFlags VmaPoolCreateFlags; - -/** \brief Describes parameter of created `VmaPool`. +- `vkCreateBuffer` +- `vkGetBufferMemoryRequirements` +- `vmaFindMemoryTypeIndex` +- `vkDestroyBuffer` */ -typedef struct VmaPoolCreateInfo { - /** \brief Vulkan memory type index to allocate this pool from. - */ - uint32_t memoryTypeIndex; - /** \brief Use combination of `VmaPoolCreateFlagBits`. - */ - VmaPoolCreateFlags flags; - /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); - Optional. Leave 0 to use default. - */ - VkDeviceSize blockSize; - /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty. +/** +\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo. - Set to 0 to have no preallocated blocks and let the pool be completely empty. - */ - size_t minBlockCount; - /** \brief Maximum number of blocks that can be allocated in this pool. +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy image that never has memory bound. +It is just a convenience function, equivalent to calling: - Optional. Set to 0 to use `SIZE_MAX`, which means no limit. - - Set to same value as minBlockCount to have fixed amount of memory allocated - throuout whole lifetime of this pool. - */ - size_t maxBlockCount; - /** \brief Maximum number of additional frames that are in use at the same time as current frame. - - This value is used only when you make allocations with - `VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT` flag. Such allocation cannot become - lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount. - - For example, if you double-buffer your command buffers, so resources used for - rendering in previous frame may still be in use by the GPU at the moment you - allocate resources needed for the current frame, set this value to 1. - - If you want to allow any allocations other than used in the current frame to - become lost, set this value to 0. - */ - uint32_t frameInUseCount; -} VmaPoolCreateInfo; - -/** \brief Describes parameter of existing `VmaPool`. +- `vkCreateImage` +- `vkGetImageMemoryRequirements` +- `vmaFindMemoryTypeIndex` +- `vkDestroyImage` */ -typedef struct VmaPoolStats { - /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes. - */ - VkDeviceSize size; - /** \brief Total number of bytes in the pool not used by any `VmaAllocation`. - */ - VkDeviceSize unusedSize; - /** \brief Number of VmaAllocation objects created from this pool that were not destroyed or lost. - */ - size_t allocationCount; - /** \brief Number of continuous memory ranges in the pool not used by any `VmaAllocation`. - */ - size_t unusedRangeCount; - /** \brief Size of the largest continuous free memory region. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator VMA_NOT_NULL allocator, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); - Making a new allocation of that size is not guaranteed to succeed because of - possible additional margin required to respect alignment and buffer/image - granularity. - */ - VkDeviceSize unusedRangeSizeMax; -} VmaPoolStats; +/** \brief Allocates Vulkan device memory and creates #VmaPool object. -/** \brief Allocates Vulkan device memory and creates `VmaPool` object. - -@param allocator Allocator object. -@param pCreateInfo Parameters of pool to create. -@param[out] pPool Handle to created pool. +\param allocator Allocator object. +\param pCreateInfo Parameters of pool to create. +\param[out] pPool Handle to created pool. */ -VkResult vmaCreatePool( - VmaAllocator allocator, - const VmaPoolCreateInfo* pCreateInfo, - VmaPool* pPool); +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool( + VmaAllocator VMA_NOT_NULL allocator, + const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool); -/** \brief Destroys VmaPool object and frees Vulkan device memory. +/** \brief Destroys #VmaPool object and frees Vulkan device memory. */ -void vmaDestroyPool( - VmaAllocator allocator, - VmaPool pool); +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NULLABLE pool); -/** \brief Retrieves statistics of existing VmaPool object. +/** @} */ -@param allocator Allocator object. -@param pool Pool object. -@param[out] pPoolStats Statistics of specified pool. +/** +\addtogroup group_stats +@{ */ -void vmaGetPoolStats( - VmaAllocator allocator, - VmaPool pool, - VmaPoolStats* pPoolStats); -/** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now. +/** \brief Retrieves statistics of existing #VmaPool object. -@param allocator Allocator object. -@param pool Pool. -@param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information. +\param allocator Allocator object. +\param pool Pool object. +\param[out] pPoolStats Statistics of specified pool. */ -void vmaMakePoolAllocationsLost( - VmaAllocator allocator, - VmaPool pool, - size_t* pLostAllocationCount); +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + VmaPoolStats* VMA_NOT_NULL pPoolStats); -VK_DEFINE_HANDLE(VmaAllocation) +/** @} */ -/** \brief Parameters of `VmaAllocation` objects, that can be retrieved using function vmaGetAllocationInfo(). +/** +\addtogroup group_alloc +@{ */ -typedef struct VmaAllocationInfo { - /** \brief Memory type index that this allocation was allocated from. - - It never changes. - */ - uint32_t memoryType; - /** \brief Handle to Vulkan memory object. - Same memory object can be shared by multiple allocations. - - It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost. +/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions. - If the allocation is lost, it is equal to `VK_NULL_HANDLE`. - */ - VkDeviceMemory deviceMemory; - /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation. +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). - It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost. - */ - VkDeviceSize offset; - /** \brief Size of this allocation, in bytes. +Possible return values: - It never changes, unless allocation is lost. - */ - VkDeviceSize size; - /** \brief Pointer to the beginning of this allocation as mapped data. +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool); - If the allocation hasn't been mapped using vmaMapMemory() and hasn't been - created with `VMA_ALLOCATION_CREATE_MAPPED_BIT` flag, this value null. +/** \brief Retrieves name of a custom pool. - It can change after call to vmaMapMemory(), vmaUnmapMemory(). - It can also change after call to vmaDefragment() if this allocation is passed to the function. - */ - void* pMappedData; - /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData(). +After the call `ppName` is either null or points to an internally-owned null-terminated string +containing name of the pool that was previously set. The pointer becomes invalid when the pool is +destroyed or its name is changed using vmaSetPoolName(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + const char* VMA_NULLABLE* VMA_NOT_NULL ppName); - It can change after call to vmaSetAllocationUserData() for this allocation. - */ - void* pUserData; -} VmaAllocationInfo; +/** \brief Sets name of a custom pool. + +`pName` can be either null or pointer to a null-terminated string with new name for the pool. +Function makes internal copy of the string, so it can be changed or freed immediately after this call. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + const char* VMA_NULLABLE pName); /** \brief General purpose memory allocation. -@param[out] pAllocation Handle to allocated memory. -@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). +\param allocator +\param pVkMemoryRequirements +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). -You should free the memory using vmaFreeMemory(). +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), vmaCreateBuffer(), vmaCreateImage() instead whenever possible. */ -VkResult vmaAllocateMemory( - VmaAllocator allocator, - const VkMemoryRequirements* pVkMemoryRequirements, - const VmaAllocationCreateInfo* pCreateInfo, - VmaAllocation* pAllocation, - VmaAllocationInfo* pAllocationInfo); +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory( + VmaAllocator VMA_NOT_NULL allocator, + const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief General purpose memory allocation for multiple allocation objects at once. + +\param allocator Allocator object. +\param pVkMemoryRequirements Memory requirements for each allocation. +\param pCreateInfo Creation parameters for each allocation. +\param allocationCount Number of allocations to make. +\param[out] pAllocations Pointer to array that will be filled with handles to created allocations. +\param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations. + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding. +It is just a general purpose allocation function able to make multiple allocations at once. +It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times. + +All allocations are made using same parameters. All of them are created out of the same memory pool and type. +If any allocation fails, all allocations already made within this function call are also freed, so that when +returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages( + VmaAllocator VMA_NOT_NULL allocator, + const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements, + const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo, + size_t allocationCount, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations, + VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo); /** -@param[out] pAllocation Handle to allocated memory. -@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). +\param allocator +\param buffer +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). You should free the memory using vmaFreeMemory(). */ -VkResult vmaAllocateMemoryForBuffer( - VmaAllocator allocator, - VkBuffer buffer, - const VmaAllocationCreateInfo* pCreateInfo, - VmaAllocation* pAllocation, - VmaAllocationInfo* pAllocationInfo); +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); /// Function similar to vmaAllocateMemoryForBuffer(). -VkResult vmaAllocateMemoryForImage( - VmaAllocator allocator, - VkImage image, - const VmaAllocationCreateInfo* pCreateInfo, - VmaAllocation* pAllocation, - VmaAllocationInfo* pAllocationInfo); +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); -/// Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). -void vmaFreeMemory( - VmaAllocator allocator, - VmaAllocation allocation); +/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). -/// Returns current information about specified allocation. -void vmaGetAllocationInfo( - VmaAllocator allocator, - VmaAllocation allocation, - VmaAllocationInfo* pAllocationInfo); +Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory( + VmaAllocator VMA_NOT_NULL allocator, + const VmaAllocation VMA_NULLABLE allocation); + +/** \brief Frees memory and destroys multiple allocations. + +Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding. +It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(), +vmaAllocateMemoryPages() and other functions. +It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times. + +Allocations in `pAllocations` array can come from any memory pools and types. +Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages( + VmaAllocator VMA_NOT_NULL allocator, + size_t allocationCount, + const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations); + +/** \brief Returns current information about specified allocation. + +Current paramteres of given allocation are returned in `pAllocationInfo`. + +Although this function doesn't lock any mutex, so it should be quite efficient, +you should avoid calling it too often. +You can retrieve same VmaAllocationInfo structure while creating your resource, from function +vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change +(e.g. due to defragmentation). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo); /** \brief Sets pUserData in given allocation to new value. If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT, pUserData must be either null, or pointer to a null-terminated string. The function -makes local copy of the string and sets it as allocation's pUserData. String +makes local copy of the string and sets it as allocation's `pUserData`. String passed as pUserData doesn't need to be valid for whole lifetime of the allocation - you can free it after this call. String previously pointed by allocation's pUserData is freed from memory. -If the flag was not used, the value of pointer pUserData is just copied to -allocation's pUserData. It is opaque, so you can use it however you want - e.g. +If the flag was not used, the value of pointer `pUserData` is just copied to +allocation's `pUserData`. It is opaque, so you can use it however you want - e.g. as a pointer, ordinal number or some handle to you own data. */ -void vmaSetAllocationUserData( - VmaAllocator allocator, - VmaAllocation allocation, - void* pUserData); +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE pUserData); -/** \brief Creates new allocation that is in lost state from the beginning. +/** +\brief Given an allocation, returns Property Flags of its memory type. -It can be useful if you need a dummy, non-null allocation. - -You still need to destroy created object using vmaFreeMemory(). - -Returned allocation is not tied to any specific memory pool or memory type and -not bound to any image or buffer. It has size = 0. It cannot be turned into -a real, non-empty allocation. +This is just a convenience function. Same information can be obtained using +vmaGetAllocationInfo() + vmaGetMemoryProperties(). */ -void vmaCreateLostAllocation( - VmaAllocator allocator, - VmaAllocation* pAllocation); +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); /** \brief Maps memory represented by given allocation and returns pointer to it. Maps memory represented by given allocation to make it accessible to CPU code. When succeeded, `*ppData` contains pointer to first byte of this memory. -If the allocation is part of bigger `VkDeviceMemory` block, the pointer is -correctly offseted to the beginning of region assigned to this particular -allocation. + +\warning +If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is +correctly offsetted to the beginning of region assigned to this particular allocation. +Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block. +You should not add VmaAllocationInfo::offset to it! Mapping is internally reference-counted and synchronized, so despite raw Vulkan function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory` @@ -1400,145 +1924,335 @@ It also safe to call this function multiple times on the same allocation. You must call vmaUnmapMemory() same number of times as you called vmaMapMemory(). It is also safe to call this function on allocation created with -`VMA_ALLOCATION_CREATE_MAPPED_BIT` flag. Its memory stays mapped all the time. +#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time. You must still call vmaUnmapMemory() same number of times as you called vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the -"0-th" mapping made automatically due to `VMA_ALLOCATION_CREATE_MAPPED_BIT` flag. +"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. This function fails when used on allocation made in memory type that is not `HOST_VISIBLE`. -This function always fails when called for allocation that was created with -`VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT` flag. Such allocations cannot be -mapped. +This function doesn't automatically flush or invalidate caches. +If the allocation is made from a memory types that is not `HOST_COHERENT`, +you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification. */ -VkResult vmaMapMemory( - VmaAllocator allocator, - VmaAllocation allocation, - void** ppData); +VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE* VMA_NOT_NULL ppData); /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory(). For details, see description of vmaMapMemory(). + +This function doesn't automatically flush or invalidate caches. +If the allocation is made from a memory types that is not `HOST_COHERENT`, +you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification. */ -void vmaUnmapMemory( - VmaAllocator allocator, - VmaAllocation allocation); +VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation); -/** \brief Optional configuration parameters to be passed to function vmaDefragment(). */ -typedef struct VmaDefragmentationInfo { - /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places. - - Default is `VK_WHOLE_SIZE`, which means no limit. - */ - VkDeviceSize maxBytesToMove; - /** \brief Maximum number of allocations that can be moved to different place. +/** \brief Flushes memory of given allocation. - Default is `UINT32_MAX`, which means no limit. - */ - uint32_t maxAllocationsToMove; -} VmaDefragmentationInfo; +Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation. +It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`. +Unmap operation doesn't do that automatically. -/** \brief Statistics returned by function vmaDefragment(). */ -typedef struct VmaDefragmentationStats { - /// Total number of bytes that have been copied while moving allocations to different places. - VkDeviceSize bytesMoved; - /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects. - VkDeviceSize bytesFreed; - /// Number of allocations that have been moved to different places. - uint32_t allocationsMoved; - /// Number of empty `VkDeviceMemory` objects that have been released to the system. - uint32_t deviceMemoryBlocksFreed; -} VmaDefragmentationStats; +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. -/** \brief Compacts memory by moving allocations. +Warning! `offset` and `size` are relative to the contents of given `allocation`. +If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively. +Do not pass allocation's offset as `offset`!!! -@param pAllocations Array of allocations that can be moved during this compation. -@param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays. -@param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information. -@param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values. -@param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information. -@return VK_SUCCESS if completed, VK_INCOMPLETE if succeeded but didn't make all possible optimizations because limits specified in pDefragmentationInfo have been reached, negative error code in case of error. +This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize offset, + VkDeviceSize size); + +/** \brief Invalidates memory of given allocation. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation. +It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`. +Map operation doesn't do that automatically. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. + +Warning! `offset` and `size` are relative to the contents of given `allocation`. +If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively. +Do not pass allocation's offset as `offset`!!! + +This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if +it is called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize offset, + VkDeviceSize size); + +/** \brief Flushes memory of given set of allocations. + +Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations. +For more information, see documentation of vmaFlushAllocation(). + +\param allocator +\param allocationCount +\param allocations +\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero. +\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations. + +This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t allocationCount, + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes); + +/** \brief Invalidates memory of given set of allocations. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations. +For more information, see documentation of vmaInvalidateAllocation(). + +\param allocator +\param allocationCount +\param allocations +\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero. +\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations. + +This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t allocationCount, + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes); + +/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions. + +\param allocator +\param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeBits); + +/** \brief Begins defragmentation process. + +\param allocator Allocator object. +\param pInfo Structure filled with parameters of defragmentation. +\param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information. +\param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation. +\return `VK_SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `VK_NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error. + +Use this function instead of old, deprecated vmaDefragment(). + +Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd(): + +- You should not use any of allocations passed as `pInfo->pAllocations` or + any allocations that belong to pools passed as `pInfo->pPools`, + including calling vmaGetAllocationInfo(), or access + their data. +- Some mutexes protecting internal data structures may be locked, so trying to + make or free any allocations, bind buffers or images, map memory, or launch + another simultaneous defragmentation in between may cause stall (when done on + another thread) or deadlock (when done on the same thread), unless you are + 100% sure that defragmented allocations are in different pools. +- Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined. + They become valid after call to vmaDefragmentationEnd(). +- If `pInfo->commandBuffer` is not null, you must submit that command buffer + and make sure it finished execution before calling vmaDefragmentationEnd(). + +For more information and important limitations regarding defragmentation, see documentation chapter: +[Defragmentation](@ref defragmentation). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin( + VmaAllocator VMA_NOT_NULL allocator, + const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo, + VmaDefragmentationStats* VMA_NULLABLE pStats, + VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext); + +/** \brief Ends defragmentation process. + +Use this function to finish defragmentation started by vmaDefragmentationBegin(). +It is safe to pass `context == null`. The function then does nothing. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NULLABLE context); + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NULLABLE context, + VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo); + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NULLABLE context); + +/** \brief Deprecated. Compacts memory by moving allocations. + +\param allocator +\param pAllocations Array of allocations that can be moved during this compation. +\param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays. +\param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information. +\param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values. +\param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information. +\return `VK_SUCCESS` if completed, negative error code in case of error. + +\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead. This function works by moving allocations to different places (different `VkDeviceMemory` objects and/or different offsets) in order to optimize memory -usage. Only allocations that are in pAllocations array can be moved. All other +usage. Only allocations that are in `pAllocations` array can be moved. All other allocations are considered nonmovable in this call. Basic rules: - Only allocations made in memory types that have - `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag can be compacted. You may pass other - allocations but it makes no sense - these will never be moved. -- You may pass allocations made with `VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT` but - it makes no sense - they will never be moved. -- Both allocations made with or without `VMA_ALLOCATION_CREATE_MAPPED_BIT` + `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` + flags can be compacted. You may pass other allocations but it makes no sense - + these will never be moved. +- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or + #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations + passed to this function that come from such pools are ignored. +- Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or + created as dedicated allocations for any other reason are also ignored. +- Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT flag can be compacted. If not persistently mapped, memory will be mapped temporarily inside this function if needed. -- You must not pass same `VmaAllocation` object multiple times in pAllocations array. +- You must not pass same #VmaAllocation object multiple times in `pAllocations` array. The function also frees empty `VkDeviceMemory` blocks. -After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or -VmaAllocationInfo::offset changes. You must query them again using -vmaGetAllocationInfo() if you need them. +Warning: This function may be time-consuming, so you shouldn't call it too often +(like after every resource creation/destruction). +You can call it on special occasions (like when reloading a game level or +when you just destroyed a lot of objects). Calling it every frame may be OK, but +you should measure that on your platform. -If an allocation has been moved, data in memory is copied to new place -automatically, but if it was bound to a buffer or an image, you must destroy -that object yourself, create new one and bind it to the new memory pointed by -the allocation. You must use `vkDestroyBuffer()`, `vkDestroyImage()`, -`vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(), -vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage()! Example: - -\code -VkDevice device = ...; -VmaAllocator allocator = ...; -std::vector buffers = ...; -std::vector allocations = ...; - -std::vector allocationsChanged(allocations.size()); -vmaDefragment(allocator, allocations.data(), allocations.size(), allocationsChanged.data(), nullptr, nullptr); - -for(size_t i = 0; i < allocations.size(); ++i) -{ - if(allocationsChanged[i]) - { - VmaAllocationInfo allocInfo; - vmaGetAllocationInfo(allocator, allocations[i], &allocInfo); - - vkDestroyBuffer(device, buffers[i], nullptr); - - VkBufferCreateInfo bufferInfo = ...; - vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]); - - // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning. - - vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset); - } -} -\endcode - -Warning! This function is not correct according to Vulkan specification. Use it -at your own risk. That's becuase Vulkan doesn't guarantee that memory -requirements (size and alignment) for a new buffer or image are consistent. They -may be different even for subsequent calls with the same parameters. It really -does happen on some platforms, especially with images. - -This function may be time-consuming, so you shouldn't call it too often (like -every frame or after every resource creation/destruction), but rater you can -call it on special occasions (like when reloading a game level, when you just -destroyed a lot of objects). +For more information, see [Defragmentation](@ref defragmentation) chapter. */ -VkResult vmaDefragment( - VmaAllocator allocator, - VmaAllocation* pAllocations, +VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment( + VmaAllocator VMA_NOT_NULL allocator, + const VmaAllocation VMA_NOT_NULL* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations, size_t allocationCount, - VkBool32* pAllocationsChanged, - const VmaDefragmentationInfo *pDefragmentationInfo, - VmaDefragmentationStats* pDefragmentationStats); + VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged, + const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo, + VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats); + +/** \brief Binds buffer to allocation. + +Binds specified buffer to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create a buffer, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindBufferMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateBuffer() instead of this one. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer); + +/** \brief Binds buffer to allocation with additional parameters. + +\param allocator +\param allocation +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. +\param buffer +\param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null. + +This function is similar to vmaBindBufferMemory(), but it provides additional parameters. + +If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag +or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer, + const void* VMA_NULLABLE pNext); + +/** \brief Binds image to allocation. + +Binds specified image to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create an image, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindImageMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateImage() instead of this one. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image); + +/** \brief Binds image to allocation with additional parameters. + +\param allocator +\param allocation +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. +\param image +\param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null. + +This function is similar to vmaBindImageMemory(), but it provides additional parameters. + +If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag +or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image, + const void* VMA_NULLABLE pNext); /** -@param[out] pBuffer Buffer that was created. -@param[out] pAllocation Allocation that was created. -@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). +\param allocator +\param pBufferCreateInfo +\param pAllocationCreateInfo +\param[out] pBuffer Buffer that was created. +\param[out] pAllocation Allocation that was created. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). This function automatically: @@ -1553,21 +2267,40 @@ If the function succeeded, you must destroy both buffer and allocation when you no longer need them using either convenience function vmaDestroyBuffer() or separately, using `vkDestroyBuffer()` and vmaFreeMemory(). -If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used, +If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used, VK_KHR_dedicated_allocation extension is used internally to query driver whether it requires or prefers the new buffer to have dedicated allocation. If yes, and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null -and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated +and #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated allocation for this buffer, just like when using -VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +#VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + +\note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer, +although recommended as a good practice, is out of scope of this library and could be implemented +by the user as a higher-level logic on top of VMA. */ -VkResult vmaCreateBuffer( - VmaAllocator allocator, - const VkBufferCreateInfo* pBufferCreateInfo, - const VmaAllocationCreateInfo* pAllocationCreateInfo, - VkBuffer* pBuffer, - VmaAllocation* pAllocation, - VmaAllocationInfo* pAllocationInfo); +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Creates a buffer with additional minimum alignment. + +Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom, +minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g. +for interop with OpenGL. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); /** \brief Destroys Vulkan buffer and frees allocated memory. @@ -1580,19 +2313,19 @@ vmaFreeMemory(allocator, allocation); It it safe to pass null as buffer and/or allocation. */ -void vmaDestroyBuffer( - VmaAllocator allocator, - VkBuffer buffer, - VmaAllocation allocation); +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer, + VmaAllocation VMA_NULLABLE allocation); /// Function similar to vmaCreateBuffer(). -VkResult vmaCreateImage( - VmaAllocator allocator, - const VkImageCreateInfo* pImageCreateInfo, - const VmaAllocationCreateInfo* pAllocationCreateInfo, - VkImage* pImage, - VmaAllocation* pAllocation, - VmaAllocationInfo* pAllocationInfo); +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage( + VmaAllocator VMA_NOT_NULL allocator, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); /** \brief Destroys Vulkan image and frees allocated memory. @@ -1605,10 +2338,142 @@ vmaFreeMemory(allocator, allocation); It it safe to pass null as image and/or allocation. */ -void vmaDestroyImage( - VmaAllocator allocator, - VkImage image, - VmaAllocation allocation); +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NULLABLE_NON_DISPATCHABLE image, + VmaAllocation VMA_NULLABLE allocation); + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \brief Creates new #VmaVirtualBlock object. + +\param pCreateInfo Parameters for creation. +\param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( + const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock); + +/** \brief Destroys #VmaVirtualBlock object. + +Please note that you should consciously handle virtual allocations that could remain unfreed in the block. +You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock() +if you are sure this is what you want. If you do neither, an assert is called. + +If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`, +don't forget to free them. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock( + VmaVirtualBlock VMA_NULLABLE virtualBlock); + +/** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations. +*/ +VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty( + VmaVirtualBlock VMA_NOT_NULL virtualBlock); + +/** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo); + +/** \brief Allocates new virtual allocation inside given #VmaVirtualBlock. + +There is no handle type for a virtual allocation. +Virtual allocations within a specific virtual block are uniquely identified by their offsets. + +If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned +(despite the function doesn't ever allocate actual GPU memory). + +\param virtualBlock Virtual block +\param pCreateInfo Parameters for the allocation +\param[out] pAllocation Returned handle of the new allocation +\param[out] pOffset Returned offset of the new allocation. Optional, can be null. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualAllocation* VMA_NOT_NULL pAllocation, + VkDeviceSize* VMA_NULLABLE pOffset); + +/** \brief Frees virtual allocation inside given #VmaVirtualBlock. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NULLABLE allocation); + +/** \brief Frees all virtual allocations inside given #VmaVirtualBlock. + +You must either call this function or free each virtual allocation individually with vmaVirtualFree() +before destroying a virtual block. Otherwise, an assert is called. + +If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`, +don't forget to free it as well. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock( + VmaVirtualBlock VMA_NOT_NULL virtualBlock); + +/** \brief Changes custom pointer associated with given virtual allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE pUserData); + +/** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStats( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaStatInfo* VMA_NOT_NULL pStatInfo); + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock. +\param virtualBlock Virtual block. +\param[out] ppStatsString Returned string. +\param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStats(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces. + +Returned string must be freed using vmaFreeVirtualBlockStatsString(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString, + VkBool32 detailedMap); + +/// Frees a string returned by vmaBuildVirtualBlockStatsString(). +VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE pStatsString); + +#if VMA_STATS_STRING_ENABLED +/** \brief Builds and returns statistics as a null-terminated string in JSON format. +\param allocator +\param[out] ppStatsString Must be freed using vmaFreeStatsString() function. +\param detailedMap +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString( + VmaAllocator VMA_NOT_NULL allocator, + char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString, + VkBool32 detailedMap); + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString( + VmaAllocator VMA_NOT_NULL allocator, + char* VMA_NULLABLE pStatsString); +#endif // VMA_STATS_STRING_ENABLED + +/** @} */ + +#endif // _VMA_FUNCTION_HEADERS #ifdef __cplusplus } @@ -1616,8 +2481,16 @@ void vmaDestroyImage( #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + // For Visual Studio IntelliSense. -#ifdef __INTELLISENSE__ +#if defined(__cplusplus) && defined(__INTELLISENSE__) #define VMA_IMPLEMENTATION #endif @@ -1627,6 +2500,7 @@ void vmaDestroyImage( #include #include #include +#include /******************************************************************************* CONFIGURATION SECTION @@ -1634,45 +2508,43 @@ CONFIGURATION SECTION Define some of these macros before each #include of this header or change them here if you need other then default behavior depending on your environment. */ +#ifndef _VMA_CONFIGURATION /* Define this macro to 1 to make the library fetch pointers to Vulkan functions internally, like: vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; - -Define to 0 if you are going to provide you own pointers to Vulkan functions via -VmaAllocatorCreateInfo::pVulkanFunctions. */ -#ifndef VMA_STATIC_VULKAN_FUNCTIONS -#define VMA_STATIC_VULKAN_FUNCTIONS 1 +#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES) + #define VMA_STATIC_VULKAN_FUNCTIONS 1 #endif -// Define this macro to 1 to make the library use STL containers instead of its own implementation. -//#define VMA_USE_STL_CONTAINERS 1 +/* +Define this macro to 1 to make the library fetch pointers to Vulkan functions +internally, like: -/* Set this macro to 1 to make the library including and using STL containers: -std::pair, std::vector, std::list, std::unordered_map. + vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory"); -Set it to 0 or undefined to make the library using its own implementation of -the containers. +To use this feature in new versions of VMA you now have to pass +VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as +VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null. */ -#if VMA_USE_STL_CONTAINERS - #define VMA_USE_STL_VECTOR 1 - #define VMA_USE_STL_UNORDERED_MAP 1 - #define VMA_USE_STL_LIST 1 +#if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS) + #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 #endif -#if VMA_USE_STL_VECTOR - #include -#endif - -#if VMA_USE_STL_UNORDERED_MAP - #include -#endif - -#if VMA_USE_STL_LIST - #include +#ifndef VMA_USE_STL_SHARED_MUTEX + // Compiler conforms to C++17. + #if __cplusplus >= 201703L + #define VMA_USE_STL_SHARED_MUTEX 1 + // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus + // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2. + #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L + #define VMA_USE_STL_SHARED_MUTEX 1 + #else + #define VMA_USE_STL_SHARED_MUTEX 0 + #endif #endif /* @@ -1681,63 +2553,141 @@ remove them if not needed. */ #include // for assert #include // for min, max -#include // for std::mutex -#include // for std::atomic +#include -#if !defined(_WIN32) - #include // for aligned_alloc() +#ifndef VMA_NULL + // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. + #define VMA_NULL nullptr #endif +#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16) +#include +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + return memalign(alignment, size); +} +#elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC)) +#include + +#if defined(__APPLE__) +#include +#endif + +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4) + // Therefore, for now disable this specific exception until a proper solution is found. + //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0)) + //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0 + // // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only + // // with the MacOSX11.0 SDK in Xcode 12 (which is what adds + // // MAC_OS_X_VERSION_10_16), even though the function is marked + // // availabe for 10.15. That is why the preprocessor checks for 10.16 but + // // the __builtin_available checks for 10.15. + // // People who use C++17 could call aligned_alloc with the 10.15 SDK already. + // if (__builtin_available(macOS 10.15, iOS 13, *)) + // return aligned_alloc(alignment, size); + //#endif + //#endif + + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + void *pointer; + if(posix_memalign(&pointer, alignment, size) == 0) + return pointer; + return VMA_NULL; +} +#elif defined(_WIN32) +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + return _aligned_malloc(size, alignment); +} +#else +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} +#endif + +#if defined(_WIN32) +static void vma_aligned_free(void* ptr) +{ + _aligned_free(ptr); +} +#else +static void vma_aligned_free(void* VMA_NULLABLE ptr) +{ + free(ptr); +} +#endif + +// If your compiler is not compatible with C++11 and definition of +// aligned_alloc() function is missing, uncommeting following line may help: + +//#include + // Normal assert to check for programmer's errors, especially in Debug configuration. #ifndef VMA_ASSERT - #ifdef _DEBUG - #define VMA_ASSERT(expr) assert(expr) - #else + #ifdef NDEBUG #define VMA_ASSERT(expr) + #else + #define VMA_ASSERT(expr) assert(expr) #endif #endif // Assert that will be called very often, like inside data structures e.g. operator[]. // Making it non-empty can make program slow. #ifndef VMA_HEAVY_ASSERT - #ifdef _DEBUG - #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) - #else + #ifdef NDEBUG #define VMA_HEAVY_ASSERT(expr) + #else + #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) #endif #endif -#ifndef VMA_NULL - // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. - #define VMA_NULL nullptr -#endif - #ifndef VMA_ALIGN_OF #define VMA_ALIGN_OF(type) (__alignof(type)) #endif #ifndef VMA_SYSTEM_ALIGNED_MALLOC - #if defined(_WIN32) - #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment))) - #else - #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) )) - #endif + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size)) #endif -#ifndef VMA_SYSTEM_FREE - #if defined(_WIN32) - #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr) +#ifndef VMA_SYSTEM_ALIGNED_FREE + // VMA_SYSTEM_FREE is the old name, but might have been defined by the user + #if defined(VMA_SYSTEM_FREE) + #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr) #else - #define VMA_SYSTEM_FREE(ptr) free(ptr) - #endif + #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr) + #endif +#endif + +#ifndef VMA_BITSCAN_LSB + // Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX + #define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask) +#endif + +#ifndef VMA_BITSCAN_MSB + // Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX + #define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask) #endif #ifndef VMA_MIN - #define VMA_MIN(v1, v2) (std::min((v1), (v2))) + #define VMA_MIN(v1, v2) ((std::min)((v1), (v2))) #endif #ifndef VMA_MAX - #define VMA_MAX(v1, v2) (std::max((v1), (v2))) + #define VMA_MAX(v1, v2) ((std::max)((v1), (v2))) #endif #ifndef VMA_SWAP @@ -1760,171 +2710,564 @@ remove them if not needed. // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString. #if VMA_STATS_STRING_ENABLED - static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num) - { - snprintf(outStr, strLen, "%u", static_cast(num)); - } - static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num) - { - snprintf(outStr, strLen, "%llu", static_cast(num)); - } - static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr) - { - snprintf(outStr, strLen, "%p", ptr); - } + static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num) + { + snprintf(outStr, strLen, "%u", static_cast(num)); + } + static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num) + { + snprintf(outStr, strLen, "%llu", static_cast(num)); + } + static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr) + { + snprintf(outStr, strLen, "%p", ptr); + } #endif #ifndef VMA_MUTEX - class VmaMutex - { - public: - VmaMutex() { } - ~VmaMutex() { } - void Lock() { m_Mutex.lock(); } - void Unlock() { m_Mutex.unlock(); } - private: - std::mutex m_Mutex; - }; - #define VMA_MUTEX VmaMutex + class VmaMutex + { + public: + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + bool TryLock() { return m_Mutex.try_lock(); } + private: + std::mutex m_Mutex; + }; + #define VMA_MUTEX VmaMutex #endif +// Read-write mutex, where "read" is shared access, "write" is exclusive access. +#ifndef VMA_RW_MUTEX + #if VMA_USE_STL_SHARED_MUTEX + // Use std::shared_mutex from C++17. + #include + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.lock_shared(); } + void UnlockRead() { m_Mutex.unlock_shared(); } + bool TryLockRead() { return m_Mutex.try_lock_shared(); } + void LockWrite() { m_Mutex.lock(); } + void UnlockWrite() { m_Mutex.unlock(); } + bool TryLockWrite() { return m_Mutex.try_lock(); } + private: + std::shared_mutex m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600 + // Use SRWLOCK from WinAPI. + // Minimum supported client = Windows Vista, server = Windows Server 2008. + class VmaRWMutex + { + public: + VmaRWMutex() { InitializeSRWLock(&m_Lock); } + void LockRead() { AcquireSRWLockShared(&m_Lock); } + void UnlockRead() { ReleaseSRWLockShared(&m_Lock); } + bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; } + void LockWrite() { AcquireSRWLockExclusive(&m_Lock); } + void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); } + bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; } + private: + SRWLOCK m_Lock; + }; + #define VMA_RW_MUTEX VmaRWMutex + #else + // Less efficient fallback: Use normal mutex. + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.Lock(); } + void UnlockRead() { m_Mutex.Unlock(); } + bool TryLockRead() { return m_Mutex.TryLock(); } + void LockWrite() { m_Mutex.Lock(); } + void UnlockWrite() { m_Mutex.Unlock(); } + bool TryLockWrite() { return m_Mutex.TryLock(); } + private: + VMA_MUTEX m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #endif // #if VMA_USE_STL_SHARED_MUTEX +#endif // #ifndef VMA_RW_MUTEX + /* -If providing your own implementation, you need to implement a subset of std::atomic: - -- Constructor(uint32_t desired) -- uint32_t load() const -- void store(uint32_t desired) -- bool compare_exchange_weak(uint32_t& expected, uint32_t desired) +If providing your own implementation, you need to implement a subset of std::atomic. */ #ifndef VMA_ATOMIC_UINT32 - #define VMA_ATOMIC_UINT32 std::atomic + #include + #define VMA_ATOMIC_UINT32 std::atomic #endif -#ifndef VMA_BEST_FIT - /** - Main parameter for function assessing how good is a free suballocation for a new - allocation request. - - - Set to 1 to use Best-Fit algorithm - prefer smaller blocks, as close to the - size of requested allocations as possible. - - Set to 0 to use Worst-Fit algorithm - prefer larger blocks, as large as - possible. - - Experiments in special testing environment showed that Best-Fit algorithm is - better. - */ - #define VMA_BEST_FIT (1) +#ifndef VMA_ATOMIC_UINT64 + #include + #define VMA_ATOMIC_UINT64 std::atomic #endif #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY - /** - Every allocation will have its own memory block. - Define to 1 for debugging purposes only. - */ - #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0) + /** + Every allocation will have its own memory block. + Define to 1 for debugging purposes only. + */ + #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0) #endif -#ifndef VMA_DEBUG_ALIGNMENT - /** - Minimum alignment of all suballocations, in bytes. - Set to more than 1 for debugging purposes only. Must be power of two. - */ - #define VMA_DEBUG_ALIGNMENT (1) +#ifndef VMA_MIN_ALIGNMENT + /** + Minimum alignment of all allocations, in bytes. + Set to more than 1 for debugging purposes. Must be power of two. + */ + #ifdef VMA_DEBUG_ALIGNMENT // Old name + #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT + #else + #define VMA_MIN_ALIGNMENT (1) + #endif #endif #ifndef VMA_DEBUG_MARGIN - /** - Minimum margin between suballocations, in bytes. - Set nonzero for debugging purposes only. - */ - #define VMA_DEBUG_MARGIN (0) + /** + Minimum margin before and after every allocation, in bytes. + Set nonzero for debugging purposes only. + */ + #define VMA_DEBUG_MARGIN (0) +#endif + +#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS + /** + Define this macro to 1 to automatically fill new allocations and destroyed + allocations with some bit pattern. + */ + #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0) +#endif + +#ifndef VMA_DEBUG_DETECT_CORRUPTION + /** + Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to + enable writing magic value to the margin before and after every allocation and + validating it, so that memory corruptions (out-of-bounds writes) are detected. + */ + #define VMA_DEBUG_DETECT_CORRUPTION (0) #endif #ifndef VMA_DEBUG_GLOBAL_MUTEX - /** - Set this to 1 for debugging purposes only, to enable single mutex protecting all - entry calls to the library. Can be useful for debugging multithreading issues. - */ - #define VMA_DEBUG_GLOBAL_MUTEX (0) + /** + Set this to 1 for debugging purposes only, to enable single mutex protecting all + entry calls to the library. Can be useful for debugging multithreading issues. + */ + #define VMA_DEBUG_GLOBAL_MUTEX (0) #endif #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY - /** - Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity. - Set to more than 1 for debugging purposes only. Must be power of two. - */ - #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1) + /** + Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity. + Set to more than 1 for debugging purposes only. Must be power of two. + */ + #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1) +#endif + +#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + /* + Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount + and return error instead of leaving up to Vulkan implementation what to do in such cases. + */ + #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0) #endif #ifndef VMA_SMALL_HEAP_MAX_SIZE /// Maximum size of a memory heap in Vulkan to consider it "small". - #define VMA_SMALL_HEAP_MAX_SIZE (512 * 1024 * 1024) + #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024) #endif #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE /// Default size of a block allocated as single VkDeviceMemory from a "large" heap. - #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256 * 1024 * 1024) + #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024) #endif -#ifndef VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE - /// Default size of a block allocated as single VkDeviceMemory from a "small" heap. - #define VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE (64 * 1024 * 1024) +#ifndef VMA_CLASS_NO_COPY + #define VMA_CLASS_NO_COPY(className) \ + private: \ + className(const className&) = delete; \ + className& operator=(const className&) = delete; #endif -static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX; +#define VMA_VALIDATE(cond) do { if(!(cond)) { \ + VMA_ASSERT(0 && "Validation failed: " #cond); \ + return false; \ + } } while(false) /******************************************************************************* END OF CONFIGURATION */ +#endif // _VMA_CONFIGURATION -static VkAllocationCallbacks VmaEmptyAllocationCallbacks = { - VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC; +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF; +// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F. +static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666; + +// Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants. +static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040; +static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080; +static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000; +static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u; +static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32; +static const uint32_t VMA_VENDOR_ID_AMD = 4098; + + +#if VMA_STATS_STRING_ENABLED +// Correspond to values of enum VmaSuballocationType. +static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = +{ + "FREE", + "UNKNOWN", + "BUFFER", + "IMAGE_UNKNOWN", + "IMAGE_LINEAR", + "IMAGE_OPTIMAL", +}; +#endif + +static VkAllocationCallbacks VmaEmptyAllocationCallbacks = + { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; + + +#ifndef _VMA_ENUM_DECLARATIONS + +enum VmaSuballocationType +{ + VMA_SUBALLOCATION_TYPE_FREE = 0, + VMA_SUBALLOCATION_TYPE_UNKNOWN = 1, + VMA_SUBALLOCATION_TYPE_BUFFER = 2, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3, + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4, + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5, + VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF +}; + +enum VMA_CACHE_OPERATION +{ + VMA_CACHE_FLUSH, + VMA_CACHE_INVALIDATE +}; + +enum class VmaAllocationRequestType +{ + Normal, + TLSF, + // Used by "Linear" algorithm. + UpperAddress, + EndOf1st, + EndOf2nd, +}; + +#endif // _VMA_ENUM_DECLARATIONS + +#ifndef _VMA_FORWARD_DECLARATIONS +// Opaque handle used by allocation algorithms to identify single allocation in any conforming way. +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle); + +struct VmaMutexLock; +struct VmaMutexLockRead; +struct VmaMutexLockWrite; + +template +struct AtomicTransactionalIncrement; + +template +struct VmaStlAllocator; + +template +class VmaVector; + +template +class VmaSmallVector; + +template +class VmaPoolAllocator; + +template +struct VmaListItem; + +template +class VmaRawList; + +template +class VmaList; + +template +class VmaIntrusiveLinkedList; + +// Unused in this version +#if 0 +template +struct VmaPair; +template +struct VmaPairFirstLess; + +template +class VmaMap; +#endif + +#if VMA_STATS_STRING_ENABLED +class VmaStringBuilder; +class VmaJsonWriter; +#endif + +class VmaDeviceMemoryBlock; + +struct VmaDedicatedAllocationListItemTraits; +class VmaDedicatedAllocationList; + +struct VmaSuballocation; +struct VmaSuballocationOffsetLess; +struct VmaSuballocationOffsetGreater; +struct VmaSuballocationItemSizeLess; + +typedef VmaList> VmaSuballocationList; + +struct VmaAllocationRequest; + +class VmaBlockMetadata; +class VmaBlockMetadata_Generic; +class VmaBlockMetadata_Linear; +class VmaBlockMetadata_Buddy; +class VmaBlockMetadata_TLSF; + +class VmaBlockVector; + +struct VmaDefragmentationMove; +class VmaDefragmentationAlgorithm; +class VmaDefragmentationAlgorithm_Generic; +class VmaDefragmentationAlgorithm_Fast; + +struct VmaPoolListItemTraits; + +struct VmaBlockDefragmentationContext; +class VmaBlockVectorDefragmentationContext; + +struct VmaCurrentBudgetData; + +class VmaAllocationObjectAllocator; + +#endif // _VMA_FORWARD_DECLARATIONS + + +#ifndef _VMA_FUNCTIONS // Returns number of bits set to 1 in (v). static inline uint32_t VmaCountBitsSet(uint32_t v) { - uint32_t c = v - ((v >> 1) & 0x55555555); - c = ((c >> 2) & 0x33333333) + (c & 0x33333333); - c = ((c >> 4) + c) & 0x0F0F0F0F; - c = ((c >> 8) + c) & 0x00FF00FF; - c = ((c >> 16) + c) & 0x0000FFFF; - return c; + uint32_t c = v - ((v >> 1) & 0x55555555); + c = ((c >> 2) & 0x33333333) + (c & 0x33333333); + c = ((c >> 4) + c) & 0x0F0F0F0F; + c = ((c >> 8) + c) & 0x00FF00FF; + c = ((c >> 16) + c) & 0x0000FFFF; + return c; +} + +static inline uint8_t VmaBitScanLSB(uint64_t mask) +{ +#ifdef _MSC_VER + DWORD pos; + if (_BitScanForward64(&pos, mask)) + return static_cast(pos); +#else + uint8_t pos = 0; + do + { + if (mask & (1ULL << pos)) + return pos; + } while (pos++ < 63); +#endif + return UINT8_MAX; +} + +static inline uint8_t VmaBitScanLSB(uint32_t mask) +{ +#ifdef _MSC_VER + DWORD pos; + if (_BitScanForward(&pos, mask)) + return static_cast(pos); +#else + uint8_t pos = 0; + do + { + if (mask & (1UL << pos)) + return pos; + } while (pos++ < 31); +#endif + return UINT8_MAX; +} + +static inline uint8_t VmaBitScanMSB(uint64_t mask) +{ +#ifdef _MSC_VER + DWORD pos; + if (_BitScanReverse64(&pos, mask)) + return static_cast(pos); +#else + uint8_t pos = 63; + do + { + if (mask & (1ULL << pos)) + return pos; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +static inline uint8_t VmaBitScanMSB(uint32_t mask) +{ +#ifdef _MSC_VER + DWORD pos; + if (_BitScanReverse(&pos, mask)) + return static_cast(pos); +#else + uint8_t pos = 31; + do + { + if (mask & (1UL << pos)) + return pos; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +/* +Returns true if given number is a power of two. +T must be unsigned integer number or signed integer but always nonnegative. +For 0 returns true. +*/ +template +inline bool VmaIsPow2(T x) +{ + return (x & (x - 1)) == 0; } // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16. // Use types like uint32_t, uint64_t as T. template -static inline T VmaAlignUp(T val, T align) +static inline T VmaAlignUp(T val, T alignment) { - return (val + align - 1) / align * align; + VMA_HEAVY_ASSERT(VmaIsPow2(alignment)); + return (val + alignment - 1) & ~(alignment - 1); +} + +// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignDown(T val, T alignment) +{ + VMA_HEAVY_ASSERT(VmaIsPow2(alignment)); + return val & ~(alignment - 1); } // Division with mathematical rounding to nearest number. template -inline T VmaRoundDiv(T x, T y) +static inline T VmaRoundDiv(T x, T y) { - return (x + (y / (T)2)) / y; + return (x + (y / (T)2)) / y; } -#ifndef VMA_SORT +// Divide by 'y' and round up to nearest integer. +template +static inline T VmaDivideRoundingUp(T x, T y) +{ + return (x + y - (T)1) / y; +} +// Returns smallest power of 2 greater or equal to v. +static inline uint32_t VmaNextPow2(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static inline uint64_t VmaNextPow2(uint64_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +// Returns largest power of 2 less or equal to v. +static inline uint32_t VmaPrevPow2(uint32_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v = v ^ (v >> 1); + return v; +} + +static inline uint64_t VmaPrevPow2(uint64_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v = v ^ (v >> 1); + return v; +} + +static inline bool VmaStrIsEmpty(const char* pStr) +{ + return pStr == VMA_NULL || *pStr == '\0'; +} + +#if VMA_STATS_STRING_ENABLED +static const char* VmaAlgorithmToStr(uint32_t algorithm) +{ + switch (algorithm) + { + case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: + return "Linear"; + case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT: + return "Buddy"; + case VMA_POOL_CREATE_TLSF_ALGORITHM_BIT: + return "TLSF"; + case 0: + return "Default"; + default: + VMA_ASSERT(0); + return ""; + } +} +#endif // VMA_STATS_STRING_ENABLED + +#ifndef VMA_SORT template Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp) { Iterator centerValue = end; --centerValue; Iterator insertIndex = beg; - for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex) + for (Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex) { - if(cmp(*memTypeIndex, *centerValue)) + if (cmp(*memTypeIndex, *centerValue)) { - if(insertIndex != memTypeIndex) + if (insertIndex != memTypeIndex) { VMA_SWAP(*memTypeIndex, *insertIndex); } ++insertIndex; } } - if(insertIndex != centerValue) + if (insertIndex != centerValue) { VMA_SWAP(*insertIndex, *centerValue); } @@ -1934,7 +3277,7 @@ Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp) template void VmaQuickSort(Iterator beg, Iterator end, Compare cmp) { - if(beg < end) + if (beg < end) { Iterator it = VmaQuickSortPartition(beg, end, cmp); VmaQuickSort(beg, it, cmp); @@ -1943,8 +3286,7 @@ void VmaQuickSort(Iterator beg, Iterator end, Compare cmp) } #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp) - -#endif // #ifndef VMA_SORT +#endif // VMA_SORT /* Returns true if two memory blocks occupy overlapping pages. @@ -1967,17 +3309,6 @@ static inline bool VmaBlocksOnSamePage( return resourceAEndPage == resourceBStartPage; } -enum VmaSuballocationType -{ - VMA_SUBALLOCATION_TYPE_FREE = 0, - VMA_SUBALLOCATION_TYPE_UNKNOWN = 1, - VMA_SUBALLOCATION_TYPE_BUFFER = 2, - VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3, - VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4, - VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5, - VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF -}; - /* Returns true if given suballocation types could conflict and must respect VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer @@ -1988,12 +3319,12 @@ static inline bool VmaIsBufferImageGranularityConflict( VmaSuballocationType suballocType1, VmaSuballocationType suballocType2) { - if(suballocType1 > suballocType2) + if (suballocType1 > suballocType2) { VMA_SWAP(suballocType1, suballocType2); } - - switch(suballocType1) + + switch (suballocType1) { case VMA_SUBALLOCATION_TYPE_FREE: return false; @@ -2019,40 +3350,48 @@ static inline bool VmaIsBufferImageGranularityConflict( } } -// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope). -struct VmaMutexLock +static void VmaWriteMagicValue(void* pData, VkDeviceSize offset) { -public: - VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) : - m_pMutex(useMutex ? &mutex : VMA_NULL) +#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION + uint32_t* pDst = (uint32_t*)((char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for (size_t i = 0; i < numberCount; ++i, ++pDst) { - if(m_pMutex) - { - m_pMutex->Lock(); - } + *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE; } - - ~VmaMutexLock() - { - if(m_pMutex) - { - m_pMutex->Unlock(); - } - } - -private: - VMA_MUTEX* m_pMutex; -}; - -#if VMA_DEBUG_GLOBAL_MUTEX - static VMA_MUTEX gDebugGlobalMutex; - #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true); #else - #define VMA_DEBUG_GLOBAL_MUTEX_LOCK + // no-op #endif +} + +static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset) +{ +#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION + const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for (size_t i = 0; i < numberCount; ++i, ++pSrc) + { + if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE) + { + return false; + } + } +#endif + return true; +} + +/* +Fills structure with parameters of an example buffer to be used for transfers +during GPU memory defragmentation. +*/ +static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo) +{ + memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo)); + outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size. +} -// Minimum size of a free suballocation to register it in the free suballocation collection. -static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16; /* Performs binary search and returns iterator to first element that is greater or @@ -2063,23 +3402,69 @@ Cmp should return true if first argument is less than second argument. Returned value is the found element, if present in the collection or place where new element with value (key) should be inserted. */ -template -static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpT cmp) +template +static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp) { - size_t down = 0, up = (end - beg); - while(down < up) - { - const size_t mid = (down + up) / 2; - if(cmp(*(beg+mid), key)) - { - down = mid + 1; - } - else - { - up = mid; - } - } - return beg + down; + size_t down = 0, up = (end - beg); + while (down < up) + { + const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation + if (cmp(*(beg + mid), key)) + { + down = mid + 1; + } + else + { + up = mid; + } + } + return beg + down; +} + +template +IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp) +{ + IterT it = VmaBinaryFindFirstNotLess( + beg, end, value, cmp); + if (it == end || + (!cmp(*it, value) && !cmp(value, *it))) + { + return it; + } + return end; +} + +/* +Returns true if all pointers in the array are not-null and unique. +Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT. +T must be pointer type, e.g. VmaAllocation, VmaPool. +*/ +template +static bool VmaValidatePointerArray(uint32_t count, const T* arr) +{ + for (uint32_t i = 0; i < count; ++i) + { + const T iPtr = arr[i]; + if (iPtr == VMA_NULL) + { + return false; + } + for (uint32_t j = i + 1; j < count; ++j) + { + if (iPtr == arr[j]) + { + return false; + } + } + } + return true; +} + +template +static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct) +{ + newStruct->pNext = mainStruct->pNext; + mainStruct->pNext = newStruct; } //////////////////////////////////////////////////////////////////////////////// @@ -2087,10 +3472,11 @@ static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, Cm static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment) { - if((pAllocationCallbacks != VMA_NULL) && + void* result = VMA_NULL; + if ((pAllocationCallbacks != VMA_NULL) && (pAllocationCallbacks->pfnAllocation != VMA_NULL)) { - return (*pAllocationCallbacks->pfnAllocation)( + result = (*pAllocationCallbacks->pfnAllocation)( pAllocationCallbacks->pUserData, size, alignment, @@ -2098,20 +3484,22 @@ static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t } else { - return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment); + result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment); } + VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed."); + return result; } static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr) { - if((pAllocationCallbacks != VMA_NULL) && + if ((pAllocationCallbacks != VMA_NULL) && (pAllocationCallbacks->pfnFree != VMA_NULL)) { (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr); } else { - VMA_SYSTEM_FREE(ptr); + VMA_SYSTEM_ALIGNED_FREE(ptr); } } @@ -2141,9 +3529,9 @@ static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr template static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count) { - if(ptr != VMA_NULL) + if (ptr != VMA_NULL) { - for(size_t i = count; i--; ) + for (size_t i = count; i--; ) { ptr[i].~T(); } @@ -2151,272 +3539,41 @@ static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, } } -// STL-compatible allocator. -template -class VmaStlAllocator +static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr) { -public: - const VkAllocationCallbacks* const m_pCallbacks; - typedef T value_type; - - VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { } - template VmaStlAllocator(const VmaStlAllocator& src) : m_pCallbacks(src.m_pCallbacks) { } - - T* allocate(size_t n) { return VmaAllocateArray(m_pCallbacks, n); } - void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); } - - template - bool operator==(const VmaStlAllocator& rhs) const + if (srcStr != VMA_NULL) { - return m_pCallbacks == rhs.m_pCallbacks; + const size_t len = strlen(srcStr); + char* const result = vma_new_array(allocs, char, len + 1); + memcpy(result, srcStr, len + 1); + return result; } - template - bool operator!=(const VmaStlAllocator& rhs) const - { - return m_pCallbacks != rhs.m_pCallbacks; - } - - VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete; -}; - -#if VMA_USE_STL_VECTOR - -#define VmaVector std::vector - -template -static void VmaVectorInsert(std::vector& vec, size_t index, const T& item) -{ - vec.insert(vec.begin() + index, item); + return VMA_NULL; } -template -static void VmaVectorRemove(std::vector& vec, size_t index) +#if VMA_STATS_STRING_ENABLED +static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen) { - vec.erase(vec.begin() + index); + if (srcStr != VMA_NULL) + { + char* const result = vma_new_array(allocs, char, strLen + 1); + memcpy(result, srcStr, strLen); + result[strLen] = '\0'; + return result; + } + return VMA_NULL; } +#endif // VMA_STATS_STRING_ENABLED -#else // #if VMA_USE_STL_VECTOR - -/* Class with interface compatible with subset of std::vector. -T must be POD because constructors and destructors are not called and memcpy is -used for these objects. */ -template -class VmaVector +static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str) { -public: - typedef T value_type; - - VmaVector(const AllocatorT& allocator) : - m_Allocator(allocator), - m_pArray(VMA_NULL), - m_Count(0), - m_Capacity(0) + if (str != VMA_NULL) { + const size_t len = strlen(str); + vma_delete_array(allocs, str, len + 1); } - - VmaVector(size_t count, const AllocatorT& allocator) : - m_Allocator(allocator), - m_pArray(count ? (T*)VmaAllocateArray(allocator.m_pCallbacks, count) : VMA_NULL), - m_Count(count), - m_Capacity(count) - { - } - - VmaVector(const VmaVector& src) : - m_Allocator(src.m_Allocator), - m_pArray(src.m_Count ? (T*)VmaAllocateArray(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL), - m_Count(src.m_Count), - m_Capacity(src.m_Count) - { - if(m_Count != 0) - { - memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T)); - } - } - - ~VmaVector() - { - VmaFree(m_Allocator.m_pCallbacks, m_pArray); - } - - VmaVector& operator=(const VmaVector& rhs) - { - if(&rhs != this) - { - resize(rhs.m_Count); - if(m_Count != 0) - { - memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T)); - } - } - return *this; - } - - bool empty() const { return m_Count == 0; } - size_t size() const { return m_Count; } - T* data() { return m_pArray; } - const T* data() const { return m_pArray; } - - T& operator[](size_t index) - { - VMA_HEAVY_ASSERT(index < m_Count); - return m_pArray[index]; - } - const T& operator[](size_t index) const - { - VMA_HEAVY_ASSERT(index < m_Count); - return m_pArray[index]; - } - - T& front() - { - VMA_HEAVY_ASSERT(m_Count > 0); - return m_pArray[0]; - } - const T& front() const - { - VMA_HEAVY_ASSERT(m_Count > 0); - return m_pArray[0]; - } - T& back() - { - VMA_HEAVY_ASSERT(m_Count > 0); - return m_pArray[m_Count - 1]; - } - const T& back() const - { - VMA_HEAVY_ASSERT(m_Count > 0); - return m_pArray[m_Count - 1]; - } - - void reserve(size_t newCapacity, bool freeMemory = false) - { - newCapacity = VMA_MAX(newCapacity, m_Count); - - if((newCapacity < m_Capacity) && !freeMemory) - { - newCapacity = m_Capacity; - } - - if(newCapacity != m_Capacity) - { - T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator, newCapacity) : VMA_NULL; - if(m_Count != 0) - { - memcpy(newArray, m_pArray, m_Count * sizeof(T)); - } - VmaFree(m_Allocator.m_pCallbacks, m_pArray); - m_Capacity = newCapacity; - m_pArray = newArray; - } - } - - void resize(size_t newCount, bool freeMemory = false) - { - size_t newCapacity = m_Capacity; - if(newCount > m_Capacity) - { - newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8)); - } - else if(freeMemory) - { - newCapacity = newCount; - } - - if(newCapacity != m_Capacity) - { - T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL; - const size_t elementsToCopy = VMA_MIN(m_Count, newCount); - if(elementsToCopy != 0) - { - memcpy(newArray, m_pArray, elementsToCopy * sizeof(T)); - } - VmaFree(m_Allocator.m_pCallbacks, m_pArray); - m_Capacity = newCapacity; - m_pArray = newArray; - } - - m_Count = newCount; - } - - void clear(bool freeMemory = false) - { - resize(0, freeMemory); - } - - void insert(size_t index, const T& src) - { - VMA_HEAVY_ASSERT(index <= m_Count); - const size_t oldCount = size(); - resize(oldCount + 1); - if(index < oldCount) - { - memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T)); - } - m_pArray[index] = src; - } - - void remove(size_t index) - { - VMA_HEAVY_ASSERT(index < m_Count); - const size_t oldCount = size(); - if(index < oldCount - 1) - { - memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T)); - } - resize(oldCount - 1); - } - - void push_back(const T& src) - { - const size_t newIndex = size(); - resize(newIndex + 1); - m_pArray[newIndex] = src; - } - - void pop_back() - { - VMA_HEAVY_ASSERT(m_Count > 0); - resize(size() - 1); - } - - void push_front(const T& src) - { - insert(0, src); - } - - void pop_front() - { - VMA_HEAVY_ASSERT(m_Count > 0); - remove(0); - } - - typedef T* iterator; - - iterator begin() { return m_pArray; } - iterator end() { return m_pArray + m_Count; } - -private: - AllocatorT m_Allocator; - T* m_pArray; - size_t m_Count; - size_t m_Capacity; -}; - -template -static void VmaVectorInsert(VmaVector& vec, size_t index, const T& item) -{ - vec.insert(index, item); } -template -static void VmaVectorRemove(VmaVector& vec, size_t index) -{ - vec.remove(index); -} - -#endif // #if VMA_USE_STL_VECTOR - template size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value) { @@ -2438,7 +3595,7 @@ bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& vector.end(), value, comparator); - if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it)) + if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it)) { size_t indexToRemove = it - vector.begin(); VmaVectorRemove(vector, indexToRemove); @@ -2446,29 +3603,550 @@ bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& } return false; } +#endif // _VMA_FUNCTIONS -template -size_t VmaVectorFindSorted(const VectorT& vector, const typename VectorT::value_type& value) +#ifndef _VMA_STAT_INFO_FUNCTIONS +static void VmaInitStatInfo(VmaStatInfo& outInfo) { - CmpLess comparator; - typename VectorT::iterator it = VmaBinaryFindFirstNotLess( - vector.data(), - vector.data() + vector.size(), - value, - comparator); - if(it != vector.size() && !comparator(*it, value) && !comparator(value, *it)) + memset(&outInfo, 0, sizeof(outInfo)); + outInfo.allocationSizeMin = UINT64_MAX; + outInfo.unusedRangeSizeMin = UINT64_MAX; +} + +// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo. +static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo) +{ + inoutInfo.blockCount += srcInfo.blockCount; + inoutInfo.allocationCount += srcInfo.allocationCount; + inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount; + inoutInfo.usedBytes += srcInfo.usedBytes; + inoutInfo.unusedBytes += srcInfo.unusedBytes; + inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin); + inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax); + inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin); + inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax); +} + +static void VmaAddStatInfoAllocation(VmaStatInfo& inoutInfo, VkDeviceSize size) +{ + ++inoutInfo.allocationCount; + inoutInfo.usedBytes += size; + if (size < inoutInfo.allocationSizeMin) { - return it - vector.begin(); + inoutInfo.allocationSizeMin = size; } - else + if (size > inoutInfo.allocationSizeMax) { - return vector.size(); + inoutInfo.allocationSizeMax = size; } } -//////////////////////////////////////////////////////////////////////////////// -// class VmaPoolAllocator +static void VmaAddStatInfoUnusedRange(VmaStatInfo& inoutInfo, VkDeviceSize size) +{ + ++inoutInfo.unusedRangeCount; + inoutInfo.unusedBytes += size; + if (size < inoutInfo.unusedRangeSizeMin) + { + inoutInfo.unusedRangeSizeMin = size; + } + if (size > inoutInfo.unusedRangeSizeMax) + { + inoutInfo.unusedRangeSizeMax = size; + } +} +static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo) +{ + inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ? + VmaRoundDiv(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0; + inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ? + VmaRoundDiv(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0; +} +#endif // _VMA_STAT_INFO_FUNCTIONS + + +#ifndef _VMA_MUTEX_LOCK +// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope). +struct VmaMutexLock +{ + VMA_CLASS_NO_COPY(VmaMutexLock) +public: + VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->Lock(); } + } + ~VmaMutexLock() { if (m_pMutex) { m_pMutex->Unlock(); } } + +private: + VMA_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading. +struct VmaMutexLockRead +{ + VMA_CLASS_NO_COPY(VmaMutexLockRead) +public: + VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->LockRead(); } + } + ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } } + +private: + VMA_RW_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing. +struct VmaMutexLockWrite +{ + VMA_CLASS_NO_COPY(VmaMutexLockWrite) +public: + VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) + : m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->LockWrite(); } + } + ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } } + +private: + VMA_RW_MUTEX* m_pMutex; +}; + +#if VMA_DEBUG_GLOBAL_MUTEX + static VMA_MUTEX gDebugGlobalMutex; + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true); +#else + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK +#endif +#endif // _VMA_MUTEX_LOCK + +#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT +// An object that increments given atomic but decrements it back in the destructor unless Commit() is called. +template +struct AtomicTransactionalIncrement +{ +public: + typedef std::atomic AtomicT; + + ~AtomicTransactionalIncrement() + { + if(m_Atomic) + --(*m_Atomic); + } + + void Commit() { m_Atomic = nullptr; } + T Increment(AtomicT* atomic) + { + m_Atomic = atomic; + return m_Atomic->fetch_add(1); + } + +private: + AtomicT* m_Atomic = nullptr; +}; +#endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT + +#ifndef _VMA_STL_ALLOCATOR +// STL-compatible allocator. +template +struct VmaStlAllocator +{ + const VkAllocationCallbacks* const m_pCallbacks; + typedef T value_type; + + VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {} + template + VmaStlAllocator(const VmaStlAllocator& src) : m_pCallbacks(src.m_pCallbacks) {} + VmaStlAllocator(const VmaStlAllocator&) = default; + VmaStlAllocator& operator=(const VmaStlAllocator&) = delete; + + T* allocate(size_t n) { return VmaAllocateArray(m_pCallbacks, n); } + void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); } + + template + bool operator==(const VmaStlAllocator& rhs) const + { + return m_pCallbacks == rhs.m_pCallbacks; + } + template + bool operator!=(const VmaStlAllocator& rhs) const + { + return m_pCallbacks != rhs.m_pCallbacks; + } +}; +#endif // _VMA_STL_ALLOCATOR + +#ifndef _VMA_VECTOR +/* Class with interface compatible with subset of std::vector. +T must be POD because constructors and destructors are not called and memcpy is +used for these objects. */ +template +class VmaVector +{ +public: + typedef T value_type; + typedef T* iterator; + typedef const T* const_iterator; + + VmaVector(const AllocatorT& allocator); + VmaVector(size_t count, const AllocatorT& allocator); + // This version of the constructor is here for compatibility with pre-C++14 std::vector. + // value is unused. + VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {} + VmaVector(const VmaVector& src); + VmaVector& operator=(const VmaVector& rhs); + ~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); } + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_pArray; } + T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; } + T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; } + const T* data() const { return m_pArray; } + const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; } + const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; } + + iterator begin() { return m_pArray; } + iterator end() { return m_pArray + m_Count; } + const_iterator cbegin() const { return m_pArray; } + const_iterator cend() const { return m_pArray + m_Count; } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); } + void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); } + void push_front(const T& src) { insert(0, src); } + + void push_back(const T& src); + void reserve(size_t newCapacity, bool freeMemory = false); + void resize(size_t newCount); + void clear() { resize(0); } + void shrink_to_fit(); + void insert(size_t index, const T& src); + void remove(size_t index); + + T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; } + const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; } + +private: + AllocatorT m_Allocator; + T* m_pArray; + size_t m_Count; + size_t m_Capacity; +}; + +#ifndef _VMA_VECTOR_FUNCTIONS +template +VmaVector::VmaVector(const AllocatorT& allocator) + : m_Allocator(allocator), + m_pArray(VMA_NULL), + m_Count(0), + m_Capacity(0) {} + +template +VmaVector::VmaVector(size_t count, const AllocatorT& allocator) + : m_Allocator(allocator), + m_pArray(count ? (T*)VmaAllocateArray(allocator.m_pCallbacks, count) : VMA_NULL), + m_Count(count), + m_Capacity(count) {} + +template +VmaVector::VmaVector(const VmaVector& src) + : m_Allocator(src.m_Allocator), + m_pArray(src.m_Count ? (T*)VmaAllocateArray(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL), + m_Count(src.m_Count), + m_Capacity(src.m_Count) +{ + if (m_Count != 0) + { + memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T)); + } +} + +template +VmaVector& VmaVector::operator=(const VmaVector& rhs) +{ + if (&rhs != this) + { + resize(rhs.m_Count); + if (m_Count != 0) + { + memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T)); + } + } + return *this; +} + +template +void VmaVector::push_back(const T& src) +{ + const size_t newIndex = size(); + resize(newIndex + 1); + m_pArray[newIndex] = src; +} + +template +void VmaVector::reserve(size_t newCapacity, bool freeMemory) +{ + newCapacity = VMA_MAX(newCapacity, m_Count); + + if ((newCapacity < m_Capacity) && !freeMemory) + { + newCapacity = m_Capacity; + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator, newCapacity) : VMA_NULL; + if (m_Count != 0) + { + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } +} + +template +void VmaVector::resize(size_t newCount) +{ + size_t newCapacity = m_Capacity; + if (newCount > m_Capacity) + { + newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8)); + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL; + const size_t elementsToCopy = VMA_MIN(m_Count, newCount); + if (elementsToCopy != 0) + { + memcpy(newArray, m_pArray, elementsToCopy * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } + + m_Count = newCount; +} + +template +void VmaVector::shrink_to_fit() +{ + if (m_Capacity > m_Count) + { + T* newArray = VMA_NULL; + if (m_Count > 0) + { + newArray = VmaAllocateArray(m_Allocator.m_pCallbacks, m_Count); + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = m_Count; + m_pArray = newArray; + } +} + +template +void VmaVector::insert(size_t index, const T& src) +{ + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + if (index < oldCount) + { + memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T)); + } + m_pArray[index] = src; +} + +template +void VmaVector::remove(size_t index) +{ + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} +#endif // _VMA_VECTOR_FUNCTIONS + +template +static void VmaVectorInsert(VmaVector& vec, size_t index, const T& item) +{ + vec.insert(index, item); +} + +template +static void VmaVectorRemove(VmaVector& vec, size_t index) +{ + vec.remove(index); +} +#endif // _VMA_VECTOR + +#ifndef _VMA_SMALL_VECTOR +/* +This is a vector (a variable-sized array), optimized for the case when the array is small. + +It contains some number of elements in-place, which allows it to avoid heap allocation +when the actual number of elements is below that threshold. This allows normal "small" +cases to be fast without losing generality for large inputs. +*/ +template +class VmaSmallVector +{ +public: + typedef T value_type; + typedef T* iterator; + + VmaSmallVector(const AllocatorT& allocator); + VmaSmallVector(size_t count, const AllocatorT& allocator); + template + VmaSmallVector(const VmaSmallVector&) = delete; + template + VmaSmallVector& operator=(const VmaSmallVector&) = delete; + ~VmaSmallVector() = default; + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; } + T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; } + T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; } + const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; } + const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; } + const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; } + + iterator begin() { return data(); } + iterator end() { return data() + m_Count; } + + void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); } + void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); } + void push_front(const T& src) { insert(0, src); } + + void push_back(const T& src); + void resize(size_t newCount, bool freeMemory = false); + void clear(bool freeMemory = false); + void insert(size_t index, const T& src); + void remove(size_t index); + + T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; } + const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; } + +private: + size_t m_Count; + T m_StaticArray[N]; // Used when m_Size <= N + VmaVector m_DynamicArray; // Used when m_Size > N +}; + +#ifndef _VMA_SMALL_VECTOR_FUNCTIONS +template +VmaSmallVector::VmaSmallVector(const AllocatorT& allocator) + : m_Count(0), + m_DynamicArray(allocator) {} + +template +VmaSmallVector::VmaSmallVector(size_t count, const AllocatorT& allocator) + : m_Count(count), + m_DynamicArray(count > N ? count : 0, allocator) {} + +template +void VmaSmallVector::push_back(const T& src) +{ + resize(m_Count + 1); + data()[m_Count] = src; +} + +template +void VmaSmallVector::resize(size_t newCount, bool freeMemory) +{ + if (newCount > N && m_Count > N) + { + // Any direction, staying in m_DynamicArray + m_DynamicArray.resize(newCount); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + } + else if (newCount > N && m_Count <= N) + { + // Growing, moving from m_StaticArray to m_DynamicArray + m_DynamicArray.resize(newCount); + if (m_Count > 0) + { + memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T)); + } + } + else if (newCount <= N && m_Count > N) + { + // Shrinking, moving from m_DynamicArray to m_StaticArray + if (newCount > 0) + { + memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T)); + } + m_DynamicArray.resize(0); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + } + else + { + // Any direction, staying in m_StaticArray - nothing to do here + } + m_Count = newCount; +} + +template +void VmaSmallVector::clear(bool freeMemory) +{ + m_DynamicArray.clear(); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + m_Count = 0; +} + +template +void VmaSmallVector::insert(size_t index, const T& src) +{ + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + T* const dataPtr = data(); + if (index < oldCount) + { + // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray. + memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T)); + } + dataPtr[index] = src; +} + +template +void VmaSmallVector::remove(size_t index) +{ + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray. + T* const dataPtr = data(); + memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} +#endif // _VMA_SMALL_VECTOR_FUNCTIONS +#endif // _VMA_SMALL_VECTOR + +#ifndef _VMA_POOL_ALLOCATOR /* Allocator for objects of type T using a list of arrays (pools) to speed up allocation. Number of elements that can be allocated is not bounded because @@ -2477,68 +4155,65 @@ allocator can create multiple blocks. template class VmaPoolAllocator { + VMA_CLASS_NO_COPY(VmaPoolAllocator) public: - VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock); + VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity); ~VmaPoolAllocator(); - void Clear(); - T* Alloc(); + template T* Alloc(Types&&... args); void Free(T* ptr); private: union Item { uint32_t NextFreeIndex; - T Value; + alignas(T) char Value[sizeof(T)]; }; - struct ItemBlock { Item* pItems; + uint32_t Capacity; uint32_t FirstFreeIndex; }; - + const VkAllocationCallbacks* m_pAllocationCallbacks; - size_t m_ItemsPerBlock; - VmaVector< ItemBlock, VmaStlAllocator > m_ItemBlocks; + const uint32_t m_FirstBlockCapacity; + VmaVector> m_ItemBlocks; ItemBlock& CreateNewBlock(); }; +#ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS template -VmaPoolAllocator::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) : - m_pAllocationCallbacks(pAllocationCallbacks), - m_ItemsPerBlock(itemsPerBlock), +VmaPoolAllocator::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) + : m_pAllocationCallbacks(pAllocationCallbacks), + m_FirstBlockCapacity(firstBlockCapacity), m_ItemBlocks(VmaStlAllocator(pAllocationCallbacks)) { - VMA_ASSERT(itemsPerBlock > 0); + VMA_ASSERT(m_FirstBlockCapacity > 1); } template VmaPoolAllocator::~VmaPoolAllocator() { - Clear(); -} - -template -void VmaPoolAllocator::Clear() -{ - for(size_t i = m_ItemBlocks.size(); i--; ) - vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock); + for (size_t i = m_ItemBlocks.size(); i--;) + vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity); m_ItemBlocks.clear(); } template -T* VmaPoolAllocator::Alloc() +template T* VmaPoolAllocator::Alloc(Types&&... args) { - for(size_t i = m_ItemBlocks.size(); i--; ) + for (size_t i = m_ItemBlocks.size(); i--; ) { ItemBlock& block = m_ItemBlocks[i]; // This block has some free items: Use first one. - if(block.FirstFreeIndex != UINT32_MAX) + if (block.FirstFreeIndex != UINT32_MAX) { Item* const pItem = &block.pItems[block.FirstFreeIndex]; block.FirstFreeIndex = pItem->NextFreeIndex; - return &pItem->Value; + T* result = (T*)&pItem->Value; + new(result)T(std::forward(args)...); // Explicit constructor call. + return result; } } @@ -2546,24 +4221,27 @@ T* VmaPoolAllocator::Alloc() ItemBlock& newBlock = CreateNewBlock(); Item* const pItem = &newBlock.pItems[0]; newBlock.FirstFreeIndex = pItem->NextFreeIndex; - return &pItem->Value; + T* result = (T*)&pItem->Value; + new(result) T(std::forward(args)...); // Explicit constructor call. + return result; } template void VmaPoolAllocator::Free(T* ptr) { // Search all memory blocks to find ptr. - for(size_t i = 0; i < m_ItemBlocks.size(); ++i) + for (size_t i = m_ItemBlocks.size(); i--; ) { ItemBlock& block = m_ItemBlocks[i]; - + // Casting to union. Item* pItemPtr; memcpy(&pItemPtr, &ptr, sizeof(pItemPtr)); - + // Check if pItemPtr is in address range of this block. - if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock)) + if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity)) { + ptr->~T(); // Explicit destructor call. const uint32_t index = static_cast(pItemPtr - block.pItems); pItemPtr->NextFreeIndex = block.FirstFreeIndex; block.FirstFreeIndex = index; @@ -2576,27 +4254,28 @@ void VmaPoolAllocator::Free(T* ptr) template typename VmaPoolAllocator::ItemBlock& VmaPoolAllocator::CreateNewBlock() { - ItemBlock newBlock = { - vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 }; + const uint32_t newBlockCapacity = m_ItemBlocks.empty() ? + m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2; + + const ItemBlock newBlock = + { + vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity), + newBlockCapacity, + 0 + }; m_ItemBlocks.push_back(newBlock); // Setup singly-linked list of all free items in this block. - for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i) + for (uint32_t i = 0; i < newBlockCapacity - 1; ++i) newBlock.pItems[i].NextFreeIndex = i + 1; - newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX; + newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX; return m_ItemBlocks.back(); } +#endif // _VMA_POOL_ALLOCATOR_FUNCTIONS +#endif // _VMA_POOL_ALLOCATOR -//////////////////////////////////////////////////////////////////////////////// -// class VmaRawList, VmaList - -#if VMA_USE_STL_LIST - -#define VmaList std::list - -#else // #if VMA_USE_STL_LIST - +#ifndef _VMA_RAW_LIST template struct VmaListItem { @@ -2609,36 +4288,38 @@ struct VmaListItem template class VmaRawList { + VMA_CLASS_NO_COPY(VmaRawList) public: typedef VmaListItem ItemType; VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks); - ~VmaRawList(); - void Clear(); + // Intentionally not calling Clear, because that would be unnecessary + // computations to return all items to m_ItemAllocator as free. + ~VmaRawList() = default; size_t GetCount() const { return m_Count; } bool IsEmpty() const { return m_Count == 0; } ItemType* Front() { return m_pFront; } - const ItemType* Front() const { return m_pFront; } ItemType* Back() { return m_pBack; } + const ItemType* Front() const { return m_pFront; } const ItemType* Back() const { return m_pBack; } - ItemType* PushBack(); ItemType* PushFront(); - ItemType* PushBack(const T& value); + ItemType* PushBack(); ItemType* PushFront(const T& value); - void PopBack(); + ItemType* PushBack(const T& value); void PopFront(); - + void PopBack(); + // Item can be null - it means PushBack. ItemType* InsertBefore(ItemType* pItem); // Item can be null - it means PushFront. ItemType* InsertAfter(ItemType* pItem); - ItemType* InsertBefore(ItemType* pItem, const T& value); ItemType* InsertAfter(ItemType* pItem, const T& value); + void Clear(); void Remove(ItemType* pItem); private: @@ -2647,45 +4328,37 @@ private: ItemType* m_pFront; ItemType* m_pBack; size_t m_Count; - - // Declared not defined, to block copy constructor and assignment operator. - VmaRawList(const VmaRawList& src); - VmaRawList& operator=(const VmaRawList& rhs); }; +#ifndef _VMA_RAW_LIST_FUNCTIONS template -VmaRawList::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) : - m_pAllocationCallbacks(pAllocationCallbacks), +VmaRawList::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) + : m_pAllocationCallbacks(pAllocationCallbacks), m_ItemAllocator(pAllocationCallbacks, 128), m_pFront(VMA_NULL), m_pBack(VMA_NULL), - m_Count(0) -{ -} + m_Count(0) {} template -VmaRawList::~VmaRawList() +VmaListItem* VmaRawList::PushFront() { - // Intentionally not calling Clear, because that would be unnecessary - // computations to return all items to m_ItemAllocator as free. -} - -template -void VmaRawList::Clear() -{ - if(IsEmpty() == false) + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pPrev = VMA_NULL; + if (IsEmpty()) { - ItemType* pItem = m_pBack; - while(pItem != VMA_NULL) - { - ItemType* const pPrevItem = pItem->pPrev; - m_ItemAllocator.Free(pItem); - pItem = pPrevItem; - } - m_pFront = VMA_NULL; - m_pBack = VMA_NULL; - m_Count = 0; + pNewItem->pNext = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; } + else + { + pNewItem->pNext = m_pFront; + m_pFront->pPrev = pNewItem; + m_pFront = pNewItem; + ++m_Count; + } + return pNewItem; } template @@ -2711,24 +4384,10 @@ VmaListItem* VmaRawList::PushBack() } template -VmaListItem* VmaRawList::PushFront() +VmaListItem* VmaRawList::PushFront(const T& value) { - ItemType* const pNewItem = m_ItemAllocator.Alloc(); - pNewItem->pPrev = VMA_NULL; - if(IsEmpty()) - { - pNewItem->pNext = VMA_NULL; - m_pFront = pNewItem; - m_pBack = pNewItem; - m_Count = 1; - } - else - { - pNewItem->pNext = m_pFront; - m_pFront->pPrev = pNewItem; - m_pFront = pNewItem; - ++m_Count; - } + ItemType* const pNewItem = PushFront(); + pNewItem->Value = value; return pNewItem; } @@ -2741,11 +4400,18 @@ VmaListItem* VmaRawList::PushBack(const T& value) } template -VmaListItem* VmaRawList::PushFront(const T& value) +void VmaRawList::PopFront() { - ItemType* const pNewItem = PushFront(); - pNewItem->Value = value; - return pNewItem; + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pFrontItem = m_pFront; + ItemType* const pNextItem = pFrontItem->pNext; + if (pNextItem != VMA_NULL) + { + pNextItem->pPrev = VMA_NULL; + } + m_pFront = pNextItem; + m_ItemAllocator.Free(pFrontItem); + --m_Count; } template @@ -2764,18 +4430,21 @@ void VmaRawList::PopBack() } template -void VmaRawList::PopFront() +void VmaRawList::Clear() { - VMA_HEAVY_ASSERT(m_Count > 0); - ItemType* const pFrontItem = m_pFront; - ItemType* const pNextItem = pFrontItem->pNext; - if(pNextItem != VMA_NULL) + if (IsEmpty() == false) { - pNextItem->pPrev = VMA_NULL; + ItemType* pItem = m_pBack; + while (pItem != VMA_NULL) + { + ItemType* const pPrevItem = pItem->pPrev; + m_ItemAllocator.Free(pItem); + pItem = pPrevItem; + } + m_pFront = VMA_NULL; + m_pBack = VMA_NULL; + m_Count = 0; } - m_pFront = pNextItem; - m_ItemAllocator.Free(pFrontItem); - --m_Count; } template @@ -2875,172 +4544,123 @@ VmaListItem* VmaRawList::InsertAfter(ItemType* pItem, const T& value) newItem->Value = value; return newItem; } +#endif // _VMA_RAW_LIST_FUNCTIONS +#endif // _VMA_RAW_LIST +#ifndef _VMA_LIST template class VmaList { + VMA_CLASS_NO_COPY(VmaList) public: + class reverse_iterator; + class const_iterator; + class const_reverse_iterator; + class iterator { + friend class VmaList; public: - iterator() : - m_pList(VMA_NULL), - m_pItem(VMA_NULL) - { - } + iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} - T& operator*() const - { - VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); - return m_pItem->Value; - } - T* operator->() const - { - VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); - return &m_pItem->Value; - } + T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } - iterator& operator++() - { - VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); - m_pItem = m_pItem->pNext; - return *this; - } - iterator& operator--() - { - if(m_pItem != VMA_NULL) - { - m_pItem = m_pItem->pPrev; - } - else - { - VMA_HEAVY_ASSERT(!m_pList.IsEmpty()); - m_pItem = m_pList->Back(); - } - return *this; - } + bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } - iterator operator++(int) - { - iterator result = *this; - ++*this; - return result; - } - iterator operator--(int) - { - iterator result = *this; - --*this; - return result; - } + iterator operator++(int) { iterator result = *this; ++*this; return result; } + iterator operator--(int) { iterator result = *this; --*this; return result; } + + iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; } + iterator& operator--(); - bool operator==(const iterator& rhs) const - { - VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); - return m_pItem == rhs.m_pItem; - } - bool operator!=(const iterator& rhs) const - { - VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); - return m_pItem != rhs.m_pItem; - } - private: VmaRawList* m_pList; VmaListItem* m_pItem; - iterator(VmaRawList* pList, VmaListItem* pItem) : - m_pList(pList), - m_pItem(pItem) - { - } - - friend class VmaList; + iterator(VmaRawList* pList, VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} }; + class reverse_iterator + { + friend class VmaList; + public: + reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; } + reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; } + + reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; } + reverse_iterator& operator--(); + + private: + VmaRawList* m_pList; + VmaListItem* m_pItem; + + reverse_iterator(VmaRawList* pList, VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; class const_iterator { + friend class VmaList; public: - const_iterator() : - m_pList(VMA_NULL), - m_pItem(VMA_NULL) - { - } + const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} - const_iterator(const iterator& src) : - m_pList(src.m_pList), - m_pItem(src.m_pItem) - { - } - - const T& operator*() const - { - VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); - return m_pItem->Value; - } - const T* operator->() const - { - VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); - return &m_pItem->Value; - } + const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } - const_iterator& operator++() - { - VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); - m_pItem = m_pItem->pNext; - return *this; - } - const_iterator& operator--() - { - if(m_pItem != VMA_NULL) - { - m_pItem = m_pItem->pPrev; - } - else - { - VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); - m_pItem = m_pList->Back(); - } - return *this; - } + bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } - const_iterator operator++(int) - { - const_iterator result = *this; - ++*this; - return result; - } - const_iterator operator--(int) - { - const_iterator result = *this; - --*this; - return result; - } + const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; } + const_iterator operator--(int) { const_iterator result = *this; --* this; return result; } + + const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; } + const_iterator& operator--(); - bool operator==(const const_iterator& rhs) const - { - VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); - return m_pItem == rhs.m_pItem; - } - bool operator!=(const const_iterator& rhs) const - { - VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); - return m_pItem != rhs.m_pItem; - } - private: - const_iterator(const VmaRawList* pList, const VmaListItem* pItem) : - m_pList(pList), - m_pItem(pItem) - { - } - const VmaRawList* m_pList; const VmaListItem* m_pItem; + const_iterator(const VmaRawList* pList, const VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class const_reverse_iterator + { friend class VmaList; + public: + const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; } + const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; } + + const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; } + const_reverse_iterator& operator--(); + + private: + const VmaRawList* m_pList; + const VmaListItem* m_pItem; + + const_reverse_iterator(const VmaRawList* pList, const VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} }; - VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { } + VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {} bool empty() const { return m_RawList.IsEmpty(); } size_t size() const { return m_RawList.GetCount(); } @@ -3051,67 +4671,353 @@ public: const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); } const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); } - void clear() { m_RawList.Clear(); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); } + reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); } + + const_reverse_iterator crbegin() { return const_reverse_iterator(&m_RawList, m_RawList.Back()); } + const_reverse_iterator crend() { return const_reverse_iterator(&m_RawList, VMA_NULL); } + + const_reverse_iterator rbegin() const { return crbegin(); } + const_reverse_iterator rend() const { return crend(); } + void push_back(const T& value) { m_RawList.PushBack(value); } - void erase(iterator it) { m_RawList.Remove(it.m_pItem); } iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); } + void clear() { m_RawList.Clear(); } + void erase(iterator it) { m_RawList.Remove(it.m_pItem); } + private: VmaRawList m_RawList; }; -#endif // #if VMA_USE_STL_LIST +#ifndef _VMA_LIST_FUNCTIONS +template +typename VmaList::iterator& VmaList::iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} -//////////////////////////////////////////////////////////////////////////////// -// class VmaMap +template +typename VmaList::reverse_iterator& VmaList::reverse_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Front(); + } + return *this; +} + +template +typename VmaList::const_iterator& VmaList::const_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename VmaList::const_reverse_iterator& VmaList::const_reverse_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} +#endif // _VMA_LIST_FUNCTIONS +#endif // _VMA_LIST + +#ifndef _VMA_INTRUSIVE_LINKED_LIST +/* +Expected interface of ItemTypeTraits: +struct MyItemTypeTraits +{ + typedef MyItem ItemType; + static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; } + static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; } + static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; } + static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; } +}; +*/ +template +class VmaIntrusiveLinkedList +{ +public: + typedef typename ItemTypeTraits::ItemType ItemType; + static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); } + static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); } + + // Movable, not copyable. + VmaIntrusiveLinkedList() = default; + VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src); + VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete; + VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src); + VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete; + ~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); } + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + ItemType* Front() { return m_Front; } + ItemType* Back() { return m_Back; } + const ItemType* Front() const { return m_Front; } + const ItemType* Back() const { return m_Back; } + + void PushBack(ItemType* item); + void PushFront(ItemType* item); + ItemType* PopBack(); + ItemType* PopFront(); + + // MyItem can be null - it means PushBack. + void InsertBefore(ItemType* existingItem, ItemType* newItem); + // MyItem can be null - it means PushFront. + void InsertAfter(ItemType* existingItem, ItemType* newItem); + void Remove(ItemType* item); + void RemoveAll(); + +private: + ItemType* m_Front = VMA_NULL; + ItemType* m_Back = VMA_NULL; + size_t m_Count = 0; +}; + +#ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS +template +VmaIntrusiveLinkedList::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src) + : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count) +{ + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; +} + +template +VmaIntrusiveLinkedList& VmaIntrusiveLinkedList::operator=(VmaIntrusiveLinkedList&& src) +{ + if (&src != this) + { + VMA_HEAVY_ASSERT(IsEmpty()); + m_Front = src.m_Front; + m_Back = src.m_Back; + m_Count = src.m_Count; + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; + } + return *this; +} + +template +void VmaIntrusiveLinkedList::PushBack(ItemType* item) +{ + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessPrev(item) = m_Back; + ItemTypeTraits::AccessNext(m_Back) = item; + m_Back = item; + ++m_Count; + } +} + +template +void VmaIntrusiveLinkedList::PushFront(ItemType* item) +{ + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessNext(item) = m_Front; + ItemTypeTraits::AccessPrev(m_Front) = item; + m_Front = item; + ++m_Count; + } +} + +template +typename VmaIntrusiveLinkedList::ItemType* VmaIntrusiveLinkedList::PopBack() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const backItem = m_Back; + ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem); + if (prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = VMA_NULL; + } + m_Back = prevItem; + --m_Count; + ItemTypeTraits::AccessPrev(backItem) = VMA_NULL; + ItemTypeTraits::AccessNext(backItem) = VMA_NULL; + return backItem; +} + +template +typename VmaIntrusiveLinkedList::ItemType* VmaIntrusiveLinkedList::PopFront() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const frontItem = m_Front; + ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem); + if (nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL; + } + m_Front = nextItem; + --m_Count; + ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL; + ItemTypeTraits::AccessNext(frontItem) = VMA_NULL; + return frontItem; +} + +template +void VmaIntrusiveLinkedList::InsertBefore(ItemType* existingItem, ItemType* newItem) +{ + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if (existingItem != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem); + ItemTypeTraits::AccessPrev(newItem) = prevItem; + ItemTypeTraits::AccessNext(newItem) = existingItem; + ItemTypeTraits::AccessPrev(existingItem) = newItem; + if (prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Front == existingItem); + m_Front = newItem; + } + ++m_Count; + } + else + PushBack(newItem); +} + +template +void VmaIntrusiveLinkedList::InsertAfter(ItemType* existingItem, ItemType* newItem) +{ + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if (existingItem != VMA_NULL) + { + ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem); + ItemTypeTraits::AccessNext(newItem) = nextItem; + ItemTypeTraits::AccessPrev(newItem) = existingItem; + ItemTypeTraits::AccessNext(existingItem) = newItem; + if (nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Back == existingItem); + m_Back = newItem; + } + ++m_Count; + } + else + return PushFront(newItem); +} + +template +void VmaIntrusiveLinkedList::Remove(ItemType* item) +{ + VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0); + if (ItemTypeTraits::GetPrev(item) != VMA_NULL) + { + ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item); + } + else + { + VMA_HEAVY_ASSERT(m_Front == item); + m_Front = ItemTypeTraits::GetNext(item); + } + + if (ItemTypeTraits::GetNext(item) != VMA_NULL) + { + ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item); + } + else + { + VMA_HEAVY_ASSERT(m_Back == item); + m_Back = ItemTypeTraits::GetPrev(item); + } + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + --m_Count; +} + +template +void VmaIntrusiveLinkedList::RemoveAll() +{ + if (!IsEmpty()) + { + ItemType* item = m_Back; + while (item != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::AccessPrev(item); + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + item = prevItem; + } + m_Front = VMA_NULL; + m_Back = VMA_NULL; + m_Count = 0; + } +} +#endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS +#endif // _VMA_INTRUSIVE_LINKED_LIST // Unused in this version. #if 0 -#if VMA_USE_STL_UNORDERED_MAP - -#define VmaPair std::pair - -#define VMA_MAP_TYPE(KeyT, ValueT) \ - std::unordered_map< KeyT, ValueT, std::hash, std::equal_to, VmaStlAllocator< std::pair > > - -#else // #if VMA_USE_STL_UNORDERED_MAP - +#ifndef _VMA_PAIR template struct VmaPair { T1 first; T2 second; - VmaPair() : first(), second() { } - VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { } + VmaPair() : first(), second() {} + VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) {} }; -/* Class compatible with subset of interface of std::unordered_map. -KeyT, ValueT must be POD because they will be stored in VmaVector. -*/ -template -class VmaMap -{ -public: - typedef VmaPair PairType; - typedef PairType* iterator; - - VmaMap(const VmaStlAllocator& allocator) : m_Vector(allocator) { } - - iterator begin() { return m_Vector.begin(); } - iterator end() { return m_Vector.end(); } - - void insert(const PairType& pair); - iterator find(const KeyT& key); - void erase(iterator it); - -private: - VmaVector< PairType, VmaStlAllocator > m_Vector; -}; - -#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap - template struct VmaPairFirstLess { @@ -3124,7 +5030,33 @@ struct VmaPairFirstLess return lhs.first < rhsFirst; } }; +#endif // _VMA_PAIR +#ifndef _VMA_MAP +/* Class compatible with subset of interface of std::unordered_map. +KeyT, ValueT must be POD because they will be stored in VmaVector. +*/ +template +class VmaMap +{ +public: + typedef VmaPair PairType; + typedef PairType* iterator; + + VmaMap(const VmaStlAllocator& allocator) : m_Vector(allocator) {} + + iterator begin() { return m_Vector.begin(); } + iterator end() { return m_Vector.end(); } + + void insert(const PairType& pair); + iterator find(const KeyT& key); + void erase(iterator it); + +private: + VmaVector< PairType, VmaStlAllocator > m_Vector; +}; + +#ifndef _VMA_MAP_FUNCTIONS template void VmaMap::insert(const PairType& pair) { @@ -3144,7 +5076,7 @@ VmaPair* VmaMap::find(const KeyT& key) m_Vector.data() + m_Vector.size(), key, VmaPairFirstLess()); - if((it != m_Vector.end()) && (it->first == key)) + if ((it != m_Vector.end()) && (it->first == key)) { return it; } @@ -3159,890 +5091,37 @@ void VmaMap::erase(iterator it) { VmaVectorRemove(m_Vector, it - m_Vector.begin()); } - -#endif // #if VMA_USE_STL_UNORDERED_MAP +#endif // _VMA_MAP_FUNCTIONS +#endif // _VMA_MAP #endif // #if 0 -//////////////////////////////////////////////////////////////////////////////// - -class VmaDeviceMemoryBlock; - -struct VmaAllocation_T -{ -private: - static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80; - - enum FLAGS - { - FLAG_USER_DATA_STRING = 0x01, - }; - -public: - enum ALLOCATION_TYPE - { - ALLOCATION_TYPE_NONE, - ALLOCATION_TYPE_BLOCK, - ALLOCATION_TYPE_DEDICATED, - }; - - VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) : - m_Alignment(1), - m_Size(0), - m_pUserData(VMA_NULL), - m_LastUseFrameIndex(currentFrameIndex), - m_Type((uint8_t)ALLOCATION_TYPE_NONE), - m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN), - m_MapCount(0), - m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0) - { - } - - ~VmaAllocation_T() - { - VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction."); - - // Check if owned string was freed. - VMA_ASSERT(m_pUserData == VMA_NULL); - } - - void InitBlockAllocation( - VmaPool hPool, - VmaDeviceMemoryBlock* block, - VkDeviceSize offset, - VkDeviceSize alignment, - VkDeviceSize size, - VmaSuballocationType suballocationType, - bool mapped, - bool canBecomeLost) - { - VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); - VMA_ASSERT(block != VMA_NULL); - m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; - m_Alignment = alignment; - m_Size = size; - m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; - m_SuballocationType = (uint8_t)suballocationType; - m_BlockAllocation.m_hPool = hPool; - m_BlockAllocation.m_Block = block; - m_BlockAllocation.m_Offset = offset; - m_BlockAllocation.m_CanBecomeLost = canBecomeLost; - } - - void InitLost() - { - VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); - VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST); - m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; - m_BlockAllocation.m_hPool = VK_NULL_HANDLE; - m_BlockAllocation.m_Block = VMA_NULL; - m_BlockAllocation.m_Offset = 0; - m_BlockAllocation.m_CanBecomeLost = true; - } - - void ChangeBlockAllocation( - VmaDeviceMemoryBlock* block, - VkDeviceSize offset) - { - VMA_ASSERT(block != VMA_NULL); - VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); - m_BlockAllocation.m_Block = block; - m_BlockAllocation.m_Offset = offset; - } - - // pMappedData not null means allocation is created with MAPPED flag. - void InitDedicatedAllocation( - uint32_t memoryTypeIndex, - VkDeviceMemory hMemory, - VmaSuballocationType suballocationType, - void* pMappedData, - VkDeviceSize size) - { - VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); - VMA_ASSERT(hMemory != VK_NULL_HANDLE); - m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED; - m_Alignment = 0; - m_Size = size; - m_SuballocationType = (uint8_t)suballocationType; - m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; - m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex; - m_DedicatedAllocation.m_hMemory = hMemory; - m_DedicatedAllocation.m_pMappedData = pMappedData; - } - - ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } - VkDeviceSize GetAlignment() const { return m_Alignment; } - VkDeviceSize GetSize() const { return m_Size; } - bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; } - void* GetUserData() const { return m_pUserData; } - void SetUserData(VmaAllocator hAllocator, void* pUserData); - VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; } - - VmaDeviceMemoryBlock* GetBlock() const - { - VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); - return m_BlockAllocation.m_Block; - } - VkDeviceSize GetOffset() const; - VkDeviceMemory GetMemory() const; - uint32_t GetMemoryTypeIndex() const; - bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; } - void* GetMappedData() const; - bool CanBecomeLost() const; - VmaPool GetPool() const; - - uint32_t GetLastUseFrameIndex() const - { - return m_LastUseFrameIndex.load(); - } - bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired) - { - return m_LastUseFrameIndex.compare_exchange_weak(expected, desired); - } - /* - - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex, - makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true. - - Else, returns false. - - If hAllocation is already lost, assert - you should not call it then. - If hAllocation was not created with CAN_BECOME_LOST_BIT, assert. - */ - bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); - - void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo) - { - VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED); - outInfo.blockCount = 1; - outInfo.allocationCount = 1; - outInfo.unusedRangeCount = 0; - outInfo.usedBytes = m_Size; - outInfo.unusedBytes = 0; - outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size; - outInfo.unusedRangeSizeMin = UINT64_MAX; - outInfo.unusedRangeSizeMax = 0; - } - - void BlockAllocMap(); - void BlockAllocUnmap(); - VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData); - void DedicatedAllocUnmap(VmaAllocator hAllocator); - -private: - VkDeviceSize m_Alignment; - VkDeviceSize m_Size; - void* m_pUserData; - VMA_ATOMIC_UINT32 m_LastUseFrameIndex; - uint8_t m_Type; // ALLOCATION_TYPE - uint8_t m_SuballocationType; // VmaSuballocationType - // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT. - // Bits with mask 0x7F, used only when ALLOCATION_TYPE_DEDICATED, are reference counter for vmaMapMemory()/vmaUnmapMemory(). - uint8_t m_MapCount; - uint8_t m_Flags; // enum FLAGS - - // Allocation out of VmaDeviceMemoryBlock. - struct BlockAllocation - { - VmaPool m_hPool; // Null if belongs to general memory. - VmaDeviceMemoryBlock* m_Block; - VkDeviceSize m_Offset; - bool m_CanBecomeLost; - }; - - // Allocation for an object that has its own private VkDeviceMemory. - struct DedicatedAllocation - { - uint32_t m_MemoryTypeIndex; - VkDeviceMemory m_hMemory; - void* m_pMappedData; // Not null means memory is mapped. - }; - - union - { - // Allocation out of VmaDeviceMemoryBlock. - BlockAllocation m_BlockAllocation; - // Allocation for an object that has its own private VkDeviceMemory. - DedicatedAllocation m_DedicatedAllocation; - }; - - void FreeUserDataString(VmaAllocator hAllocator); -}; - -/* -Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as -allocated memory block or free. -*/ -struct VmaSuballocation -{ - VkDeviceSize offset; - VkDeviceSize size; - VmaAllocation hAllocation; - VmaSuballocationType type; -}; - -typedef VmaList< VmaSuballocation, VmaStlAllocator > VmaSuballocationList; - -// Cost of one additional allocation lost, as equivalent in bytes. -static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576; - -/* -Parameters of planned allocation inside a VmaDeviceMemoryBlock. - -If canMakeOtherLost was false: -- item points to a FREE suballocation. -- itemsToMakeLostCount is 0. - -If canMakeOtherLost was true: -- item points to first of sequence of suballocations, which are either FREE, - or point to VmaAllocations that can become lost. -- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for - the requested allocation to succeed. -*/ -struct VmaAllocationRequest -{ - VkDeviceSize offset; - VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation. - VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation. - VmaSuballocationList::iterator item; - size_t itemsToMakeLostCount; - - VkDeviceSize CalcCost() const - { - return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST; - } -}; - -/* -Data structure used for bookkeeping of allocations and unused ranges of memory -in a single VkDeviceMemory block. -*/ -class VmaBlockMetadata -{ -public: - VmaBlockMetadata(VmaAllocator hAllocator); - ~VmaBlockMetadata(); - void Init(VkDeviceSize size); - - // Validates all data structures inside this object. If not valid, returns false. - bool Validate() const; - VkDeviceSize GetSize() const { return m_Size; } - size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; } - VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; } - VkDeviceSize GetUnusedRangeSizeMax() const; - // Returns true if this block is empty - contains only single free suballocation. - bool IsEmpty() const; - - void CalcAllocationStatInfo(VmaStatInfo& outInfo) const; - void AddPoolStats(VmaPoolStats& inoutStats) const; - -#if VMA_STATS_STRING_ENABLED - void PrintDetailedMap(class VmaJsonWriter& json) const; -#endif - - // Creates trivial request for case when block is empty. - void CreateFirstAllocationRequest(VmaAllocationRequest* pAllocationRequest); - - // Tries to find a place for suballocation with given parameters inside this block. - // If succeeded, fills pAllocationRequest and returns true. - // If failed, returns false. - bool CreateAllocationRequest( - uint32_t currentFrameIndex, - uint32_t frameInUseCount, - VkDeviceSize bufferImageGranularity, - VkDeviceSize allocSize, - VkDeviceSize allocAlignment, - VmaSuballocationType allocType, - bool canMakeOtherLost, - VmaAllocationRequest* pAllocationRequest); - - bool MakeRequestedAllocationsLost( - uint32_t currentFrameIndex, - uint32_t frameInUseCount, - VmaAllocationRequest* pAllocationRequest); - - uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount); - - // Makes actual allocation based on request. Request must already be checked and valid. - void Alloc( - const VmaAllocationRequest& request, - VmaSuballocationType type, - VkDeviceSize allocSize, - VmaAllocation hAllocation); - - // Frees suballocation assigned to given memory region. - void Free(const VmaAllocation allocation); - -private: - VkDeviceSize m_Size; - uint32_t m_FreeCount; - VkDeviceSize m_SumFreeSize; - VmaSuballocationList m_Suballocations; - // Suballocations that are free and have size greater than certain threshold. - // Sorted by size, ascending. - VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize; - - bool ValidateFreeSuballocationList() const; - - // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem. - // If yes, fills pOffset and returns true. If no, returns false. - bool CheckAllocation( - uint32_t currentFrameIndex, - uint32_t frameInUseCount, - VkDeviceSize bufferImageGranularity, - VkDeviceSize allocSize, - VkDeviceSize allocAlignment, - VmaSuballocationType allocType, - VmaSuballocationList::const_iterator suballocItem, - bool canMakeOtherLost, - VkDeviceSize* pOffset, - size_t* itemsToMakeLostCount, - VkDeviceSize* pSumFreeSize, - VkDeviceSize* pSumItemSize) const; - // Given free suballocation, it merges it with following one, which must also be free. - void MergeFreeWithNext(VmaSuballocationList::iterator item); - // Releases given suballocation, making it free. - // Merges it with adjacent free suballocations if applicable. - // Returns iterator to new free suballocation at this place. - VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem); - // Given free suballocation, it inserts it into sorted list of - // m_FreeSuballocationsBySize if it's suitable. - void RegisterFreeSuballocation(VmaSuballocationList::iterator item); - // Given free suballocation, it removes it from sorted list of - // m_FreeSuballocationsBySize if it's suitable. - void UnregisterFreeSuballocation(VmaSuballocationList::iterator item); -}; - -// Helper class that represents mapped memory. Synchronized internally. -class VmaDeviceMemoryMapping -{ -public: - VmaDeviceMemoryMapping(); - ~VmaDeviceMemoryMapping(); - - void* GetMappedData() const { return m_pMappedData; } - - // ppData can be null. - VkResult Map(VmaAllocator hAllocator, VkDeviceMemory hMemory, void **ppData); - void Unmap(VmaAllocator hAllocator, VkDeviceMemory hMemory); - -private: - VMA_MUTEX m_Mutex; - uint32_t m_MapCount; - void* m_pMappedData; -}; - -/* -Represents a single block of device memory (`VkDeviceMemory`) with all the -data about its regions (aka suballocations, `VmaAllocation`), assigned and free. - -Thread-safety: This class must be externally synchronized. -*/ -class VmaDeviceMemoryBlock -{ -public: - uint32_t m_MemoryTypeIndex; - VkDeviceMemory m_hMemory; - VmaDeviceMemoryMapping m_Mapping; - VmaBlockMetadata m_Metadata; - - VmaDeviceMemoryBlock(VmaAllocator hAllocator); - - ~VmaDeviceMemoryBlock() - { - VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); - } - - // Always call after construction. - void Init( - uint32_t newMemoryTypeIndex, - VkDeviceMemory newMemory, - VkDeviceSize newSize); - // Always call before destruction. - void Destroy(VmaAllocator allocator); - - // Validates all data structures inside this object. If not valid, returns false. - bool Validate() const; - - // ppData can be null. - VkResult Map(VmaAllocator hAllocator, void** ppData); - void Unmap(VmaAllocator hAllocator); -}; - -struct VmaPointerLess -{ - bool operator()(const void* lhs, const void* rhs) const - { - return lhs < rhs; - } -}; - -class VmaDefragmentator; - -/* -Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific -Vulkan memory type. - -Synchronized internally with a mutex. -*/ -struct VmaBlockVector -{ - VmaBlockVector( - VmaAllocator hAllocator, - uint32_t memoryTypeIndex, - VkDeviceSize preferredBlockSize, - size_t minBlockCount, - size_t maxBlockCount, - VkDeviceSize bufferImageGranularity, - uint32_t frameInUseCount, - bool isCustomPool); - ~VmaBlockVector(); - - VkResult CreateMinBlocks(); - - uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } - VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; } - VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } - uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; } - - void GetPoolStats(VmaPoolStats* pStats); - - bool IsEmpty() const { return m_Blocks.empty(); } - - VkResult Allocate( - VmaPool hCurrentPool, - uint32_t currentFrameIndex, - const VkMemoryRequirements& vkMemReq, - const VmaAllocationCreateInfo& createInfo, - VmaSuballocationType suballocType, - VmaAllocation* pAllocation); - - void Free( - VmaAllocation hAllocation); - - // Adds statistics of this BlockVector to pStats. - void AddStats(VmaStats* pStats); - -#if VMA_STATS_STRING_ENABLED - void PrintDetailedMap(class VmaJsonWriter& json); -#endif - - void MakePoolAllocationsLost( - uint32_t currentFrameIndex, - size_t* pLostAllocationCount); - - VmaDefragmentator* EnsureDefragmentator( - VmaAllocator hAllocator, - uint32_t currentFrameIndex); - - VkResult Defragment( - VmaDefragmentationStats* pDefragmentationStats, - VkDeviceSize& maxBytesToMove, - uint32_t& maxAllocationsToMove); - - void DestroyDefragmentator(); - -private: - friend class VmaDefragmentator; - - const VmaAllocator m_hAllocator; - const uint32_t m_MemoryTypeIndex; - const VkDeviceSize m_PreferredBlockSize; - const size_t m_MinBlockCount; - const size_t m_MaxBlockCount; - const VkDeviceSize m_BufferImageGranularity; - const uint32_t m_FrameInUseCount; - const bool m_IsCustomPool; - VMA_MUTEX m_Mutex; - // Incrementally sorted by sumFreeSize, ascending. - VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator > m_Blocks; - /* There can be at most one allocation that is completely empty - a - hysteresis to avoid pessimistic case of alternating creation and destruction - of a VkDeviceMemory. */ - bool m_HasEmptyBlock; - VmaDefragmentator* m_pDefragmentator; - - // Finds and removes given block from vector. - void Remove(VmaDeviceMemoryBlock* pBlock); - - // Performs single step in sorting m_Blocks. They may not be fully sorted - // after this call. - void IncrementallySortBlocks(); - - VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); -}; - -struct VmaPool_T -{ -public: - VmaBlockVector m_BlockVector; - - // Takes ownership. - VmaPool_T( - VmaAllocator hAllocator, - const VmaPoolCreateInfo& createInfo); - ~VmaPool_T(); - - VmaBlockVector& GetBlockVector() { return m_BlockVector; } - -#if VMA_STATS_STRING_ENABLED - //void PrintDetailedMap(class VmaStringBuilder& sb); -#endif -}; - -class VmaDefragmentator -{ - const VmaAllocator m_hAllocator; - VmaBlockVector* const m_pBlockVector; - uint32_t m_CurrentFrameIndex; - VkDeviceSize m_BytesMoved; - uint32_t m_AllocationsMoved; - - struct AllocationInfo - { - VmaAllocation m_hAllocation; - VkBool32* m_pChanged; - - AllocationInfo() : - m_hAllocation(VK_NULL_HANDLE), - m_pChanged(VMA_NULL) - { - } - }; - - struct AllocationInfoSizeGreater - { - bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const - { - return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize(); - } - }; - - // Used between AddAllocation and Defragment. - VmaVector< AllocationInfo, VmaStlAllocator > m_Allocations; - - struct BlockInfo - { - VmaDeviceMemoryBlock* m_pBlock; - bool m_HasNonMovableAllocations; - VmaVector< AllocationInfo, VmaStlAllocator > m_Allocations; - - BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) : - m_pBlock(VMA_NULL), - m_HasNonMovableAllocations(true), - m_Allocations(pAllocationCallbacks), - m_pMappedDataForDefragmentation(VMA_NULL) - { - } - - void CalcHasNonMovableAllocations() - { - const size_t blockAllocCount = m_pBlock->m_Metadata.GetAllocationCount(); - const size_t defragmentAllocCount = m_Allocations.size(); - m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount; - } - - void SortAllocationsBySizeDescecnding() - { - VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater()); - } - - VkResult EnsureMapping(VmaAllocator hAllocator, void** ppMappedData); - void Unmap(VmaAllocator hAllocator); - - private: - // Not null if mapped for defragmentation only, not originally mapped. - void* m_pMappedDataForDefragmentation; - }; - - struct BlockPointerLess - { - bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const - { - return pLhsBlockInfo->m_pBlock < pRhsBlock; - } - bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const - { - return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock; - } - }; - - // 1. Blocks with some non-movable allocations go first. - // 2. Blocks with smaller sumFreeSize go first. - struct BlockInfoCompareMoveDestination - { - bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const - { - if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations) - { - return true; - } - if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations) - { - return false; - } - if(pLhsBlockInfo->m_pBlock->m_Metadata.GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_Metadata.GetSumFreeSize()) - { - return true; - } - return false; - } - }; - - typedef VmaVector< BlockInfo*, VmaStlAllocator > BlockInfoVector; - BlockInfoVector m_Blocks; - - VkResult DefragmentRound( - VkDeviceSize maxBytesToMove, - uint32_t maxAllocationsToMove); - - static bool MoveMakesSense( - size_t dstBlockIndex, VkDeviceSize dstOffset, - size_t srcBlockIndex, VkDeviceSize srcOffset); - -public: - VmaDefragmentator( - VmaAllocator hAllocator, - VmaBlockVector* pBlockVector, - uint32_t currentFrameIndex); - - ~VmaDefragmentator(); - - VkDeviceSize GetBytesMoved() const { return m_BytesMoved; } - uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; } - - void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); - - VkResult Defragment( - VkDeviceSize maxBytesToMove, - uint32_t maxAllocationsToMove); -}; - -// Main allocator object. -struct VmaAllocator_T -{ - bool m_UseMutex; - bool m_UseKhrDedicatedAllocation; - VkDevice m_hDevice; - bool m_AllocationCallbacksSpecified; - VkAllocationCallbacks m_AllocationCallbacks; - VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks; - - // Number of bytes free out of limit, or VK_WHOLE_SIZE if not limit for that heap. - VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS]; - VMA_MUTEX m_HeapSizeLimitMutex; - - VkPhysicalDeviceProperties m_PhysicalDeviceProperties; - VkPhysicalDeviceMemoryProperties m_MemProps; - - // Default pools. - VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES]; - - // Each vector is sorted by memory (handle value). - typedef VmaVector< VmaAllocation, VmaStlAllocator > AllocationVectorType; - AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES]; - VMA_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES]; - - VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); - ~VmaAllocator_T(); - - const VkAllocationCallbacks* GetAllocationCallbacks() const - { - return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0; - } - const VmaVulkanFunctions& GetVulkanFunctions() const - { - return m_VulkanFunctions; - } - - VkDeviceSize GetBufferImageGranularity() const - { - return VMA_MAX( - static_cast(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY), - m_PhysicalDeviceProperties.limits.bufferImageGranularity); - } - - uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; } - uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; } - - uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const - { - VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount); - return m_MemProps.memoryTypes[memTypeIndex].heapIndex; - } - - void GetBufferMemoryRequirements( - VkBuffer hBuffer, - VkMemoryRequirements& memReq, - bool& requiresDedicatedAllocation, - bool& prefersDedicatedAllocation) const; - void GetImageMemoryRequirements( - VkImage hImage, - VkMemoryRequirements& memReq, - bool& requiresDedicatedAllocation, - bool& prefersDedicatedAllocation) const; - - // Main allocation function. - VkResult AllocateMemory( - const VkMemoryRequirements& vkMemReq, - bool requiresDedicatedAllocation, - bool prefersDedicatedAllocation, - VkBuffer dedicatedBuffer, - VkImage dedicatedImage, - const VmaAllocationCreateInfo& createInfo, - VmaSuballocationType suballocType, - VmaAllocation* pAllocation); - - // Main deallocation function. - void FreeMemory(const VmaAllocation allocation); - - void CalculateStats(VmaStats* pStats); - -#if VMA_STATS_STRING_ENABLED - void PrintDetailedMap(class VmaJsonWriter& json); -#endif - - VkResult Defragment( - VmaAllocation* pAllocations, - size_t allocationCount, - VkBool32* pAllocationsChanged, - const VmaDefragmentationInfo* pDefragmentationInfo, - VmaDefragmentationStats* pDefragmentationStats); - - void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo); - - VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool); - void DestroyPool(VmaPool pool); - void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats); - - void SetCurrentFrameIndex(uint32_t frameIndex); - - void MakePoolAllocationsLost( - VmaPool hPool, - size_t* pLostAllocationCount); - - void CreateLostAllocation(VmaAllocation* pAllocation); - - VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory); - void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory); - - VkResult Map(VmaAllocation hAllocation, void** ppData); - void Unmap(VmaAllocation hAllocation); - -private: - VkDeviceSize m_PreferredLargeHeapBlockSize; - VkDeviceSize m_PreferredSmallHeapBlockSize; - - VkPhysicalDevice m_PhysicalDevice; - VMA_ATOMIC_UINT32 m_CurrentFrameIndex; - - VMA_MUTEX m_PoolsMutex; - // Protected by m_PoolsMutex. Sorted by pointer value. - VmaVector > m_Pools; - - VmaVulkanFunctions m_VulkanFunctions; - - void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); - - VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); - - VkResult AllocateMemoryOfType( - const VkMemoryRequirements& vkMemReq, - bool dedicatedAllocation, - VkBuffer dedicatedBuffer, - VkImage dedicatedImage, - const VmaAllocationCreateInfo& createInfo, - uint32_t memTypeIndex, - VmaSuballocationType suballocType, - VmaAllocation* pAllocation); - - // Allocates and registers new VkDeviceMemory specifically for single allocation. - VkResult AllocateDedicatedMemory( - VkDeviceSize size, - VmaSuballocationType suballocType, - uint32_t memTypeIndex, - bool map, - bool isUserDataString, - void* pUserData, - VkBuffer dedicatedBuffer, - VkImage dedicatedImage, - VmaAllocation* pAllocation); - - // Tries to free pMemory as Dedicated Memory. Returns true if found and freed. - void FreeDedicatedMemory(VmaAllocation allocation); -}; - -//////////////////////////////////////////////////////////////////////////////// -// Memory allocation #2 after VmaAllocator_T definition - -static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment) -{ - return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment); -} - -static void VmaFree(VmaAllocator hAllocator, void* ptr) -{ - VmaFree(&hAllocator->m_AllocationCallbacks, ptr); -} - -template -static T* VmaAllocate(VmaAllocator hAllocator) -{ - return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T)); -} - -template -static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count) -{ - return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T)); -} - -template -static void vma_delete(VmaAllocator hAllocator, T* ptr) -{ - if(ptr != VMA_NULL) - { - ptr->~T(); - VmaFree(hAllocator, ptr); - } -} - -template -static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count) -{ - if(ptr != VMA_NULL) - { - for(size_t i = count; i--; ) - ptr[i].~T(); - VmaFree(hAllocator, ptr); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// VmaStringBuilder - -#if VMA_STATS_STRING_ENABLED - +#if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED class VmaStringBuilder { public: - VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator(alloc->GetAllocationCallbacks())) { } + VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator(allocationCallbacks)) {} + ~VmaStringBuilder() = default; + size_t GetLength() const { return m_Data.size(); } const char* GetData() const { return m_Data.data(); } - - void Add(char ch) { m_Data.push_back(ch); } - void Add(const char* pStr); void AddNewLine() { Add('\n'); } + void Add(char ch) { m_Data.push_back(ch); } + + void Add(const char* pStr); void AddNumber(uint32_t num); void AddNumber(uint64_t num); void AddPointer(const void* ptr); private: - VmaVector< char, VmaStlAllocator > m_Data; + VmaVector> m_Data; }; +#ifndef _VMA_STRING_BUILDER_FUNCTIONS void VmaStringBuilder::Add(const char* pStr) { const size_t strLen = strlen(pStr); - if(strLen > 0) + if (strLen > 0) { const size_t oldCount = m_Data.size(); m_Data.resize(oldCount + strLen); @@ -4053,15 +5132,27 @@ void VmaStringBuilder::Add(const char* pStr) void VmaStringBuilder::AddNumber(uint32_t num) { char buf[11]; - VmaUint32ToStr(buf, sizeof(buf), num); - Add(buf); + buf[10] = '\0'; + char* p = &buf[10]; + do + { + *--p = '0' + (num % 10); + num /= 10; + } while (num); + Add(p); } void VmaStringBuilder::AddNumber(uint64_t num) { char buf[21]; - VmaUint64ToStr(buf, sizeof(buf), num); - Add(buf); + buf[20] = '\0'; + char* p = &buf[20]; + do + { + *--p = '0' + (num % 10); + num /= 10; + } while (num); + Add(p); } void VmaStringBuilder::AddPointer(const void* ptr) @@ -4070,42 +5161,65 @@ void VmaStringBuilder::AddPointer(const void* ptr) VmaPtrToStr(buf, sizeof(buf), ptr); Add(buf); } +#endif //_VMA_STRING_BUILDER_FUNCTIONS +#endif // _VMA_STRING_BUILDER -#endif // #if VMA_STATS_STRING_ENABLED - -//////////////////////////////////////////////////////////////////////////////// -// VmaJsonWriter - -#if VMA_STATS_STRING_ENABLED - +#if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED +/* +Allows to conveniently build a correct JSON document to be written to the +VmaStringBuilder passed to the constructor. +*/ class VmaJsonWriter { + VMA_CLASS_NO_COPY(VmaJsonWriter) public: + // sb - string builder to write the document to. Must remain alive for the whole lifetime of this object. VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb); ~VmaJsonWriter(); + // Begins object by writing "{". + // Inside an object, you must call pairs of WriteString and a value, e.g.: + // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject(); + // Will write: { "A": 1, "B": 2 } void BeginObject(bool singleLine = false); + // Ends object by writing "}". void EndObject(); - + + // Begins array by writing "[". + // Inside an array, you can write a sequence of any values. void BeginArray(bool singleLine = false); + // Ends array by writing "[". void EndArray(); - + + // Writes a string value inside "". + // pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped. void WriteString(const char* pStr); + + // Begins writing a string value. + // Call BeginString, ContinueString, ContinueString, ..., EndString instead of + // WriteString to conveniently build the string content incrementally, made of + // parts including numbers. void BeginString(const char* pStr = VMA_NULL); + // Posts next part of an open string. void ContinueString(const char* pStr); + // Posts next part of an open string. The number is converted to decimal characters. void ContinueString(uint32_t n); void ContinueString(uint64_t n); + // Posts next part of an open string. Pointer value is converted to characters + // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00 void ContinueString_Pointer(const void* ptr); + // Ends writing a string value by writing '"'. void EndString(const char* pStr = VMA_NULL); - + + // Writes a number value. void WriteNumber(uint32_t n); void WriteNumber(uint64_t n); + // Writes a boolean value - false or true. void WriteBool(bool b); + // Writes a null value. void WriteNull(); private: - static const char* const INDENT; - enum COLLECTION_TYPE { COLLECTION_TYPE_OBJECT, @@ -4118,6 +5232,8 @@ private: bool singleLineMode; }; + static const char* const INDENT; + VmaStringBuilder& m_SB; VmaVector< StackItem, VmaStlAllocator > m_Stack; bool m_InsideString; @@ -4125,15 +5241,13 @@ private: void BeginValue(bool isString); void WriteIndent(bool oneLess = false); }; - const char* const VmaJsonWriter::INDENT = " "; -VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) : - m_SB(sb), +#ifndef _VMA_JSON_WRITER_FUNCTIONS +VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) + : m_SB(sb), m_Stack(VmaStlAllocator(pAllocationCallbacks)), - m_InsideString(false) -{ -} + m_InsideString(false) {} VmaJsonWriter::~VmaJsonWriter() { @@ -4204,7 +5318,7 @@ void VmaJsonWriter::BeginString(const char* pStr) BeginValue(true); m_SB.Add('"'); m_InsideString = true; - if(pStr != VMA_NULL && pStr[0] != '\0') + if (pStr != VMA_NULL && pStr[0] != '\0') { ContinueString(pStr); } @@ -4215,22 +5329,22 @@ void VmaJsonWriter::ContinueString(const char* pStr) VMA_ASSERT(m_InsideString); const size_t strLen = strlen(pStr); - for(size_t i = 0; i < strLen; ++i) + for (size_t i = 0; i < strLen; ++i) { char ch = pStr[i]; - if(ch == '\'') + if (ch == '\\') { m_SB.Add("\\\\"); } - else if(ch == '"') + else if (ch == '"') { m_SB.Add("\\\""); } - else if(ch >= 32) + else if (ch >= 32) { m_SB.Add(ch); } - else switch(ch) + else switch (ch) { case '\b': m_SB.Add("\\b"); @@ -4275,7 +5389,7 @@ void VmaJsonWriter::ContinueString_Pointer(const void* ptr) void VmaJsonWriter::EndString(const char* pStr) { VMA_ASSERT(m_InsideString); - if(pStr != VMA_NULL && pStr[0] != '\0') + if (pStr != VMA_NULL && pStr[0] != '\0') { ContinueString(pStr); } @@ -4313,21 +5427,21 @@ void VmaJsonWriter::WriteNull() void VmaJsonWriter::BeginValue(bool isString) { - if(!m_Stack.empty()) + if (!m_Stack.empty()) { StackItem& currItem = m_Stack.back(); - if(currItem.type == COLLECTION_TYPE_OBJECT && + if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 0) { VMA_ASSERT(isString); } - if(currItem.type == COLLECTION_TYPE_OBJECT && + if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 != 0) { m_SB.Add(": "); } - else if(currItem.valueCount > 0) + else if (currItem.valueCount > 0) { m_SB.Add(", "); WriteIndent(); @@ -4342,41 +5456,6180 @@ void VmaJsonWriter::BeginValue(bool isString) void VmaJsonWriter::WriteIndent(bool oneLess) { - if(!m_Stack.empty() && !m_Stack.back().singleLineMode) + if (!m_Stack.empty() && !m_Stack.back().singleLineMode) { m_SB.AddNewLine(); - + size_t count = m_Stack.size(); - if(count > 0 && oneLess) + if (count > 0 && oneLess) { --count; } - for(size_t i = 0; i < count; ++i) + for (size_t i = 0; i < count; ++i) { m_SB.Add(INDENT); } } } +#endif // _VMA_JSON_WRITER_FUNCTIONS -#endif // #if VMA_STATS_STRING_ENABLED +static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat) +{ + json.BeginObject(); -//////////////////////////////////////////////////////////////////////////////// + json.WriteString("Blocks"); + json.WriteNumber(stat.blockCount); + + json.WriteString("Allocations"); + json.WriteNumber(stat.allocationCount); + + json.WriteString("UnusedRanges"); + json.WriteNumber(stat.unusedRangeCount); + + json.WriteString("UsedBytes"); + json.WriteNumber(stat.usedBytes); + + json.WriteString("UnusedBytes"); + json.WriteNumber(stat.unusedBytes); + + if (stat.allocationCount > 1) + { + json.WriteString("AllocationSize"); + json.BeginObject(true); + json.WriteString("Min"); + json.WriteNumber(stat.allocationSizeMin); + json.WriteString("Avg"); + json.WriteNumber(stat.allocationSizeAvg); + json.WriteString("Max"); + json.WriteNumber(stat.allocationSizeMax); + json.EndObject(); + } + + if (stat.unusedRangeCount > 1) + { + json.WriteString("UnusedRangeSize"); + json.BeginObject(true); + json.WriteString("Min"); + json.WriteNumber(stat.unusedRangeSizeMin); + json.WriteString("Avg"); + json.WriteNumber(stat.unusedRangeSizeAvg); + json.WriteString("Max"); + json.WriteNumber(stat.unusedRangeSizeMax); + json.EndObject(); + } + + json.EndObject(); +} +#endif // _VMA_JSON_WRITER + +#ifndef _VMA_DEVICE_MEMORY_BLOCK +/* +Represents a single block of device memory (`VkDeviceMemory`) with all the +data about its regions (aka suballocations, #VmaAllocation), assigned and free. + +Thread-safety: This class must be externally synchronized. +*/ +class VmaDeviceMemoryBlock +{ + VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock) +public: + VmaBlockMetadata* m_pMetadata; + + VmaDeviceMemoryBlock(VmaAllocator hAllocator); + ~VmaDeviceMemoryBlock(); + + // Always call after construction. + void Init( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm, + VkDeviceSize bufferImageGranularity); + // Always call before destruction. + void Destroy(VmaAllocator allocator); + + VmaPool GetParentPool() const { return m_hParentPool; } + VkDeviceMemory GetDeviceMemory() const { return m_hMemory; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + uint32_t GetId() const { return m_Id; } + void* GetMappedData() const { return m_pMappedData; } + + // Validates all data structures inside this object. If not valid, returns false. + bool Validate() const; + VkResult CheckCorruption(VmaAllocator hAllocator); + + // ppData can be null. + VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData); + void Unmap(VmaAllocator hAllocator, uint32_t count); + + VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + + VkResult BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext); + VkResult BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext); + +private: + VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. + uint32_t m_MemoryTypeIndex; + uint32_t m_Id; + VkDeviceMemory m_hMemory; + + /* + Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory. + Also protects m_MapCount, m_pMappedData. + Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex. + */ + VMA_MUTEX m_Mutex; + uint32_t m_MapCount; + void* m_pMappedData; +}; +#endif // _VMA_DEVICE_MEMORY_BLOCK + +#ifndef _VMA_ALLOCATION_T +struct VmaAllocation_T +{ + friend struct VmaDedicatedAllocationListItemTraits; + + static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80; + + enum FLAGS { FLAG_USER_DATA_STRING = 0x01 }; + +public: + enum ALLOCATION_TYPE + { + ALLOCATION_TYPE_NONE, + ALLOCATION_TYPE_BLOCK, + ALLOCATION_TYPE_DEDICATED, + }; + + // This struct is allocated using VmaPoolAllocator. + VmaAllocation_T(bool userDataString); + ~VmaAllocation_T(); + + void InitBlockAllocation( + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle, + VkDeviceSize alignment, + VkDeviceSize size, + uint32_t memoryTypeIndex, + VmaSuballocationType suballocationType, + bool mapped); + // pMappedData not null means allocation is created with MAPPED flag. + void InitDedicatedAllocation( + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size); + + ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } + VkDeviceSize GetAlignment() const { return m_Alignment; } + VkDeviceSize GetSize() const { return m_Size; } + bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; } + void* GetUserData() const { return m_pUserData; } + VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; } + + VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; } + + void SetUserData(VmaAllocator hAllocator, void* pUserData); + void ChangeBlockAllocation(VmaAllocator hAllocator, VmaDeviceMemoryBlock* block, VmaAllocHandle allocHandle); + void ChangeAllocHandle(VmaAllocHandle newAllocHandle); + VmaAllocHandle GetAllocHandle() const; + VkDeviceSize GetOffset() const; + VmaPool GetParentPool() const; + VkDeviceMemory GetMemory() const; + void* GetMappedData() const; + + void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo); + + void BlockAllocMap(); + void BlockAllocUnmap(); + VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData); + void DedicatedAllocUnmap(VmaAllocator hAllocator); + +#if VMA_STATS_STRING_ENABLED + uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; } + + void InitBufferImageUsage(uint32_t bufferImageUsage); + void PrintParameters(class VmaJsonWriter& json) const; +#endif + +private: + // Allocation out of VmaDeviceMemoryBlock. + struct BlockAllocation + { + VmaDeviceMemoryBlock* m_Block; + VmaAllocHandle m_AllocHandle; + }; + // Allocation for an object that has its own private VkDeviceMemory. + struct DedicatedAllocation + { + VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. + VkDeviceMemory m_hMemory; + void* m_pMappedData; // Not null means memory is mapped. + VmaAllocation_T* m_Prev; + VmaAllocation_T* m_Next; + }; + union + { + // Allocation out of VmaDeviceMemoryBlock. + BlockAllocation m_BlockAllocation; + // Allocation for an object that has its own private VkDeviceMemory. + DedicatedAllocation m_DedicatedAllocation; + }; + + VkDeviceSize m_Alignment; + VkDeviceSize m_Size; + void* m_pUserData; + uint32_t m_MemoryTypeIndex; + uint8_t m_Type; // ALLOCATION_TYPE + uint8_t m_SuballocationType; // VmaSuballocationType + // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT. + // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory(). + uint8_t m_MapCount; + uint8_t m_Flags; // enum FLAGS +#if VMA_STATS_STRING_ENABLED + uint32_t m_BufferImageUsage; // 0 if unknown. +#endif + + void FreeUserDataString(VmaAllocator hAllocator); +}; +#endif // _VMA_ALLOCATION_T + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS +struct VmaDedicatedAllocationListItemTraits +{ + typedef VmaAllocation_T ItemType; + + static ItemType* GetPrev(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType* GetNext(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } + static ItemType*& AccessPrev(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType*& AccessNext(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } +}; +#endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST +/* +Stores linked list of VmaAllocation_T objects. +Thread-safe, synchronized internally. +*/ +class VmaDedicatedAllocationList +{ +public: + VmaDedicatedAllocationList() {} + ~VmaDedicatedAllocationList(); + + void Init(bool useMutex) { m_UseMutex = useMutex; } + bool Validate(); + + void AddStats(VmaStats* stats, uint32_t memTypeIndex, uint32_t memHeapIndex); + void AddPoolStats(VmaPoolStats* stats); +#if VMA_STATS_STRING_ENABLED + // Writes JSON array with the list of allocations. + void BuildStatsString(VmaJsonWriter& json); +#endif + + bool IsEmpty(); + void Register(VmaAllocation alloc); + void Unregister(VmaAllocation alloc); + +private: + typedef VmaIntrusiveLinkedList DedicatedAllocationLinkedList; + + bool m_UseMutex = true; + VMA_RW_MUTEX m_Mutex; + DedicatedAllocationLinkedList m_AllocationList; +}; + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS + +VmaDedicatedAllocationList::~VmaDedicatedAllocationList() +{ + VMA_HEAVY_ASSERT(Validate()); + + if (!m_AllocationList.IsEmpty()) + { + VMA_ASSERT(false && "Unfreed dedicated allocations found!"); + } +} + +bool VmaDedicatedAllocationList::Validate() +{ + const size_t declaredCount = m_AllocationList.GetCount(); + size_t actualCount = 0; + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + ++actualCount; + } + VMA_VALIDATE(actualCount == declaredCount); + + return true; +} + +void VmaDedicatedAllocationList::AddStats(VmaStats* stats, uint32_t memTypeIndex, uint32_t memHeapIndex) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + VmaStatInfo allocationStatInfo; + alloc->DedicatedAllocCalcStatsInfo(allocationStatInfo); + VmaAddStatInfo(stats->total, allocationStatInfo); + VmaAddStatInfo(stats->memoryType[memTypeIndex], allocationStatInfo); + VmaAddStatInfo(stats->memoryHeap[memHeapIndex], allocationStatInfo); + } +} + +void VmaDedicatedAllocationList::AddPoolStats(VmaPoolStats* stats) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + + const size_t allocCount = m_AllocationList.GetCount(); + stats->allocationCount += allocCount; + stats->blockCount += allocCount; + + for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item)) + { + stats->size += item->GetSize(); + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + json.BeginArray(); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + json.BeginObject(true); + alloc->PrintParameters(json); + json.EndObject(); + } + json.EndArray(); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaDedicatedAllocationList::IsEmpty() +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + return m_AllocationList.IsEmpty(); +} + +void VmaDedicatedAllocationList::Register(VmaAllocation alloc) +{ + VmaMutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.PushBack(alloc); +} + +void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc) +{ + VmaMutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.Remove(alloc); +} +#endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS +#endif // _VMA_DEDICATED_ALLOCATION_LIST + +#ifndef _VMA_SUBALLOCATION +/* +Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as +allocated memory block or free. +*/ +struct VmaSuballocation +{ + VkDeviceSize offset; + VkDeviceSize size; + void* userData; + VmaSuballocationType type; +}; + +// Comparator for offsets. +struct VmaSuballocationOffsetLess +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset < rhs.offset; + } +}; + +struct VmaSuballocationOffsetGreater +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset > rhs.offset; + } +}; + +struct VmaSuballocationItemSizeLess +{ + bool operator()(const VmaSuballocationList::iterator lhs, + const VmaSuballocationList::iterator rhs) const + { + return lhs->size < rhs->size; + } + + bool operator()(const VmaSuballocationList::iterator lhs, + VkDeviceSize rhsSize) const + { + return lhs->size < rhsSize; + } +}; +#endif // _VMA_SUBALLOCATION + +#ifndef _VMA_ALLOCATION_REQUEST +/* +Parameters of planned allocation inside a VmaDeviceMemoryBlock. +item points to a FREE suballocation. +*/ +struct VmaAllocationRequest +{ + VmaAllocHandle allocHandle; + VkDeviceSize size; + VmaSuballocationList::iterator item; + void* customData; + uint64_t algorithmData; + VmaAllocationRequestType type; +}; +#endif // _VMA_ALLOCATION_REQUEST + +#ifndef _VMA_BLOCK_METADATA +/* +Data structure used for bookkeeping of allocations and unused ranges of memory +in a single VkDeviceMemory block. +*/ +class VmaBlockMetadata +{ +public: + // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object. + VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata() = default; + + virtual void Init(VkDeviceSize size) { m_Size = size; } + bool IsVirtual() const { return m_IsVirtual; } + VkDeviceSize GetSize() const { return m_Size; } + + // Validates all data structures inside this object. If not valid, returns false. + virtual bool Validate() const = 0; + virtual size_t GetAllocationCount() const = 0; + virtual VkDeviceSize GetSumFreeSize() const = 0; + // Returns true if this block is empty - contains only single free suballocation. + virtual bool IsEmpty() const = 0; + virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0; + virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0; + + // Must set blockCount to 1. + virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0; + // Shouldn't modify blockCount. + virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0; + +#if VMA_STATS_STRING_ENABLED + virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0; +#endif + + // Tries to find a place for suballocation with given parameters inside this block. + // If succeeded, fills pAllocationRequest and returns true. + // If failed, returns false. + virtual bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags. + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) = 0; + + virtual VkResult CheckCorruption(const void* pBlockData) = 0; + + // Makes actual allocation based on request. Request must already be checked and valid. + virtual void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) = 0; + + // Frees suballocation assigned to given memory region. + virtual void Free(VmaAllocHandle allocHandle) = 0; + + // Frees all allocations. + // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations! + virtual void Clear() = 0; + + virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0; + +protected: + const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + VkDeviceSize GetDebugMargin() const { return IsVirtual() ? 0 : VMA_DEBUG_MARGIN; } + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, + size_t allocationCount, + size_t unusedRangeCount) const; + void PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size, void* userData) const; + void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, + VkDeviceSize size) const; + void PrintDetailedMap_End(class VmaJsonWriter& json) const; +#endif + +private: + VkDeviceSize m_Size; + const VkAllocationCallbacks* m_pAllocationCallbacks; + const VkDeviceSize m_BufferImageGranularity; + const bool m_IsVirtual; +}; + +#ifndef _VMA_BLOCK_METADATA_FUNCTIONS +VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : m_Size(0), + m_pAllocationCallbacks(pAllocationCallbacks), + m_BufferImageGranularity(bufferImageGranularity), + m_IsVirtual(isVirtual) {} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const +{ + json.BeginObject(); + + json.WriteString("TotalBytes"); + json.WriteNumber(GetSize()); + + json.WriteString("UnusedBytes"); + json.WriteNumber(unusedBytes); + + json.WriteString("Allocations"); + json.WriteNumber((uint64_t)allocationCount); + + json.WriteString("UnusedRanges"); + json.WriteNumber((uint64_t)unusedRangeCount); + + json.WriteString("Suballocations"); + json.BeginArray(); +} + +void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size, void* userData) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + if (IsVirtual()) + { + json.WriteString("Type"); + json.WriteString("VirtualAllocation"); + + json.WriteString("Size"); + json.WriteNumber(size); + + if (userData != VMA_NULL) + { + json.WriteString("UserData"); + json.BeginString(); + json.ContinueString_Pointer(userData); + json.EndString(); + } + } + else + { + ((VmaAllocation)userData)->PrintParameters(json); + } + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]); + + json.WriteString("Size"); + json.WriteNumber(size); + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const +{ + json.EndArray(); + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_BLOCK_METADATA_FUNCTIONS +#endif // _VMA_BLOCK_METADATA + +#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY +// Before deleting object of this class remember to call 'Destroy()' +class VmaBlockBufferImageGranularity final +{ +public: + struct ValidationContext + { + const VkAllocationCallbacks* allocCallbacks; + uint16_t* pageAllocs; + }; + + VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity); + ~VmaBlockBufferImageGranularity(); + + bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_IMAGE_BUFFER_GRANULARITY; } + + void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size); + // Before destroying object you must call free it's memory + void Destroy(const VkAllocationCallbacks* pAllocationCallbacks); + + void RoundupAllocRequest(VmaSuballocationType allocType, + VkDeviceSize& inOutAllocSize, + VkDeviceSize& inOutAllocAlignment) const; + + bool IsConflict(VkDeviceSize allocSize, + VkDeviceSize allocOffset, + VkDeviceSize blockSize, + VkDeviceSize blockOffset, + VmaSuballocationType allocType) const; + + void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size); + void FreePages(VkDeviceSize offset, VkDeviceSize size); + void Clear(); + + ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks, + bool isVirutal) const; + bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const; + bool FinishValidation(ValidationContext& ctx) const; + +private: + static const uint16_t MAX_LOW_IMAGE_BUFFER_GRANULARITY = 256; + + struct RegionInfo + { + uint8_t allocType; + uint16_t allocCount; + }; + + VkDeviceSize m_BufferImageGranularity; + uint32_t m_RegionCount; + RegionInfo* m_RegionInfo; + + uint32_t GetStartPage(VkDeviceSize offset) const { return PageToIndex(offset & ~(m_BufferImageGranularity - 1)); } + uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return PageToIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); } + + uint32_t PageToIndex(VkDeviceSize offset) const; + void AllocPage(RegionInfo& page, uint8_t allocType); +}; + +#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS +VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity) + : m_BufferImageGranularity(bufferImageGranularity), + m_RegionCount(0), + m_RegionInfo(VMA_NULL) {} + +VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity() +{ + VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!"); +} + +void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size) +{ + if (IsEnabled()) + { + m_RegionCount = static_cast(VmaDivideRoundingUp(size, m_BufferImageGranularity)); + m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount); + memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo)); + } +} + +void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks) +{ + if (m_RegionInfo) + { + vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount); + m_RegionInfo = VMA_NULL; + } +} + +void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType, + VkDeviceSize& inOutAllocSize, + VkDeviceSize& inOutAllocAlignment) const +{ + if (m_BufferImageGranularity > 1 && + m_BufferImageGranularity <= MAX_LOW_IMAGE_BUFFER_GRANULARITY) + { + if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) + { + inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity); + inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity); + } + } +} + +bool VmaBlockBufferImageGranularity::IsConflict(VkDeviceSize allocSize, + VkDeviceSize allocOffset, + VkDeviceSize blockSize, + VkDeviceSize blockOffset, + VmaSuballocationType allocType) const +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(allocOffset); + if (m_RegionInfo[startPage].allocCount > 0 && + VmaIsBufferImageGranularityConflict(static_cast(m_RegionInfo[startPage].allocType), allocType)) + { + allocOffset = VmaAlignUp(allocOffset, m_BufferImageGranularity); + if (blockSize < allocSize + allocOffset - blockOffset) + return true; + ++startPage; + } + uint32_t endPage = GetEndPage(allocOffset, allocSize); + if (endPage != startPage && + m_RegionInfo[endPage].allocCount > 0 && + VmaIsBufferImageGranularityConflict(static_cast(m_RegionInfo[endPage].allocType), allocType)) + { + return true; + } + } + return false; +} + +void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size) +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(offset); + AllocPage(m_RegionInfo[startPage], allocType); + + uint32_t endPage = GetEndPage(offset, size); + if (startPage != endPage) + AllocPage(m_RegionInfo[endPage], allocType); + } +} + +void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size) +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(offset); + --m_RegionInfo[startPage].allocCount; + if (m_RegionInfo[startPage].allocCount == 0) + m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE; + uint32_t endPage = GetEndPage(offset, size); + if (startPage != endPage) + { + --m_RegionInfo[endPage].allocCount; + if (m_RegionInfo[endPage].allocCount == 0) + m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE; + } + } +} + +void VmaBlockBufferImageGranularity::Clear() +{ + if (m_RegionInfo) + memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo)); +} + +VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation( + const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const +{ + ValidationContext ctx{ pAllocationCallbacks, VMA_NULL }; + if (!isVirutal && IsEnabled()) + { + ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount); + memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t)); + } + return ctx; +} + +bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx, + VkDeviceSize offset, VkDeviceSize size) const +{ + if (IsEnabled()) + { + uint32_t start = GetStartPage(offset); + ++ctx.pageAllocs[start]; + VMA_VALIDATE(m_RegionInfo[start].allocCount > 0); + + uint32_t end = GetEndPage(offset, size); + if (start != end) + { + ++ctx.pageAllocs[end]; + VMA_VALIDATE(m_RegionInfo[end].allocCount > 0); + } + } + return true; +} + +bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const +{ + // Check proper page structure + if (IsEnabled()) + { + VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!"); + + for (uint32_t page = 0; page < m_RegionCount; ++page) + { + VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount); + } + vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount); + ctx.pageAllocs = VMA_NULL; + } + return true; +} + +uint32_t VmaBlockBufferImageGranularity::PageToIndex(VkDeviceSize offset) const +{ + return static_cast(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity)); +} + +void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType) +{ + // When current alloc type is free then it can be overriden by new type + if (page.allocCount == 0 || page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE) + page.allocType = allocType; + + ++page.allocCount; +} +#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS +#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY + +#ifndef _VMA_BLOCK_METADATA_GENERIC +class VmaBlockMetadata_Generic : public VmaBlockMetadata +{ + friend class VmaDefragmentationAlgorithm_Generic; + friend class VmaDefragmentationAlgorithm_Fast; + VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic) +public: + VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Generic() = default; + + size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; } + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; } + bool IsEmpty() const override { return (m_Suballocations.size() == 1) && (m_FreeCount == 1); } + void Free(VmaAllocHandle allocHandle) override { FreeSuballocation(FindAtOffset((VkDeviceSize)allocHandle)); } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle; }; + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void CalcAllocationStatInfo(VmaStatInfo& outInfo) const override; + void AddPoolStats(VmaPoolStats& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + + // For defragmentation + bool IsBufferImageGranularityConflictPossible( + VkDeviceSize bufferImageGranularity, + VmaSuballocationType& inOutPrevSuballocType) const; + +private: + uint32_t m_FreeCount; + VkDeviceSize m_SumFreeSize; + VmaSuballocationList m_Suballocations; + // Suballocations that are free. Sorted by size, ascending. + VmaVector> m_FreeSuballocationsBySize; + + VkDeviceSize AlignAllocationSize(VkDeviceSize size) const { return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16); } + + VmaSuballocationList::iterator FindAtOffset(VkDeviceSize offset); + bool ValidateFreeSuballocationList() const; + + // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem. + // If yes, fills pOffset and returns true. If no, returns false. + bool CheckAllocation( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaSuballocationList::const_iterator suballocItem, + VmaAllocHandle* pAllocHandle) const; + + // Given free suballocation, it merges it with following one, which must also be free. + void MergeFreeWithNext(VmaSuballocationList::iterator item); + // Releases given suballocation, making it free. + // Merges it with adjacent free suballocations if applicable. + // Returns iterator to new free suballocation at this place. + VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem); + // Given free suballocation, it inserts it into sorted list of + // m_FreeSuballocationsBySize if it is suitable. + void RegisterFreeSuballocation(VmaSuballocationList::iterator item); + // Given free suballocation, it removes it from sorted list of + // m_FreeSuballocationsBySize if it is suitable. + void UnregisterFreeSuballocation(VmaSuballocationList::iterator item); +}; + +#ifndef _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS +VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_FreeCount(0), + m_SumFreeSize(0), + m_Suballocations(VmaStlAllocator(pAllocationCallbacks)), + m_FreeSuballocationsBySize(VmaStlAllocator(pAllocationCallbacks)) {} + +void VmaBlockMetadata_Generic::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + m_FreeCount = 1; + m_SumFreeSize = size; + + VmaSuballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = size; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + + m_Suballocations.push_back(suballoc); + m_FreeSuballocationsBySize.push_back(m_Suballocations.begin()); +} + +bool VmaBlockMetadata_Generic::Validate() const +{ + VMA_VALIDATE(!m_Suballocations.empty()); + + // Expected offset of new suballocation as calculated from previous ones. + VkDeviceSize calculatedOffset = 0; + // Expected number of free suballocations as calculated from traversing their list. + uint32_t calculatedFreeCount = 0; + // Expected sum size of free suballocations as calculated from traversing their list. + VkDeviceSize calculatedSumFreeSize = 0; + // Expected number of free suballocations that should be registered in + // m_FreeSuballocationsBySize calculated from traversing their list. + size_t freeSuballocationsToRegister = 0; + // True if previous visited suballocation was free. + bool prevFree = false; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + for (const auto& subAlloc : m_Suballocations) + { + // Actual offset of this suballocation doesn't match expected one. + VMA_VALIDATE(subAlloc.offset == calculatedOffset); + + const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE); + // Two adjacent free suballocations are invalid. They should be merged. + VMA_VALIDATE(!prevFree || !currFree); + + VmaAllocation alloc = (VmaAllocation)subAlloc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + + if (currFree) + { + calculatedSumFreeSize += subAlloc.size; + ++calculatedFreeCount; + ++freeSuballocationsToRegister; + + // Margin required between allocations - every free space must be at least that large. + VMA_VALIDATE(subAlloc.size >= debugMargin); + } + else + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == subAlloc.offset); + VMA_VALIDATE(alloc->GetSize() == subAlloc.size); + } + + // Margin required between allocations - previous allocation must be free. + VMA_VALIDATE(debugMargin == 0 || prevFree); + } + + calculatedOffset += subAlloc.size; + prevFree = currFree; + } + + // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't + // match expected one. + VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister); + + VkDeviceSize lastSize = 0; + for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i) + { + VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i]; + + // Only free suballocations can be registered in m_FreeSuballocationsBySize. + VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE); + // They must be sorted by size ascending. + VMA_VALIDATE(suballocItem->size >= lastSize); + + lastSize = suballocItem->size; + } + + // Check if totals match calculated values. + VMA_VALIDATE(ValidateFreeSuballocationList()); + VMA_VALIDATE(calculatedOffset == GetSize()); + VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize); + VMA_VALIDATE(calculatedFreeCount == m_FreeCount); + + return true; +} + +void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const +{ + const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); + VmaInitStatInfo(outInfo); + outInfo.blockCount = 1; + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + VmaAddStatInfoAllocation(outInfo, suballoc.size); + } + else + { + VmaAddStatInfoUnusedRange(outInfo, suballoc.size); + } + } +} + +void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const +{ + const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); + + inoutStats.size += GetSize(); + inoutStats.unusedSize += m_SumFreeSize; + inoutStats.allocationCount += rangeCount - m_FreeCount; + inoutStats.unusedRangeCount += m_FreeCount; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const +{ + PrintDetailedMap_Begin(json, + m_SumFreeSize, // unusedBytes + m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount + m_FreeCount); // unusedRangeCount + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE) + { + PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size); + } + else + { + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + } + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Generic::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(!upperAddress); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + + allocSize = AlignAllocationSize(allocSize); + + pAllocationRequest->type = VmaAllocationRequestType::Normal; + pAllocationRequest->size = allocSize; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + // There is not enough total free space in this block to fulfill the request: Early return. + if (m_SumFreeSize < allocSize + 2 * debugMargin) + { + return false; + } + + // New algorithm, efficiently searching freeSuballocationsBySize. + const size_t freeSuballocCount = m_FreeSuballocationsBySize.size(); + if (freeSuballocCount > 0) + { + if (strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) + { + // Find first free suballocation with size not less than allocSize + 2 * debugMargin. + VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + freeSuballocCount, + allocSize + 2 * debugMargin, + VmaSuballocationItemSizeLess()); + size_t index = it - m_FreeSuballocationsBySize.data(); + for (; index < freeSuballocCount; ++index) + { + if (CheckAllocation( + allocSize, + allocAlignment, + allocType, + m_FreeSuballocationsBySize[index], + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + else if (strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET) + { + for (VmaSuballocationList::iterator it = m_Suballocations.begin(); + it != m_Suballocations.end(); + ++it) + { + if (it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation( + allocSize, + allocAlignment, + allocType, + it, + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = it; + return true; + } + } + } + else // WORST_FIT, FIRST_FIT + { + // Search staring from biggest suballocations. + for (size_t index = freeSuballocCount; index--; ) + { + if (CheckAllocation( + allocSize, + allocAlignment, + allocType, + m_FreeSuballocationsBySize[index], + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + } + + return false; +} + +VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData) +{ + for (auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin())) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN; + } + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Generic::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::Normal); + VMA_ASSERT(request.item != m_Suballocations.end()); + VmaSuballocation& suballoc = *request.item; + // Given suballocation is a free block. + VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + // Given offset is inside this suballocation. + VMA_ASSERT((VkDeviceSize)request.allocHandle >= suballoc.offset); + const VkDeviceSize paddingBegin = (VkDeviceSize)request.allocHandle - suballoc.offset; + VMA_ASSERT(suballoc.size >= paddingBegin + request.size); + const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size; + + // Unregister this free suballocation from m_FreeSuballocationsBySize and update + // it to become used. + UnregisterFreeSuballocation(request.item); + + suballoc.offset = (VkDeviceSize)request.allocHandle; + suballoc.size = request.size; + suballoc.type = type; + suballoc.userData = userData; + + // If there are any free bytes remaining at the end, insert new free suballocation after current one. + if (paddingEnd) + { + VmaSuballocation paddingSuballoc = {}; + paddingSuballoc.offset = suballoc.offset + suballoc.size; + paddingSuballoc.size = paddingEnd; + paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + VmaSuballocationList::iterator next = request.item; + ++next; + const VmaSuballocationList::iterator paddingEndItem = + m_Suballocations.insert(next, paddingSuballoc); + RegisterFreeSuballocation(paddingEndItem); + } + + // If there are any free bytes remaining at the beginning, insert new free suballocation before current one. + if (paddingBegin) + { + VmaSuballocation paddingSuballoc = {}; + paddingSuballoc.offset = suballoc.offset - paddingBegin; + paddingSuballoc.size = paddingBegin; + paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + const VmaSuballocationList::iterator paddingBeginItem = + m_Suballocations.insert(request.item, paddingSuballoc); + RegisterFreeSuballocation(paddingBeginItem); + } + + // Update totals. + m_FreeCount = m_FreeCount - 1; + if (paddingBegin > 0) + { + ++m_FreeCount; + } + if (paddingEnd > 0) + { + ++m_FreeCount; + } + m_SumFreeSize -= request.size; +} + +void VmaBlockMetadata_Generic::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + const VmaSuballocation& suballoc = *FindAtOffset((VkDeviceSize)allocHandle); + outInfo.offset = (VkDeviceSize)allocHandle; + outInfo.size = suballoc.size; + outInfo.pUserData = suballoc.userData; +} + +void VmaBlockMetadata_Generic::Clear() +{ + const VkDeviceSize size = GetSize(); + + VMA_ASSERT(IsVirtual()); + m_FreeCount = 1; + m_SumFreeSize = size; + m_Suballocations.clear(); + m_FreeSuballocationsBySize.clear(); + + VmaSuballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = size; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + m_Suballocations.push_back(suballoc); + + m_FreeSuballocationsBySize.push_back(m_Suballocations.begin()); +} + +void VmaBlockMetadata_Generic::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + VmaSuballocation& suballoc = *FindAtOffset((VkDeviceSize)allocHandle); + suballoc.userData = userData; +} + +VmaSuballocationList::iterator VmaBlockMetadata_Generic::FindAtOffset(VkDeviceSize offset) +{ + VMA_HEAVY_ASSERT(!m_Suballocations.empty()); + const VkDeviceSize last = m_Suballocations.rbegin()->offset; + if (last == offset) + return m_Suballocations.rbegin(); + const VkDeviceSize first = m_Suballocations.begin()->offset; + if (first == offset) + return m_Suballocations.begin(); + + const size_t suballocCount = m_Suballocations.size(); + const VkDeviceSize step = (last - first + m_Suballocations.begin()->size) / suballocCount; + auto findSuballocation = [&](auto begin, auto end) -> VmaSuballocationList::iterator + { + for (auto suballocItem = begin; + suballocItem != end; + ++suballocItem) + { + VmaSuballocation& suballoc = *suballocItem; + if (suballoc.offset == offset) + return suballocItem; + } + VMA_ASSERT(false && "Not found!"); + return m_Suballocations.end(); + }; + // If requested offset is closer to the end of range, search from the end + if (offset - first > suballocCount * step / 2) + { + return findSuballocation(m_Suballocations.rbegin(), m_Suballocations.rend()); + } + return findSuballocation(m_Suballocations.begin(), m_Suballocations.end()); +} + +bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const +{ + VkDeviceSize lastSize = 0; + for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i) + { + const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i]; + + VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_VALIDATE(it->size >= lastSize); + lastSize = it->size; + } + return true; +} + +bool VmaBlockMetadata_Generic::CheckAllocation( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaSuballocationList::const_iterator suballocItem, + VmaAllocHandle* pAllocHandle) const +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(suballocItem != m_Suballocations.cend()); + VMA_ASSERT(pAllocHandle != VMA_NULL); + + const VkDeviceSize debugMargin = GetDebugMargin(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + + const VmaSuballocation& suballoc = *suballocItem; + VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + // Size of this suballocation is too small for this request: Early return. + if (suballoc.size < allocSize) + { + return false; + } + + // Start from offset equal to beginning of this suballocation. + VkDeviceSize offset = suballoc.offset; + + // Apply debugMargin at the beginning. + if (debugMargin > 0) + { + offset += debugMargin; + } + + // Apply alignment. + offset = VmaAlignUp(offset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment) + { + bool bufferImageGranularityConflict = false; + VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; + while (prevSuballocItem != m_Suballocations.cbegin()) + { + --prevSuballocItem; + const VmaSuballocation& prevSuballoc = *prevSuballocItem; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + offset = VmaAlignUp(offset, bufferImageGranularity); + } + } + + // Calculate padding at the beginning based on current offset. + const VkDeviceSize paddingBegin = offset - suballoc.offset; + + // Calculate required margin at the end. + const VkDeviceSize requiredEndMargin = debugMargin; + + // Fail if requested size plus margin before and after is bigger than size of this suballocation. + if (paddingBegin + allocSize + requiredEndMargin > suballoc.size) + { + return false; + } + + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (allocSize % bufferImageGranularity || offset % bufferImageGranularity) + { + VmaSuballocationList::const_iterator nextSuballocItem = suballocItem; + ++nextSuballocItem; + while (nextSuballocItem != m_Suballocations.cend()) + { + const VmaSuballocation& nextSuballoc = *nextSuballocItem; + if (VmaBlocksOnSamePage(offset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + ++nextSuballocItem; + } + } + + *pAllocHandle = (VmaAllocHandle)offset; + // All tests passed: Success. pAllocHandle is already filled. + return true; +} + +void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item != m_Suballocations.end()); + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaSuballocationList::iterator nextItem = item; + ++nextItem; + VMA_ASSERT(nextItem != m_Suballocations.end()); + VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE); + + item->size += nextItem->size; + --m_FreeCount; + m_Suballocations.erase(nextItem); +} + +VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem) +{ + // Change this suballocation to be marked as free. + VmaSuballocation& suballoc = *suballocItem; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + suballoc.userData = VMA_NULL; + + // Update totals. + ++m_FreeCount; + m_SumFreeSize += suballoc.size; + + // Merge with previous and/or next suballocation if it's also free. + bool mergeWithNext = false; + bool mergeWithPrev = false; + + VmaSuballocationList::iterator nextItem = suballocItem; + ++nextItem; + if ((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)) + { + mergeWithNext = true; + } + + VmaSuballocationList::iterator prevItem = suballocItem; + if (suballocItem != m_Suballocations.begin()) + { + --prevItem; + if (prevItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + mergeWithPrev = true; + } + } + + if (mergeWithNext) + { + UnregisterFreeSuballocation(nextItem); + MergeFreeWithNext(suballocItem); + } + + if (mergeWithPrev) + { + UnregisterFreeSuballocation(prevItem); + MergeFreeWithNext(prevItem); + RegisterFreeSuballocation(prevItem); + return prevItem; + } + else + { + RegisterFreeSuballocation(suballocItem); + return suballocItem; + } +} + +void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + if (m_FreeSuballocationsBySize.empty()) + { + m_FreeSuballocationsBySize.push_back(item); + } + else + { + VmaVectorInsertSorted(m_FreeSuballocationsBySize, item); + } + + //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} + +void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(), + item, + VmaSuballocationItemSizeLess()); + for (size_t index = it - m_FreeSuballocationsBySize.data(); + index < m_FreeSuballocationsBySize.size(); + ++index) + { + if (m_FreeSuballocationsBySize[index] == item) + { + VmaVectorRemove(m_FreeSuballocationsBySize, index); + return; + } + VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found."); + } + VMA_ASSERT(0 && "Not found."); + + //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} + +bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible( + VkDeviceSize bufferImageGranularity, + VmaSuballocationType& inOutPrevSuballocType) const +{ + if (bufferImageGranularity == 1 || IsEmpty() || IsVirtual()) + { + return false; + } + + VkDeviceSize minAlignment = VK_WHOLE_SIZE; + bool typeConflictFound = false; + for (const auto& suballoc : m_Suballocations) + { + const VmaSuballocationType suballocType = suballoc.type; + if (suballocType != VMA_SUBALLOCATION_TYPE_FREE) + { + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + minAlignment = VMA_MIN(minAlignment, alloc->GetAlignment()); + if (VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType)) + { + typeConflictFound = true; + } + inOutPrevSuballocType = suballocType; + } + } + + return typeConflictFound || minAlignment >= bufferImageGranularity; +} +#endif // _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_GENERIC + +#ifndef _VMA_BLOCK_METADATA_LINEAR +/* +Allocations and their references in internal data structure look like this: + +if(m_2ndVectorMode == SECOND_VECTOR_EMPTY): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER): + + 0 +-------+ + | Alloc | 2nd[0] + +-------+ + | Alloc | 2nd[1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[1] + +-------+ + | Alloc | 2nd[0] +GetSize() +-------+ + +*/ +class VmaBlockMetadata_Linear : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear) +public: + VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Linear() = default; + + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; } + bool IsEmpty() const override { return GetAllocationCount() == 0; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle; }; + + void Init(VkDeviceSize size) override; + bool Validate() const override; + size_t GetAllocationCount() const override; + + void CalcAllocationStatInfo(VmaStatInfo& outInfo) const override; + void AddPoolStats(VmaPoolStats& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + +private: + /* + There are two suballocation vectors, used in ping-pong way. + The one with index m_1stVectorIndex is called 1st. + The one with index (m_1stVectorIndex ^ 1) is called 2nd. + 2nd can be non-empty only when 1st is not empty. + When 2nd is not empty, m_2ndVectorMode indicates its mode of operation. + */ + typedef VmaVector> SuballocationVectorType; + + enum SECOND_VECTOR_MODE + { + SECOND_VECTOR_EMPTY, + /* + Suballocations in 2nd vector are created later than the ones in 1st, but they + all have smaller offset. + */ + SECOND_VECTOR_RING_BUFFER, + /* + Suballocations in 2nd vector are upper side of double stack. + They all have offsets higher than those in 1st vector. + Top of this stack means smaller offsets, but higher indices in this vector. + */ + SECOND_VECTOR_DOUBLE_STACK, + }; + + VkDeviceSize m_SumFreeSize; + SuballocationVectorType m_Suballocations0, m_Suballocations1; + uint32_t m_1stVectorIndex; + SECOND_VECTOR_MODE m_2ndVectorMode; + // Number of items in 1st vector with hAllocation = null at the beginning. + size_t m_1stNullItemsBeginCount; + // Number of other items in 1st vector with hAllocation = null somewhere in the middle. + size_t m_1stNullItemsMiddleCount; + // Number of items in 2nd vector with hAllocation = null. + size_t m_2ndNullItemsCount; + + SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + + VmaSuballocation& FindSuballocation(VkDeviceSize offset); + bool ShouldCompact1st() const; + void CleanupAfterFree(); + + bool CreateAllocationRequest_LowerAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); + bool CreateAllocationRequest_UpperAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); +}; + +#ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS +VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_SumFreeSize(0), + m_Suballocations0(VmaStlAllocator(pAllocationCallbacks)), + m_Suballocations1(VmaStlAllocator(pAllocationCallbacks)), + m_1stVectorIndex(0), + m_2ndVectorMode(SECOND_VECTOR_EMPTY), + m_1stNullItemsBeginCount(0), + m_1stNullItemsMiddleCount(0), + m_2ndNullItemsCount(0) {} + +void VmaBlockMetadata_Linear::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + m_SumFreeSize = size; +} + +bool VmaBlockMetadata_Linear::Validate() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY)); + VMA_VALIDATE(!suballocations1st.empty() || + suballocations2nd.empty() || + m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER); + + if (!suballocations1st.empty()) + { + // Null item at the beginning should be accounted into m_1stNullItemsBeginCount. + VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE); + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE); + } + if (!suballocations2nd.empty()) + { + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE); + } + + VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size()); + VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size()); + + VkDeviceSize sumUsedSize = 0; + const size_t suballoc1stCount = suballocations1st.size(); + const VkDeviceSize debugMargin = GetDebugMargin(); + VkDeviceSize offset = debugMargin; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = 0; i < suballoc2ndCount; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE && + suballoc.userData == VMA_NULL); + } + + size_t nullItem1stCount = m_1stNullItemsBeginCount; + + for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem1stCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount); + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = suballoc2ndCount; i--; ) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + VMA_VALIDATE(offset <= GetSize()); + VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize); + + return true; +} + +size_t VmaBlockMetadata_Linear::GetAllocationCount() const +{ + return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount + + AccessSuballocations2nd().size() - m_2ndNullItemsCount; +} + +void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + VmaInitStatInfo(outInfo); + outInfo.blockCount = 1; + + VkDeviceSize lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddStatInfoAllocation(outInfo, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + if (lastOffset < freeSpace2ndTo1stEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddStatInfoAllocation(outInfo, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + if (lastOffset < freeSpace1stTo2ndEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddStatInfoAllocation(outInfo, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to size. + if (lastOffset < size) + { + const VkDeviceSize unusedRangeSize = size - lastOffset; + VmaAddStatInfoUnusedRange(outInfo, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } + + outInfo.unusedBytes = size - outInfo.usedBytes; +} + +void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const VkDeviceSize size = GetSize(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + inoutStats.size += size; + + VkDeviceSize lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + const VkDeviceSize unusedRangeSize = size - lastOffset; + inoutStats.unusedSize += unusedRangeSize; + ++inoutStats.unusedRangeCount; + } + + // End of loop. + lastOffset = size; + } + } + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + // FIRST PASS + + size_t unusedRangeCount = 0; + VkDeviceSize usedBytes = 0; + + VkDeviceSize lastOffset = 0; + + size_t alloc2ndCount = 0; + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + size_t alloc1stCount = 0; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc1stCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = size; + } + } + } + + const VkDeviceSize unusedBytes = size - usedBytes; + PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount); + + // SECOND PASS + lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + nextAlloc1stIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + const VkDeviceSize unusedRangeSize = size - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Linear::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + pAllocationRequest->size = allocSize; + return upperAddress ? + CreateAllocationRequest_UpperAddress( + allocSize, allocAlignment, allocType, strategy, pAllocationRequest) : + CreateAllocationRequest_LowerAddress( + allocSize, allocAlignment, allocType, strategy, pAllocationRequest); +} + +VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData) +{ + VMA_ASSERT(!IsVirtual()); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin())) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN; + } + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN; + } + } + } + + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset - GetDebugMargin())) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN; + } + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Linear::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + const VmaSuballocation newSuballoc = { (VkDeviceSize)request.allocHandle, request.size, userData, type }; + + switch (request.type) + { + case VmaAllocationRequestType::UpperAddress: + { + VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER && + "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer."); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + suballocations2nd.push_back(newSuballoc); + m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK; + } + break; + case VmaAllocationRequestType::EndOf1st: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + + VMA_ASSERT(suballocations1st.empty() || + (VkDeviceSize)request.allocHandle >= suballocations1st.back().offset + suballocations1st.back().size); + // Check if it fits before the end of the block. + VMA_ASSERT((VkDeviceSize)request.allocHandle + request.size <= GetSize()); + + suballocations1st.push_back(newSuballoc); + } + break; + case VmaAllocationRequestType::EndOf2nd: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector. + VMA_ASSERT(!suballocations1st.empty() && + (VkDeviceSize)request.allocHandle + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + switch (m_2ndVectorMode) + { + case SECOND_VECTOR_EMPTY: + // First allocation from second part ring buffer. + VMA_ASSERT(suballocations2nd.empty()); + m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER; + break; + case SECOND_VECTOR_RING_BUFFER: + // 2-part ring buffer is already started. + VMA_ASSERT(!suballocations2nd.empty()); + break; + case SECOND_VECTOR_DOUBLE_STACK: + VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack."); + break; + default: + VMA_ASSERT(0); + } + + suballocations2nd.push_back(newSuballoc); + } + break; + default: + VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR."); + } + + m_SumFreeSize -= newSuballoc.size; +} + +void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle) +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + VkDeviceSize offset = (VkDeviceSize)allocHandle; + + if (!suballocations1st.empty()) + { + // First allocation: Mark it as next empty at the beginning. + VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; + if (firstSuballoc.offset == offset) + { + firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + firstSuballoc.userData = VMA_NULL; + m_SumFreeSize += firstSuballoc.size; + ++m_1stNullItemsBeginCount; + CleanupAfterFree(); + return; + } + } + + // Last allocation in 2-part ring buffer or top of upper stack (same logic). + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER || + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + VmaSuballocation& lastSuballoc = suballocations2nd.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations2nd.pop_back(); + CleanupAfterFree(); + return; + } + } + // Last allocation in 1st vector. + else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY) + { + VmaSuballocation& lastSuballoc = suballocations1st.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations1st.pop_back(); + CleanupAfterFree(); + return; + } + } + + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the middle of 1st vector. + { + const SuballocationVectorType::iterator it = VmaBinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + VmaSuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->userData = VMA_NULL; + ++m_1stNullItemsMiddleCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Item from the middle of 2nd vector. + const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) : + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->userData = VMA_NULL; + ++m_2ndNullItemsCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + VMA_ASSERT(0 && "Allocation to free not found in linear allocator!"); +} + +void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle); + outInfo.offset = (VkDeviceSize)allocHandle; + outInfo.size = suballoc.size; + outInfo.pUserData = suballoc.userData; +} + +void VmaBlockMetadata_Linear::Clear() +{ + m_SumFreeSize = GetSize(); + m_Suballocations0.clear(); + m_Suballocations1.clear(); + // Leaving m_1stVectorIndex unchanged - it doesn't matter. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; +} + +void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle); + suballoc.userData = userData; +} + +VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the 1st vector. + { + const SuballocationVectorType::iterator it = VmaBinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + VmaSuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + return *it; + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Rest of members stays uninitialized intentionally for better performance. + const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) : + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + return *it; + } + } + + VMA_ASSERT(0 && "Allocation not found in linear allocator!"); + return suballocations1st.back(); // Should never occur. +} + +bool VmaBlockMetadata_Linear::ShouldCompact1st() const +{ + const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + const size_t suballocCount = AccessSuballocations1st().size(); + return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3; +} + +void VmaBlockMetadata_Linear::CleanupAfterFree() +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (IsEmpty()) + { + suballocations1st.clear(); + suballocations2nd.clear(); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + else + { + const size_t suballoc1stCount = suballocations1st.size(); + const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + VMA_ASSERT(nullItem1stCount <= suballoc1stCount); + + // Find more null items at the beginning of 1st vector. + while (m_1stNullItemsBeginCount < suballoc1stCount && + suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + + // Find more null items at the end of 1st vector. + while (m_1stNullItemsMiddleCount > 0 && + suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_1stNullItemsMiddleCount; + suballocations1st.pop_back(); + } + + // Find more null items at the end of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + suballocations2nd.pop_back(); + } + + // Find more null items at the beginning of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + VmaVectorRemove(suballocations2nd, 0); + } + + if (ShouldCompact1st()) + { + const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount; + size_t srcIndex = m_1stNullItemsBeginCount; + for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex) + { + while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++srcIndex; + } + if (dstIndex != srcIndex) + { + suballocations1st[dstIndex] = suballocations1st[srcIndex]; + } + ++srcIndex; + } + suballocations1st.resize(nonNullItemCount); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + } + + // 2nd vector became empty. + if (suballocations2nd.empty()) + { + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + + // 1st vector became empty. + if (suballocations1st.size() - m_1stNullItemsBeginCount == 0) + { + suballocations1st.clear(); + m_1stNullItemsBeginCount = 0; + + if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + // Swap 1st with 2nd. Now 2nd is empty. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsMiddleCount = m_2ndNullItemsCount; + while (m_1stNullItemsBeginCount < suballocations2nd.size() && + suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + m_2ndNullItemsCount = 0; + m_1stVectorIndex ^= 1; + } + } + } + + VMA_HEAVY_ASSERT(Validate()); +} + +bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + const VkDeviceSize blockSize = GetSize(); + const VkDeviceSize debugMargin = GetDebugMargin(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + // Try to allocate at the end of 1st vector. + + VkDeviceSize resultBaseOffset = 0; + if (!suballocations1st.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations1st.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply debugMargin at the beginning. + if (debugMargin > 0) + { + resultOffset += debugMargin; + } + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? + suballocations2nd.back().offset : blockSize; + + // There is enough free space at the end after alignment. + if (resultOffset + allocSize + debugMargin <= freeSpaceEnd) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on previous page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)resultOffset; + // pAllocationRequest->item, customData unused. + pAllocationRequest->type = VmaAllocationRequestType::EndOf1st; + return true; + } + } + + // Wrap-around to end of 2nd vector. Try to allocate there, watching for the + // beginning of 1st vector as the end of free space. + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(!suballocations1st.empty()); + + VkDeviceSize resultBaseOffset = 0; + if (!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply debugMargin at the beginning. + if (debugMargin > 0) + { + resultOffset += debugMargin; + } + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + size_t index1st = m_1stNullItemsBeginCount; + + // There is enough free space at the end after alignment. + if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) || + (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset)) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) + { + for (size_t nextSuballocIndex = index1st; + nextSuballocIndex < suballocations1st.size(); + nextSuballocIndex++) + { + const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)resultOffset; + pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd; + // pAllocationRequest->item, customData unused. + return true; + } + } + + return false; +} + +bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + const VkDeviceSize blockSize = GetSize(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer."); + return false; + } + + // Try to allocate before 2nd.back(), or end of block if 2nd.empty(). + if (allocSize > blockSize) + { + return false; + } + VkDeviceSize resultBaseOffset = blockSize - allocSize; + if (!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset - allocSize; + if (allocSize > lastSuballoc.offset) + { + return false; + } + } + + // Start from offset equal to end of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + // Apply debugMargin at the end. + if (debugMargin > 0) + { + if (resultOffset < debugMargin) + { + return false; + } + resultOffset -= debugMargin; + } + + // Apply alignment. + resultOffset = VmaAlignDown(resultOffset, allocAlignment); + + // Check next suballocations from 2nd for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity); + } + } + + // There is enough free space. + const VkDeviceSize endOf1st = !suballocations1st.empty() ? + suballocations1st.back().offset + suballocations1st.back().size : + 0; + if (endOf1st + debugMargin <= resultOffset) + { + // Check previous suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (bufferImageGranularity > 1) + { + for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)resultOffset; + // pAllocationRequest->item unused. + pAllocationRequest->type = VmaAllocationRequestType::UpperAddress; + return true; + } + + return false; +} +#endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_LINEAR + +#ifndef _VMA_BLOCK_METADATA_BUDDY +/* +- GetSize() is the original size of allocated memory block. +- m_UsableSize is this size aligned down to a power of two. + All allocations and calculations happen relative to m_UsableSize. +- GetUnusableSize() is the difference between them. + It is reported as separate, unused range, not available for allocations. + +Node at level 0 has size = m_UsableSize. +Each next level contains nodes with size 2 times smaller than current level. +m_LevelCount is the maximum number of levels to use in the current object. +*/ +class VmaBlockMetadata_Buddy : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy) +public: + VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Buddy(); + + size_t GetAllocationCount() const override { return m_AllocationCount; } + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize + GetUnusableSize(); } + bool IsEmpty() const override { return m_Root->type == Node::TYPE_FREE; } + VkResult CheckCorruption(const void* pBlockData) override { return VK_ERROR_FEATURE_NOT_PRESENT; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle; }; + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void CalcAllocationStatInfo(VmaStatInfo& outInfo) const override; + void AddPoolStats(VmaPoolStats& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + +private: + static const size_t MAX_LEVELS = 48; + + struct ValidationContext + { + size_t calculatedAllocationCount = 0; + size_t calculatedFreeCount = 0; + VkDeviceSize calculatedSumFreeSize = 0; + }; + struct Node + { + VkDeviceSize offset; + enum TYPE + { + TYPE_FREE, + TYPE_ALLOCATION, + TYPE_SPLIT, + TYPE_COUNT + } type; + Node* parent; + Node* buddy; + + union + { + struct + { + Node* prev; + Node* next; + } free; + struct + { + void* userData; + } allocation; + struct + { + Node* leftChild; + } split; + }; + }; + + // Size of the memory block aligned down to a power of two. + VkDeviceSize m_UsableSize; + uint32_t m_LevelCount; + VmaPoolAllocator m_NodeAllocator; + Node* m_Root; + struct + { + Node* front; + Node* back; + } m_FreeList[MAX_LEVELS]; + + // Number of nodes in the tree with type == TYPE_ALLOCATION. + size_t m_AllocationCount; + // Number of nodes in the tree with type == TYPE_FREE. + size_t m_FreeCount; + // Doesn't include space wasted due to internal fragmentation - allocation sizes are just aligned up to node sizes. + // Doesn't include unusable size. + VkDeviceSize m_SumFreeSize; + + VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; } + VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; } + + VkDeviceSize AlignAllocationSize(VkDeviceSize size) const + { + if (!IsVirtual()) + { + size = VmaAlignUp(size, (VkDeviceSize)16); + } + return VmaNextPow2(size); + } + Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel); + void DeleteNodeChildren(Node* node); + bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const; + uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const; + void CalcAllocationStatInfoNode(VmaStatInfo& inoutInfo, const Node* node, VkDeviceSize levelNodeSize) const; + // Adds node to the front of FreeList at given level. + // node->type must be FREE. + // node->free.prev, next can be undefined. + void AddToFreeListFront(uint32_t level, Node* node); + // Removes node from FreeList at given level. + // node->type must be FREE. + // node->free.prev, next stay untouched. + void RemoveFromFreeList(uint32_t level, Node* node); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const; +#endif +}; + +#ifndef _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS +VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_NodeAllocator(pAllocationCallbacks, 32), // firstBlockCapacity + m_Root(VMA_NULL), + m_AllocationCount(0), + m_FreeCount(1), + m_SumFreeSize(0) +{ + memset(m_FreeList, 0, sizeof(m_FreeList)); +} + +VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy() +{ + DeleteNodeChildren(m_Root); + m_NodeAllocator.Free(m_Root); +} + +void VmaBlockMetadata_Buddy::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + m_UsableSize = VmaPrevPow2(size); + m_SumFreeSize = m_UsableSize; + + // Calculate m_LevelCount. + const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16; + m_LevelCount = 1; + while (m_LevelCount < MAX_LEVELS && + LevelToNodeSize(m_LevelCount) >= minNodeSize) + { + ++m_LevelCount; + } + + Node* rootNode = m_NodeAllocator.Alloc(); + rootNode->offset = 0; + rootNode->type = Node::TYPE_FREE; + rootNode->parent = VMA_NULL; + rootNode->buddy = VMA_NULL; + + m_Root = rootNode; + AddToFreeListFront(0, rootNode); +} + +bool VmaBlockMetadata_Buddy::Validate() const +{ + // Validate tree. + ValidationContext ctx; + if (!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0))) + { + VMA_VALIDATE(false && "ValidateNode failed."); + } + VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount); + VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize); + + // Validate free node lists. + for (uint32_t level = 0; level < m_LevelCount; ++level) + { + VMA_VALIDATE(m_FreeList[level].front == VMA_NULL || + m_FreeList[level].front->free.prev == VMA_NULL); + + for (Node* node = m_FreeList[level].front; + node != VMA_NULL; + node = node->free.next) + { + VMA_VALIDATE(node->type == Node::TYPE_FREE); + + if (node->free.next == VMA_NULL) + { + VMA_VALIDATE(m_FreeList[level].back == node); + } + else + { + VMA_VALIDATE(node->free.next->free.prev == node); + } + } + } + + // Validate that free lists ar higher levels are empty. + for (uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level) + { + VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL); + } + + return true; +} + +void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const +{ + VmaInitStatInfo(outInfo); + outInfo.blockCount = 1; + + CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0)); + + const VkDeviceSize unusableSize = GetUnusableSize(); + if (unusableSize > 0) + { + VmaAddStatInfoUnusedRange(outInfo, unusableSize); + } +} + +void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const +{ + const VkDeviceSize unusableSize = GetUnusableSize(); + + inoutStats.size += GetSize(); + inoutStats.unusedSize += m_SumFreeSize + unusableSize; + inoutStats.allocationCount += m_AllocationCount; + inoutStats.unusedRangeCount += m_FreeCount; + + if (unusableSize > 0) + { + ++inoutStats.unusedRangeCount; + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const +{ + VmaStatInfo stat; + CalcAllocationStatInfo(stat); + + PrintDetailedMap_Begin( + json, + stat.unusedBytes, + stat.allocationCount, + stat.unusedRangeCount); + + PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0)); + + const VkDeviceSize unusableSize = GetUnusableSize(); + if (unusableSize > 0) + { + PrintDetailedMap_UnusedRange(json, + m_UsableSize, // offset + unusableSize); // size + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Buddy::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); + + allocSize = AlignAllocationSize(allocSize); + + // Simple way to respect bufferImageGranularity. May be optimized some day. + // Whenever it might be an OPTIMAL image... + if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) + { + allocAlignment = VMA_MAX(allocAlignment, GetBufferImageGranularity()); + allocSize = VmaAlignUp(allocSize, GetBufferImageGranularity()); + } + + if (allocSize > m_UsableSize) + { + return false; + } + + const uint32_t targetLevel = AllocSizeToLevel(allocSize); + for (uint32_t level = targetLevel; level--; ) + { + for (Node* freeNode = m_FreeList[level].front; + freeNode != VMA_NULL; + freeNode = freeNode->free.next) + { + if (freeNode->offset % allocAlignment == 0) + { + pAllocationRequest->type = VmaAllocationRequestType::Normal; + pAllocationRequest->allocHandle = (VmaAllocHandle)freeNode->offset; + pAllocationRequest->size = allocSize; + pAllocationRequest->customData = (void*)(uintptr_t)level; + return true; + } + } + } + + return false; +} + +void VmaBlockMetadata_Buddy::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::Normal); + + const uint32_t targetLevel = AllocSizeToLevel(request.size); + uint32_t currLevel = (uint32_t)(uintptr_t)request.customData; + + Node* currNode = m_FreeList[currLevel].front; + VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); + while (currNode->offset != (VkDeviceSize)request.allocHandle) + { + currNode = currNode->free.next; + VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); + } + + // Go down, splitting free nodes. + while (currLevel < targetLevel) + { + // currNode is already first free node at currLevel. + // Remove it from list of free nodes at this currLevel. + RemoveFromFreeList(currLevel, currNode); + + const uint32_t childrenLevel = currLevel + 1; + + // Create two free sub-nodes. + Node* leftChild = m_NodeAllocator.Alloc(); + Node* rightChild = m_NodeAllocator.Alloc(); + + leftChild->offset = currNode->offset; + leftChild->type = Node::TYPE_FREE; + leftChild->parent = currNode; + leftChild->buddy = rightChild; + + rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel); + rightChild->type = Node::TYPE_FREE; + rightChild->parent = currNode; + rightChild->buddy = leftChild; + + // Convert current currNode to split type. + currNode->type = Node::TYPE_SPLIT; + currNode->split.leftChild = leftChild; + + // Add child nodes to free list. Order is important! + AddToFreeListFront(childrenLevel, rightChild); + AddToFreeListFront(childrenLevel, leftChild); + + ++m_FreeCount; + ++currLevel; + currNode = m_FreeList[currLevel].front; + + /* + We can be sure that currNode, as left child of node previously split, + also fulfills the alignment requirement. + */ + } + + // Remove from free list. + VMA_ASSERT(currLevel == targetLevel && + currNode != VMA_NULL && + currNode->type == Node::TYPE_FREE); + RemoveFromFreeList(currLevel, currNode); + + // Convert to allocation node. + currNode->type = Node::TYPE_ALLOCATION; + currNode->allocation.userData = userData; + + ++m_AllocationCount; + --m_FreeCount; + m_SumFreeSize -= request.size; +} + +void VmaBlockMetadata_Buddy::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + uint32_t level = 0; + const Node* const node = FindAllocationNode((VkDeviceSize)allocHandle, level); + outInfo.offset = (VkDeviceSize)allocHandle; + outInfo.size = LevelToNodeSize(level); + outInfo.pUserData = node->allocation.userData; +} + +void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node) +{ + if (node->type == Node::TYPE_SPLIT) + { + DeleteNodeChildren(node->split.leftChild->buddy); + DeleteNodeChildren(node->split.leftChild); + const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks(); + m_NodeAllocator.Free(node->split.leftChild->buddy); + m_NodeAllocator.Free(node->split.leftChild); + } +} + +void VmaBlockMetadata_Buddy::Clear() +{ + DeleteNodeChildren(m_Root); + m_Root->type = Node::TYPE_FREE; + m_AllocationCount = 0; + m_FreeCount = 1; + m_SumFreeSize = m_UsableSize; +} + +void VmaBlockMetadata_Buddy::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + uint32_t level = 0; + Node* const node = FindAllocationNode((VkDeviceSize)allocHandle, level); + node->allocation.userData = userData; +} + +VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) +{ + Node* node = m_Root; + VkDeviceSize nodeOffset = 0; + outLevel = 0; + VkDeviceSize levelNodeSize = LevelToNodeSize(0); + while (node->type == Node::TYPE_SPLIT) + { + const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1; + if (offset < nodeOffset + nextLevelNodeSize) + { + node = node->split.leftChild; + } + else + { + node = node->split.leftChild->buddy; + nodeOffset += nextLevelNodeSize; + } + ++outLevel; + levelNodeSize = nextLevelNodeSize; + } + + VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION); + return node; +} + +bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const +{ + VMA_VALIDATE(level < m_LevelCount); + VMA_VALIDATE(curr->parent == parent); + VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL)); + VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr); + switch (curr->type) + { + case Node::TYPE_FREE: + // curr->free.prev, next are validated separately. + ctx.calculatedSumFreeSize += levelNodeSize; + ++ctx.calculatedFreeCount; + break; + case Node::TYPE_ALLOCATION: + ++ctx.calculatedAllocationCount; + if (!IsVirtual()) + { + VMA_VALIDATE(curr->allocation.userData != VMA_NULL); + } + break; + case Node::TYPE_SPLIT: + { + const uint32_t childrenLevel = level + 1; + const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1; + const Node* const leftChild = curr->split.leftChild; + VMA_VALIDATE(leftChild != VMA_NULL); + VMA_VALIDATE(leftChild->offset == curr->offset); + if (!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize)) + { + VMA_VALIDATE(false && "ValidateNode for left child failed."); + } + const Node* const rightChild = leftChild->buddy; + VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize); + if (!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize)) + { + VMA_VALIDATE(false && "ValidateNode for right child failed."); + } + } + break; + default: + return false; + } + + return true; +} + +uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const +{ + // I know this could be optimized somehow e.g. by using std::log2p1 from C++20. + uint32_t level = 0; + VkDeviceSize currLevelNodeSize = m_UsableSize; + VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1; + while (allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount) + { + ++level; + currLevelNodeSize >>= 1; + nextLevelNodeSize >>= 1; + } + return level; +} + +void VmaBlockMetadata_Buddy::Free(VmaAllocHandle allocHandle) +{ + uint32_t level = 0; + Node* node = FindAllocationNode((VkDeviceSize)allocHandle, level); + + ++m_FreeCount; + --m_AllocationCount; + m_SumFreeSize += LevelToNodeSize(level); + + node->type = Node::TYPE_FREE; + + // Join free nodes if possible. + while (level > 0 && node->buddy->type == Node::TYPE_FREE) + { + RemoveFromFreeList(level, node->buddy); + Node* const parent = node->parent; + + m_NodeAllocator.Free(node->buddy); + m_NodeAllocator.Free(node); + parent->type = Node::TYPE_FREE; + + node = parent; + --level; + --m_FreeCount; + } + + AddToFreeListFront(level, node); +} + +void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& inoutInfo, const Node* node, VkDeviceSize levelNodeSize) const +{ + switch (node->type) + { + case Node::TYPE_FREE: + VmaAddStatInfoUnusedRange(inoutInfo, levelNodeSize); + break; + case Node::TYPE_ALLOCATION: + VmaAddStatInfoAllocation(inoutInfo, levelNodeSize); + break; + case Node::TYPE_SPLIT: + { + const VkDeviceSize childrenNodeSize = levelNodeSize / 2; + const Node* const leftChild = node->split.leftChild; + CalcAllocationStatInfoNode(inoutInfo, leftChild, childrenNodeSize); + const Node* const rightChild = leftChild->buddy; + CalcAllocationStatInfoNode(inoutInfo, rightChild, childrenNodeSize); + } + break; + default: + VMA_ASSERT(0); + } +} + +void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node) +{ + VMA_ASSERT(node->type == Node::TYPE_FREE); + + // List is empty. + Node* const frontNode = m_FreeList[level].front; + if (frontNode == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].back == VMA_NULL); + node->free.prev = node->free.next = VMA_NULL; + m_FreeList[level].front = m_FreeList[level].back = node; + } + else + { + VMA_ASSERT(frontNode->free.prev == VMA_NULL); + node->free.prev = VMA_NULL; + node->free.next = frontNode; + frontNode->free.prev = node; + m_FreeList[level].front = node; + } +} + +void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node) +{ + VMA_ASSERT(m_FreeList[level].front != VMA_NULL); + + // It is at the front. + if (node->free.prev == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].front == node); + m_FreeList[level].front = node->free.next; + } + else + { + Node* const prevFreeNode = node->free.prev; + VMA_ASSERT(prevFreeNode->free.next == node); + prevFreeNode->free.next = node->free.next; + } + + // It is at the back. + if (node->free.next == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].back == node); + m_FreeList[level].back = node->free.prev; + } + else + { + Node* const nextFreeNode = node->free.next; + VMA_ASSERT(nextFreeNode->free.prev == node); + nextFreeNode->free.prev = node->free.prev; + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const +{ + switch (node->type) + { + case Node::TYPE_FREE: + PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize); + break; + case Node::TYPE_ALLOCATION: + PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData); + break; + case Node::TYPE_SPLIT: + { + const VkDeviceSize childrenNodeSize = levelNodeSize / 2; + const Node* const leftChild = node->split.leftChild; + PrintDetailedMapNode(json, leftChild, childrenNodeSize); + const Node* const rightChild = leftChild->buddy; + PrintDetailedMapNode(json, rightChild, childrenNodeSize); + } + break; + default: + VMA_ASSERT(0); + } +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_BUDDY + +#ifndef _VMA_BLOCK_METADATA_TLSF +// To not search current larger region if first allocation won't succeed and skip to smaller range +// use with VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT as strategy in CreateAllocationRequest() +class VmaBlockMetadata_TLSF : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_TLSF) +public: + VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_TLSF(); + + size_t GetAllocationCount() const override { return m_AllocCount; } + VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; } + bool IsEmpty() const override { return m_NullBlock->offset == 0; } + VkResult CheckCorruption(const void* pBlockData) override { return VK_ERROR_FEATURE_NOT_PRESENT; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; }; + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void CalcAllocationStatInfo(VmaStatInfo& outInfo) const override; + void AddPoolStats(VmaPoolStats& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + +private: + // According to original paper it should be preferable 4 or 5: + // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems" + // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf + static const uint8_t SECOND_LEVEL_INDEX = 5; + static const uint16_t SMALL_BUFFER_SIZE = 256; + static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16; + static const uint8_t MEMORY_CLASS_SHIFT = 7; + + struct RegionInfo + { + uint8_t allocType; + uint16_t allocCount; + }; + class Block + { + public: + VkDeviceSize offset; + VkDeviceSize size; + Block* prevPhysical; + Block* nextPhysical; + + void MarkFree() { prevFree = VMA_NULL; } + void MarkTaken() { prevFree = this; } + bool IsFree() const { return prevFree != this; } + void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; } + Block*& PrevFree() { return prevFree; } + Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; } + + private: + Block* prevFree; // Address of the same block here indicates that block is taken + union + { + Block* nextFree; + void* userData; + }; + }; + + size_t m_AllocCount; + // Total number of free blocks besides null block + size_t m_BlocksFreeCount; + // Total size of free blocks excluding null block + VkDeviceSize m_BlocksFreeSize; + uint32_t m_IsFree; + uint8_t m_MemoryClasses; + uint16_t* m_InnerIsFree; + uint32_t m_ListsCount; + /* + * 0: 0-3 lists for small buffers + * 1+: 0-(2^SLI-1) lists for normal buffers + */ + Block** m_FreeList; + VmaPoolAllocator m_BlockAllocator; + Block* m_NullBlock; + VmaBlockBufferImageGranularity m_GranularityHandler; + + uint8_t SizeToMemoryClass(VkDeviceSize size) const; + uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const; + uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const; + uint32_t GetListIndex(VkDeviceSize size) const; + + void RemoveFreeBlock(Block* block); + void InsertFreeBlock(Block* block); + void MergeBlock(Block* block, Block* prev); + + Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const; + bool CheckBlock( + Block& block, + uint32_t listIndex, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaAllocationRequest* pAllocationRequest); +}; + +#ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS +VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_AllocCount(0), + m_BlocksFreeCount(0), + m_BlocksFreeSize(0), + m_IsFree(0), + m_MemoryClasses(0), + m_InnerIsFree(VMA_NULL), + m_ListsCount(0), + m_FreeList(VMA_NULL), + m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT * sizeof(Block)), + m_NullBlock(VMA_NULL), + m_GranularityHandler(bufferImageGranularity) {} + +VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF() +{ + if (m_InnerIsFree) + vma_delete_array(GetAllocationCallbacks(), m_InnerIsFree, m_MemoryClasses); + if (m_FreeList) + vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount); + m_GranularityHandler.Destroy(GetAllocationCallbacks()); +} + +void VmaBlockMetadata_TLSF::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + if (!IsVirtual()) + m_GranularityHandler.Init(GetAllocationCallbacks(), size); + + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = size; + m_NullBlock->offset = 0; + m_NullBlock->prevPhysical = VMA_NULL; + m_NullBlock->nextPhysical = VMA_NULL; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = VMA_NULL; + m_NullBlock->PrevFree() = VMA_NULL; + uint8_t memoryClass = SizeToMemoryClass(size); + uint16_t sli = SizeToSecondIndex(size, memoryClass); + m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 5; + + m_MemoryClasses = memoryClass + 2; + m_InnerIsFree = vma_new_array(GetAllocationCallbacks(), uint16_t, m_MemoryClasses); + memset(m_InnerIsFree, 0, m_MemoryClasses * sizeof(uint16_t)); + + m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount); + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); +} + +bool VmaBlockMetadata_TLSF::Validate() const +{ + VMA_VALIDATE(GetSumFreeSize() <= GetSize()); + + VkDeviceSize calculatedSize = m_NullBlock->size; + VkDeviceSize calculatedFreeSize = m_NullBlock->size; + size_t allocCount = 0; + size_t freeCount = 0; + + // Check integrity of free lists + for (uint32_t list = 0; list < m_ListsCount; ++list) + { + Block* block = m_FreeList[list]; + if (block != VMA_NULL) + { + VMA_VALIDATE(block->IsFree()); + VMA_VALIDATE(block->PrevFree() == VMA_NULL); + while (block->NextFree()) + { + VMA_VALIDATE(block->NextFree()->IsFree()); + VMA_VALIDATE(block->NextFree()->PrevFree() == block); + block = block->NextFree(); + } + } + } + + VkDeviceSize nextOffset = m_NullBlock->offset; + auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual()); + + VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL); + if (m_NullBlock->prevPhysical) + { + VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock); + } + // Check all blocks + for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical) + { + VMA_VALIDATE(prev->offset + prev->size == nextOffset); + nextOffset = prev->offset; + calculatedSize += prev->size; + + uint32_t listIndex = GetListIndex(prev->size); + if (prev->IsFree()) + { + ++freeCount; + // Check if free block belongs to free list + Block* freeBlock = m_FreeList[listIndex]; + VMA_VALIDATE(freeBlock != VMA_NULL); + + bool found = false; + do + { + if (freeBlock == prev) + found = true; + + freeBlock = freeBlock->NextFree(); + } while (!found && freeBlock != VMA_NULL); + + VMA_VALIDATE(found); + calculatedFreeSize += prev->size; + } + else + { + ++allocCount; + // Check if taken block is not on a free list + Block* freeBlock = m_FreeList[listIndex]; + while (freeBlock) + { + VMA_VALIDATE(freeBlock != prev); + freeBlock = freeBlock->NextFree(); + } + + if (!IsVirtual()) + { + VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size)); + } + } + + if (prev->prevPhysical) + { + VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev); + } + } + + if (!IsVirtual()) + { + VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx)); + } + + VMA_VALIDATE(nextOffset == 0); + VMA_VALIDATE(calculatedSize == GetSize()); + VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize()); + VMA_VALIDATE(allocCount == m_AllocCount); + VMA_VALIDATE(freeCount == m_BlocksFreeCount); + + return true; +} + +void VmaBlockMetadata_TLSF::CalcAllocationStatInfo(VmaStatInfo& outInfo) const +{ + VmaInitStatInfo(outInfo); + outInfo.blockCount = 1; + if (m_NullBlock->size > 0) + VmaAddStatInfoUnusedRange(outInfo, m_NullBlock->size); + + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (block->IsFree()) + VmaAddStatInfoUnusedRange(outInfo, block->size); + else + VmaAddStatInfoAllocation(outInfo, block->size); + } +} + +void VmaBlockMetadata_TLSF::AddPoolStats(VmaPoolStats& inoutStats) const +{ + inoutStats.size += GetSize(); + inoutStats.unusedSize += GetSumFreeSize(); + inoutStats.allocationCount += m_AllocCount; + inoutStats.unusedRangeCount += m_BlocksFreeCount; + if(m_NullBlock->size > 0) + ++inoutStats.unusedRangeCount; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const +{ + size_t blockCount = m_AllocCount + m_BlocksFreeCount; + VmaStlAllocator allocator(GetAllocationCallbacks()); + VmaVector> blockList(blockCount, allocator); + + size_t i = blockCount; + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + blockList[--i] = block; + } + VMA_ASSERT(i == 0); + + VmaStatInfo stat; + CalcAllocationStatInfo(stat); + + PrintDetailedMap_Begin(json, + stat.unusedBytes, + stat.allocationCount, + stat.unusedRangeCount); + + for (; i < blockCount; ++i) + { + Block* block = blockList[i]; + if (block->IsFree()) + PrintDetailedMap_UnusedRange(json, block->offset, block->size); + else + PrintDetailedMap_Allocation(json, block->offset, block->size, block->PrevFree()); + } + if (m_NullBlock->size > 0) + PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size); + + PrintDetailedMap_End(json); +} +#endif + +bool VmaBlockMetadata_TLSF::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!"); + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); + + // For small granularity round up + if (!IsVirtual()) + m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment); + + // Quick check for too small pool + if (allocSize > GetSumFreeSize()) + return false; + + // If no free blocks in pool then check only null block + if (m_BlocksFreeCount == 0) + return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest); + + VkDeviceSize roundedSize = allocSize; + if (allocSize >= (1 << SECOND_LEVEL_INDEX)) + { + // Round up to the next block + roundedSize += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX)) - 1; + } + + uint32_t listIndex = 0; + Block* block = FindFreeBlock(roundedSize, listIndex); + while (block) + { + if (CheckBlock(*block, listIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + block = block->NextFree(); + // Region does not meet requirements + if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) + break; + } + + if ((strategy & VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) == 0) + { + // No region in previous bucket, check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + } + + // No other region found, check previous bucket + uint32_t prevListIndex = 0; + Block* prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + prevListBlock = prevListBlock->NextFree(); + } + + if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) + { + // No region in previous bucket, check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + } + + // If all searches failed and first bucket still have some free regions then check it fully + while (block) + { + if (CheckBlock(*block, listIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + block = block->NextFree(); + } + + // Worst case, if bufferImageGranularity is causing free blocks to become not suitable then full search has to be done + if (!IsVirtual() && m_GranularityHandler.IsEnabled()) + { + while (++listIndex < m_ListsCount) + { + block = m_FreeList[listIndex]; + while (block) + { + if (CheckBlock(*block, listIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + block = block->NextFree(); + } + } + } + + // No more memory sadly + return false; +} + +void VmaBlockMetadata_TLSF::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF); + + // Get block and pop it from the free list + Block* currentBlock = (Block*)request.allocHandle; + VMA_ASSERT(currentBlock != VMA_NULL); + + if (currentBlock != m_NullBlock) + RemoveFreeBlock(currentBlock); + + // Append missing alignment to prev block or create new one + VMA_ASSERT(currentBlock->offset <= request.algorithmData); + VkDeviceSize misssingAlignment = request.algorithmData - currentBlock->offset; + if (misssingAlignment) + { + Block* prevBlock = currentBlock->prevPhysical; + VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!"); + + if (prevBlock->IsFree()) + { + uint32_t oldList = GetListIndex(prevBlock->size); + prevBlock->size += misssingAlignment; + // Check if new size crosses list bucket + if (oldList != GetListIndex(prevBlock->size)) + { + prevBlock->size -= misssingAlignment; + RemoveFreeBlock(prevBlock); + prevBlock->size += misssingAlignment; + InsertFreeBlock(prevBlock); + } + else + m_BlocksFreeSize += misssingAlignment; + } + else + { + Block* newBlock = m_BlockAllocator.Alloc(); + currentBlock->prevPhysical = newBlock; + prevBlock->nextPhysical = newBlock; + newBlock->prevPhysical = prevBlock; + newBlock->nextPhysical = currentBlock; + newBlock->size = misssingAlignment; + newBlock->offset = currentBlock->offset; + newBlock->MarkTaken(); + + InsertFreeBlock(newBlock); + } + + currentBlock->size -= misssingAlignment; + currentBlock->offset += misssingAlignment; + } + + if (currentBlock->size == request.size) + { + if (currentBlock == m_NullBlock) + { + // Setup new null block + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = 0; + m_NullBlock->offset = currentBlock->offset + request.size; + m_NullBlock->prevPhysical = currentBlock; + m_NullBlock->nextPhysical = VMA_NULL; + m_NullBlock->MarkFree(); + m_NullBlock->PrevFree() = VMA_NULL; + m_NullBlock->NextFree() = VMA_NULL; + currentBlock->nextPhysical = m_NullBlock; + currentBlock->MarkTaken(); + } + } + else + { + VMA_ASSERT(currentBlock->size > request.size && "Proper block already found, shouldn't find smaller one!"); + + // Create new free block + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = currentBlock->size - request.size; + newBlock->offset = currentBlock->offset + request.size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + currentBlock->nextPhysical = newBlock; + currentBlock->size = request.size; + + if (currentBlock == m_NullBlock) + { + m_NullBlock = newBlock; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = VMA_NULL; + m_NullBlock->PrevFree() = VMA_NULL; + currentBlock->MarkTaken(); + } + else + { + newBlock->nextPhysical->prevPhysical = newBlock; + newBlock->MarkTaken(); + InsertFreeBlock(newBlock); + } + } + currentBlock->UserData() = userData; + + if (!IsVirtual()) + m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData, + currentBlock->offset, currentBlock->size); + ++m_AllocCount; +} + +void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle) +{ + Block* block = (Block*)allocHandle; + Block* next = block->nextPhysical; + VMA_ASSERT(!block->IsFree() && "Block is already free!"); + + if (!IsVirtual()) + m_GranularityHandler.FreePages(block->offset, block->size); + --m_AllocCount; + + // Try merging + Block* prev = block->prevPhysical; + if (prev != VMA_NULL && prev->IsFree()) + { + RemoveFreeBlock(prev); + MergeBlock(block, prev); + } + + if (!next->IsFree()) + InsertFreeBlock(block); + else if (next == m_NullBlock) + MergeBlock(m_NullBlock, block); + else + { + RemoveFreeBlock(next); + MergeBlock(next, block); + InsertFreeBlock(next); + } +} + +void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!"); + outInfo.offset = block->offset; + outInfo.size = block->size; + outInfo.pUserData = block->UserData(); +} + +void VmaBlockMetadata_TLSF::Clear() +{ + m_AllocCount = 0; + m_BlocksFreeCount = 0; + m_BlocksFreeSize = 0; + m_IsFree = 0; + m_NullBlock->offset = 0; + m_NullBlock->size = GetSize(); + Block* block = m_NullBlock->prevPhysical; + m_NullBlock->prevPhysical = VMA_NULL; + while (block) + { + Block* prev = block->prevPhysical; + m_BlockAllocator.Free(block); + block = prev; + } + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); + memset(m_InnerIsFree, 0, m_MemoryClasses * sizeof(uint16_t)); + m_GranularityHandler.Clear(); +} + +void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!"); + block->UserData() = userData; +} + +uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const +{ + if (size > SMALL_BUFFER_SIZE) + return VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT; + return 0; +} + +uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const +{ + if (memoryClass == 0) + return static_cast((size - 1) / 64); + return static_cast((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX)); +} + +uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const +{ + if (memoryClass == 0) + return secondIndex; + return static_cast(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex + 4; +} + +uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const +{ + uint8_t memoryClass = SizeToMemoryClass(size); + return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass)); +} + +void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block) +{ + VMA_ASSERT(block != m_NullBlock); + VMA_ASSERT(block->IsFree()); + + if (block->NextFree() != VMA_NULL) + block->NextFree()->PrevFree() = block->PrevFree(); + if (block->PrevFree() != VMA_NULL) + block->PrevFree()->NextFree() = block->NextFree(); + else + { + uint8_t memClass = SizeToMemoryClass(block->size); + uint16_t secondIndex = SizeToSecondIndex(block->size, memClass); + uint32_t index = GetListIndex(memClass, secondIndex); + m_FreeList[index] = block->NextFree(); + if (block->NextFree() == VMA_NULL) + { + m_InnerIsFree[memClass] &= ~(1U << secondIndex); + if (m_InnerIsFree[memClass] == 0) + m_IsFree &= ~(1UL << memClass); + } + } + block->MarkTaken(); + block->UserData() = VMA_NULL; + --m_BlocksFreeCount; + m_BlocksFreeSize -= block->size; +} + +void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block) +{ + VMA_ASSERT(block != m_NullBlock); + VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!"); + + uint8_t memClass = SizeToMemoryClass(block->size); + uint16_t secondIndex = SizeToSecondIndex(block->size, memClass); + uint32_t index = GetListIndex(memClass, secondIndex); + block->PrevFree() = VMA_NULL; + block->NextFree() = m_FreeList[index]; + m_FreeList[index] = block; + if (block->NextFree() != VMA_NULL) + block->NextFree()->PrevFree() = block; + else + { + m_InnerIsFree[memClass] |= 1U << secondIndex; + m_IsFree |= 1UL << memClass; + } + ++m_BlocksFreeCount; + m_BlocksFreeSize += block->size; +} + +void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev) +{ + VMA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!"); + VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!"); + + block->offset = prev->offset; + block->size += prev->size; + block->prevPhysical = prev->prevPhysical; + if (block->prevPhysical) + block->prevPhysical->nextPhysical = block; + m_BlockAllocator.Free(prev); +} + +VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const +{ + uint8_t memoryClass = SizeToMemoryClass(size); + uint16_t innerFreeMap = m_InnerIsFree[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass)); + if (!innerFreeMap) + { + // Check higher levels for avaiable blocks + uint32_t freeMap = m_IsFree & (~0UL << (memoryClass + 1)); + if (!freeMap) + return VMA_NULL; // No more memory avaible + + // Find lowest free region + innerFreeMap = m_InnerIsFree[VMA_BITSCAN_LSB(freeMap)]; + } + // Find lowest free subregion + listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(static_cast(innerFreeMap))); + return m_FreeList[listIndex]; +} + +bool VmaBlockMetadata_TLSF::CheckBlock( + Block& block, + uint32_t listIndex, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(block.IsFree() && "Block is already taken!"); + + VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment); + if (block.size < allocSize + alignedOffset - block.offset) + return false; + + // Check for granularity conflicts + if (!IsVirtual() && + m_GranularityHandler.IsConflict(allocSize, alignedOffset, block.size, block.offset, allocType)) + return false; + + // Alloc successful + pAllocationRequest->type = VmaAllocationRequestType::TLSF; + pAllocationRequest->allocHandle = (VmaAllocHandle)█ + pAllocationRequest->size = allocSize; + pAllocationRequest->customData = (void*)allocType; + pAllocationRequest->algorithmData = alignedOffset; + + // Place block at the start of list if it's normal block + if (listIndex != m_ListsCount && block.PrevFree()) + { + block.PrevFree()->NextFree() = block.NextFree(); + if (block.NextFree()) + block.NextFree()->PrevFree() = block.PrevFree(); + block.PrevFree() = VMA_NULL; + block.NextFree() = m_FreeList[listIndex]; + m_FreeList[listIndex] = █ + if (block.NextFree()) + block.NextFree()->PrevFree() = █ + } + + return true; +} +#endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_TLSF + +#ifndef _VMA_BLOCK_VECTOR +/* +Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific +Vulkan memory type. + +Synchronized internally with a mutex. +*/ +class VmaBlockVector +{ + friend class VmaDefragmentationAlgorithm_Generic; + VMA_CLASS_NO_COPY(VmaBlockVector) +public: + VmaBlockVector( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + bool explicitBlockSize, + uint32_t algorithm, + float priority, + VkDeviceSize minAllocationAlignment, + void* pMemoryAllocateNext); + ~VmaBlockVector(); + + VmaAllocator GetAllocator() const { return m_hAllocator; } + VmaPool GetParentPool() const { return m_hParentPool; } + bool IsCustomPool() const { return m_hParentPool != VMA_NULL; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + uint32_t GetAlgorithm() const { return m_Algorithm; } + bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; } + float GetPriority() const { return m_Priority; } + void* const GetAllocationNextPtr() const { return m_pMemoryAllocateNext; } + + VkResult CreateMinBlocks(); + void AddPoolStats(VmaPoolStats* pStats); + bool IsEmpty(); + bool IsCorruptionDetectionEnabled() const; + + VkResult Allocate( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + void Free(const VmaAllocation hAllocation); + // Adds statistics of this BlockVector to pStats. + void AddStats(VmaStats* pStats); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + VkResult CheckCorruption(); + + // Saves results in pCtx->res. + void Defragment( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags, + VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove, + VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove, + VkCommandBuffer commandBuffer); + void DefragmentationEnd( + class VmaBlockVectorDefragmentationContext* pCtx, + uint32_t flags, + VmaDefragmentationStats* pStats); + + uint32_t ProcessDefragmentations( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves); + + void CommitDefragmentations( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationStats* pStats); + + //////////////////////////////////////////////////////////////////////////////// + // To be used only while the m_Mutex is locked. Used during defragmentation. + + size_t GetBlockCount() const { return m_Blocks.size(); } + VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; } + size_t CalcAllocationCount() const; + bool IsBufferImageGranularityConflictPossible() const; + +private: + const VmaAllocator m_hAllocator; + const VmaPool m_hParentPool; + const uint32_t m_MemoryTypeIndex; + const VkDeviceSize m_PreferredBlockSize; + const size_t m_MinBlockCount; + const size_t m_MaxBlockCount; + const VkDeviceSize m_BufferImageGranularity; + const bool m_ExplicitBlockSize; + const uint32_t m_Algorithm; + const float m_Priority; + const VkDeviceSize m_MinAllocationAlignment; + + void* const m_pMemoryAllocateNext; + VMA_RW_MUTEX m_Mutex; + /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) - + a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */ + bool m_HasEmptyBlock; + // Incrementally sorted by sumFreeSize, ascending. + VmaVector> m_Blocks; + uint32_t m_NextBlockId; + + VkDeviceSize CalcMaxBlockSize() const; + // Finds and removes given block from vector. + void Remove(VmaDeviceMemoryBlock* pBlock); + // Performs single step in sorting m_Blocks. They may not be fully sorted + // after this call. + void IncrementallySortBlocks(); + + VkResult AllocatePage( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation); + + VkResult AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation); + + VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); + // Saves result to pCtx->res. + void ApplyDefragmentationMovesCpu( + VmaBlockVectorDefragmentationContext* pDefragCtx, + const VmaVector>& moves); + // Saves result to pCtx->res. + void ApplyDefragmentationMovesGpu( + VmaBlockVectorDefragmentationContext* pDefragCtx, + VmaVector>& moves, + VkCommandBuffer commandBuffer); + + /* + Used during defragmentation. pDefragmentationStats is optional. It is in/out + - updated with new data. + */ + void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats); + void UpdateHasEmptyBlock(); +}; +#endif // _VMA_BLOCK_VECTOR + +#ifndef _VMA_DEFRAGMENTATION_ALGORITHM +struct VmaDefragmentationMove +{ + size_t srcBlockIndex; + size_t dstBlockIndex; + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VmaAllocHandle dstHandle; + VkDeviceSize size; + VmaAllocation hAllocation; + VmaDeviceMemoryBlock* pSrcBlock; + VmaDeviceMemoryBlock* pDstBlock; +}; + +/* +Performs defragmentation: + +- Updates `pBlockVector->m_pMetadata`. +- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset(). +- Does not move actual data, only returns requested moves as `moves`. +*/ +class VmaDefragmentationAlgorithm +{ + VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm) +public: + VmaDefragmentationAlgorithm( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector) + : m_hAllocator(hAllocator), + m_pBlockVector(pBlockVector) {} + virtual ~VmaDefragmentationAlgorithm() = default; + + virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0; + virtual void AddAll() = 0; + + virtual VkResult Defragment( + VmaVector>& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove, + VmaDefragmentationFlags flags) = 0; + + virtual VkDeviceSize GetBytesMoved() const = 0; + virtual uint32_t GetAllocationsMoved() const = 0; + +protected: + struct AllocationInfo + { + VmaAllocation m_hAllocation; + VkBool32* m_pChanged; + + AllocationInfo() : m_hAllocation(VK_NULL_HANDLE), m_pChanged(VMA_NULL) {} + AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) : m_hAllocation(hAlloc), m_pChanged(pChanged) {} + }; + + VmaAllocator const m_hAllocator; + VmaBlockVector* const m_pBlockVector; +}; + +#endif // _VMA_DEFRAGMENTATION_ALGORITHM + +#ifndef _VMA_DEFRAGMENTATION_ALGORITHM_GENERIC +class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm +{ + VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic) +public: + VmaDefragmentationAlgorithm_Generic( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + bool overlappingMoveSupported); + virtual ~VmaDefragmentationAlgorithm_Generic(); + + virtual void AddAll() { m_AllAllocations = true; } + virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; } + virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; } + + virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); + virtual VkResult Defragment( + VmaVector>& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove, + VmaDefragmentationFlags flags); + +private: + struct AllocationInfoSizeGreater + { + bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const; + }; + struct AllocationInfoOffsetGreater + { + bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const; + }; + struct BlockInfo + { + size_t m_OriginalBlockIndex; + VmaDeviceMemoryBlock* m_pBlock; + bool m_HasNonMovableAllocations; + VmaVector> m_Allocations; + + BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks); + + void CalcHasNonMovableAllocations(); + void SortAllocationsBySizeDescending(); + void SortAllocationsByOffsetDescending(); + }; + struct BlockPointerLess + { + bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const; + bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const; + }; + // 1. Blocks with some non-movable allocations go first. + // 2. Blocks with smaller sumFreeSize go first. + struct BlockInfoCompareMoveDestination + { + bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const; + }; + typedef VmaVector> BlockInfoVector; + + BlockInfoVector m_Blocks; + uint32_t m_AllocationCount; + bool m_AllAllocations; + VkDeviceSize m_BytesMoved; + uint32_t m_AllocationsMoved; + + static bool MoveMakesSense( + size_t dstBlockIndex, VkDeviceSize dstOffset, + size_t srcBlockIndex, VkDeviceSize srcOffset); + + size_t CalcBlocksWithNonMovableCount() const; + VkResult DefragmentRound( + VmaVector>& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove, + bool freeOldAllocations); +}; +#endif // _VMA_DEFRAGMENTATION_ALGORITHM_GENERIC + +#ifndef _VMA_DEFRAGMENTATION_ALGORITHM_FAST +class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm +{ + VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast) +public: + VmaDefragmentationAlgorithm_Fast( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + bool overlappingMoveSupported); + virtual ~VmaDefragmentationAlgorithm_Fast() = default; + + virtual void AddAll() { m_AllAllocations = true; } + virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; } + virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; } + virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; } + + virtual VkResult Defragment( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove, + VmaDefragmentationFlags flags); + +private: + struct BlockInfo + { + size_t origBlockIndex; + }; + class FreeSpaceDatabase + { + public: + FreeSpaceDatabase(); + + void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size); + bool Fetch(VkDeviceSize alignment, VkDeviceSize size, + size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset); + + private: + static const size_t MAX_COUNT = 4; + + struct FreeSpace + { + size_t blockInfoIndex; // SIZE_MAX means this structure is invalid. + VkDeviceSize offset; + VkDeviceSize size; + } m_FreeSpaces[MAX_COUNT]; + }; + + const bool m_OverlappingMoveSupported; + + uint32_t m_AllocationCount; + bool m_AllAllocations; + VkDeviceSize m_BytesMoved; + uint32_t m_AllocationsMoved; + + VmaVector> m_BlockInfos; + + void PreprocessMetadata(); + void PostprocessMetadata(); + void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc); +}; +#endif // _VMA_DEFRAGMENTATION_ALGORITHM_FAST + +#ifndef _VMA_BLOCK_VECTOR_DEFRAGMENTATION_CONTEXT +struct VmaBlockDefragmentationContext +{ + enum BLOCK_FLAG + { + BLOCK_FLAG_USED = 0x00000001, + }; + uint32_t flags; + VkBuffer hBuffer; +}; + +class VmaBlockVectorDefragmentationContext +{ + VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext) +public: + VkResult res; + bool mutexLocked; + VmaVector> blockContexts; + VmaVector> defragmentationMoves; + uint32_t defragmentationMovesProcessed; + uint32_t defragmentationMovesCommitted; + bool hasDefragmentationPlan; + + VmaBlockVectorDefragmentationContext( + VmaAllocator hAllocator, + VmaPool hCustomPool, // Optional. + VmaBlockVector* pBlockVector); + ~VmaBlockVectorDefragmentationContext(); + + VmaPool GetCustomPool() const { return m_hCustomPool; } + VmaBlockVector* GetBlockVector() const { return m_pBlockVector; } + VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; } + void AddAll() { m_AllAllocations = true; } + + void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); + void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags); + +private: + struct AllocInfo + { + VmaAllocation hAlloc; + VkBool32* pChanged; + }; + + const VmaAllocator m_hAllocator; + // Null if not from custom pool. + const VmaPool m_hCustomPool; + // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors. + VmaBlockVector* const m_pBlockVector; + // Owner of this object. + VmaDefragmentationAlgorithm* m_pAlgorithm; + // Used between constructor and Begin. + VmaVector> m_Allocations; + bool m_AllAllocations; +}; +#endif // _VMA_BLOCK_VECTOR_DEFRAGMENTATION_CONTEXT + +#ifndef _VMA_DEFRAGMENTATION_CONTEXT +struct VmaDefragmentationContext_T +{ +private: + VMA_CLASS_NO_COPY(VmaDefragmentationContext_T) +public: + VmaDefragmentationContext_T( + VmaAllocator hAllocator, + uint32_t flags, + VmaDefragmentationStats* pStats); + ~VmaDefragmentationContext_T(); + + void AddPools(uint32_t poolCount, const VmaPool* pPools); + void AddAllocations( + uint32_t allocationCount, + const VmaAllocation* pAllocations, + VkBool32* pAllocationsChanged); + + /* + Returns: + - `VK_SUCCESS` if succeeded and object can be destroyed immediately. + - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd(). + - Negative value if error occurred and object can be destroyed immediately. + */ + VkResult Defragment( + VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove, + VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove, + VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags); + + VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo); + VkResult DefragmentPassEnd(); + +private: + const VmaAllocator m_hAllocator; + const uint32_t m_Flags; + VmaDefragmentationStats* const m_pStats; + + VkDeviceSize m_MaxCpuBytesToMove; + uint32_t m_MaxCpuAllocationsToMove; + VkDeviceSize m_MaxGpuBytesToMove; + uint32_t m_MaxGpuAllocationsToMove; + + // Owner of these objects. + VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES]; + // Owner of these objects. + VmaVector> m_CustomPoolContexts; +}; +#endif // _VMA_DEFRAGMENTATION_CONTEXT + +#ifndef _VMA_POOL_T +struct VmaPool_T +{ + friend struct VmaPoolListItemTraits; + VMA_CLASS_NO_COPY(VmaPool_T) +public: + VmaBlockVector m_BlockVector; + VmaDedicatedAllocationList m_DedicatedAllocations; + + VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize); + ~VmaPool_T(); + + uint32_t GetId() const { return m_Id; } + void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; } + + const char* GetName() const { return m_Name; } + void SetName(const char* pName); + +#if VMA_STATS_STRING_ENABLED + //void PrintDetailedMap(class VmaStringBuilder& sb); +#endif + +private: + uint32_t m_Id; + char* m_Name; + VmaPool_T* m_PrevPool = VMA_NULL; + VmaPool_T* m_NextPool = VMA_NULL; +}; + +struct VmaPoolListItemTraits +{ + typedef VmaPool_T ItemType; + + static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; } + static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; } + static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; } + static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; } +}; +#endif // _VMA_POOL_T + +#ifndef _VMA_CURRENT_BUDGET_DATA +struct VmaCurrentBudgetData +{ + VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS]; + +#if VMA_MEMORY_BUDGET + VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch; + VMA_RW_MUTEX m_BudgetMutex; + uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS]; + uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS]; + uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS]; +#endif // VMA_MEMORY_BUDGET + + VmaCurrentBudgetData(); + + void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize); + void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize); +}; + +#ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS +VmaCurrentBudgetData::VmaCurrentBudgetData() +{ + for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex) + { + m_BlockBytes[heapIndex] = 0; + m_AllocationBytes[heapIndex] = 0; +#if VMA_MEMORY_BUDGET + m_VulkanUsage[heapIndex] = 0; + m_VulkanBudget[heapIndex] = 0; + m_BlockBytesAtBudgetFetch[heapIndex] = 0; +#endif + } + +#if VMA_MEMORY_BUDGET + m_OperationsSinceBudgetFetch = 0; +#endif +} + +void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize) +{ + m_AllocationBytes[heapIndex] += allocationSize; +#if VMA_MEMORY_BUDGET + ++m_OperationsSinceBudgetFetch; +#endif +} + +void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize) +{ + VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); + m_AllocationBytes[heapIndex] -= allocationSize; +#if VMA_MEMORY_BUDGET + ++m_OperationsSinceBudgetFetch; +#endif +} +#endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS +#endif // _VMA_CURRENT_BUDGET_DATA + +#ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR +/* +Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects. +*/ +class VmaAllocationObjectAllocator +{ + VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator) +public: + VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) + : m_Allocator(pAllocationCallbacks, 1024) {} + + template VmaAllocation Allocate(Types&&... args); + void Free(VmaAllocation hAlloc); + +private: + VMA_MUTEX m_Mutex; + VmaPoolAllocator m_Allocator; +}; + +template +VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args) +{ + VmaMutexLock mutexLock(m_Mutex); + return m_Allocator.Alloc(std::forward(args)...); +} + +void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc) +{ + VmaMutexLock mutexLock(m_Mutex); + m_Allocator.Free(hAlloc); +} +#endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR + +#ifndef _VMA_VIRTUAL_BLOCK_T +struct VmaVirtualBlock_T +{ + VMA_CLASS_NO_COPY(VmaVirtualBlock_T) +public: + const bool m_AllocationCallbacksSpecified; + const VkAllocationCallbacks m_AllocationCallbacks; + + VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo); + ~VmaVirtualBlock_T(); + + VkResult Init() { return VK_SUCCESS; } + bool IsEmpty() const { return m_Metadata->IsEmpty(); } + void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); } + void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); } + void Clear() { m_Metadata->Clear(); } + + const VkAllocationCallbacks* GetAllocationCallbacks() const; + void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo); + VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation, + VkDeviceSize* outOffset); + void CalculateStats(VmaStatInfo& outStatInfo) const; +#if VMA_STATS_STRING_ENABLED + void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const; +#endif + +private: + VmaBlockMetadata* m_Metadata; +}; + +#ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS +VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo) + : m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL), + m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks) +{ + const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK; + switch (algorithm) + { + case 0: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Generic)(VK_NULL_HANDLE, 1, true); + break; + case VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Buddy)(VK_NULL_HANDLE, 1, true); + break; + case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true); + break; + case VMA_VIRTUAL_BLOCK_CREATE_TLSF_ALGORITHM_BIT: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true); + break; + default: + VMA_ASSERT(0); + } + + m_Metadata->Init(createInfo.size); +} + +VmaVirtualBlock_T::~VmaVirtualBlock_T() +{ + // This is an important assert!!! + // Hitting it means you have some memory leak - unreleased virtual allocations. + VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!"); + + vma_delete(GetAllocationCallbacks(), m_Metadata); +} + +const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const +{ + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL; +} + +void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo) +{ + m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo); +} + +VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation, + VkDeviceSize* outOffset) +{ + VmaAllocationRequest request = {}; + if (m_Metadata->CreateAllocationRequest( + createInfo.size, // allocSize + VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment + (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress + VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant + createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy + &request)) + { + m_Metadata->Alloc(request, + VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant + createInfo.pUserData); + outAllocation = (VmaVirtualAllocation)request.allocHandle; + if(outOffset) + *outOffset = m_Metadata->GetAllocationOffset(request.allocHandle); + return VK_SUCCESS; + } + outAllocation = (VmaVirtualAllocation)VK_WHOLE_SIZE; + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +void VmaVirtualBlock_T::CalculateStats(VmaStatInfo& outStatInfo) const +{ + m_Metadata->CalcAllocationStatInfo(outStatInfo); + VmaPostprocessCalcStatInfo(outStatInfo); +} + +#if VMA_STATS_STRING_ENABLED +void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const +{ + VmaJsonWriter json(GetAllocationCallbacks(), sb); + json.BeginObject(); + + VmaStatInfo stat = {}; + CalculateStats(stat); + + json.WriteString("Stats"); + VmaPrintStatInfo(json, stat); + + if (detailedMap) + { + json.WriteString("Details"); + m_Metadata->PrintDetailedMap(json); + } + + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS +#endif // _VMA_VIRTUAL_BLOCK_T + +// Main allocator object. +struct VmaAllocator_T +{ + VMA_CLASS_NO_COPY(VmaAllocator_T) +public: + bool m_UseMutex; + uint32_t m_VulkanApiVersion; + bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0). + bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0). + bool m_UseExtMemoryBudget; + bool m_UseAmdDeviceCoherentMemory; + bool m_UseKhrBufferDeviceAddress; + bool m_UseExtMemoryPriority; + VkDevice m_hDevice; + VkInstance m_hInstance; + bool m_AllocationCallbacksSpecified; + VkAllocationCallbacks m_AllocationCallbacks; + VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks; + VmaAllocationObjectAllocator m_AllocationObjectAllocator; + + // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size. + uint32_t m_HeapSizeLimitMask; + + VkPhysicalDeviceProperties m_PhysicalDeviceProperties; + VkPhysicalDeviceMemoryProperties m_MemProps; + + // Default pools. + VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES]; + VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES]; + + VmaCurrentBudgetData m_Budget; + VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects. + + VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); + VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo); + ~VmaAllocator_T(); + + const VkAllocationCallbacks* GetAllocationCallbacks() const + { + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL; + } + const VmaVulkanFunctions& GetVulkanFunctions() const + { + return m_VulkanFunctions; + } + + VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; } + + VkDeviceSize GetBufferImageGranularity() const + { + return VMA_MAX( + static_cast(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY), + m_PhysicalDeviceProperties.limits.bufferImageGranularity); + } + + uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; } + uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; } + + uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const + { + VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount); + return m_MemProps.memoryTypes[memTypeIndex].heapIndex; + } + // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT. + bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const + { + return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + // Minimum alignment for all allocations in specific memory type. + VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const + { + return IsMemoryTypeNonCoherent(memTypeIndex) ? + VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) : + (VkDeviceSize)VMA_MIN_ALIGNMENT; + } + + bool IsIntegratedGpu() const + { + return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + } + + uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; } + + void GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + void GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + + // Main allocation function. + VkResult AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown. + VkImage dedicatedImage, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Main deallocation function. + void FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations); + + void CalculateStats(VmaStats* pStats); + + void GetHeapBudgets( + VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + VkResult DefragmentationBegin( + const VmaDefragmentationInfo2& info, + VmaDefragmentationStats* pStats, + VmaDefragmentationContext* pContext); + VkResult DefragmentationEnd( + VmaDefragmentationContext context); + + VkResult DefragmentationPassBegin( + VmaDefragmentationPassInfo* pInfo, + VmaDefragmentationContext context); + VkResult DefragmentationPassEnd( + VmaDefragmentationContext context); + + void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo); + + VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool); + void DestroyPool(VmaPool pool); + void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats); + + void SetCurrentFrameIndex(uint32_t frameIndex); + uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } + + VkResult CheckPoolCorruption(VmaPool hPool); + VkResult CheckCorruption(uint32_t memoryTypeBits); + + // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping. + VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory); + // Call to Vulkan function vkFreeMemory with accompanying bookkeeping. + void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory); + // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR. + VkResult BindVulkanBuffer( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkBuffer buffer, + const void* pNext); + // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR. + VkResult BindVulkanImage( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkImage image, + const void* pNext); + + VkResult Map(VmaAllocation hAllocation, void** ppData); + void Unmap(VmaAllocation hAllocation); + + VkResult BindBufferMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext); + VkResult BindImageMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext); + + VkResult FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op); + VkResult FlushOrInvalidateAllocations( + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, const VkDeviceSize* sizes, + VMA_CACHE_OPERATION op); + + void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern); + + /* + Returns bit mask of memory types that can support defragmentation on GPU as + they support creation of required buffer for copy operations. + */ + uint32_t GetGpuDefragmentationMemoryTypeBits(); + +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const + { + return m_TypeExternalMemoryHandleTypes[memTypeIndex]; + } +#endif // #if VMA_EXTERNAL_MEMORY + +private: + VkDeviceSize m_PreferredLargeHeapBlockSize; + + VkPhysicalDevice m_PhysicalDevice; + VMA_ATOMIC_UINT32 m_CurrentFrameIndex; + VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized. +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES]; +#endif // #if VMA_EXTERNAL_MEMORY + + VMA_RW_MUTEX m_PoolsMutex; + typedef VmaIntrusiveLinkedList PoolList; + // Protected by m_PoolsMutex. + PoolList m_Pools; + uint32_t m_NextPoolId; + + VmaVulkanFunctions m_VulkanFunctions; + + // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types. + uint32_t m_GlobalMemoryTypeBits; + + void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); + +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + void ImportVulkanFunctions_Static(); +#endif + + void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions); + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + void ImportVulkanFunctions_Dynamic(); +#endif + + void ValidateVulkanFunctions(); + + VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); + + VkResult AllocateMemoryOfType( + VmaPool pool, + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedPreferred, + VkBuffer dedicatedBuffer, + VkBufferUsageFlags dedicatedBufferUsage, + VkImage dedicatedImage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + VmaBlockVector& blockVector, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Helper function only to be used inside AllocateDedicatedMemory. + VkResult AllocateDedicatedMemoryPage( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + void* pUserData, + VmaAllocation* pAllocation); + + // Allocates and registers new VkDeviceMemory specifically for dedicated allocations. + VkResult AllocateDedicatedMemory( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + bool canAliasMemory, + void* pUserData, + float priority, + VkBuffer dedicatedBuffer, + VkBufferUsageFlags dedicatedBufferUsage, + VkImage dedicatedImage, + size_t allocationCount, + VmaAllocation* pAllocations, + const void* pNextChain = nullptr); + + void FreeDedicatedMemory(const VmaAllocation allocation); + + VkResult CalcMemTypeParams( + VmaAllocationCreateInfo& outCreateInfo, + uint32_t memTypeIndex, + VkDeviceSize size, + size_t allocationCount); + VkResult CalcAllocationParams( + VmaAllocationCreateInfo& outCreateInfo, + bool dedicatedRequired, + bool dedicatedPreferred); + + /* + Calculates and returns bit mask of memory types that can support defragmentation + on GPU as they support creation of required buffer for copy operations. + */ + uint32_t CalculateGpuDefragmentationMemoryTypeBits() const; + uint32_t CalculateGlobalMemoryTypeBits() const; + + bool GetFlushOrInvalidateRange( + VmaAllocation allocation, + VkDeviceSize offset, VkDeviceSize size, + VkMappedMemoryRange& outRange) const; + +#if VMA_MEMORY_BUDGET + void UpdateVulkanBudget(); +#endif // #if VMA_MEMORY_BUDGET +}; + + +#ifndef _VMA_MEMORY_FUNCTIONS +static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment) +{ + return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment); +} + +static void VmaFree(VmaAllocator hAllocator, void* ptr) +{ + VmaFree(&hAllocator->m_AllocationCallbacks, ptr); +} + +template +static T* VmaAllocate(VmaAllocator hAllocator) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +template +static void vma_delete(VmaAllocator hAllocator, T* ptr) +{ + if(ptr != VMA_NULL) + { + ptr->~T(); + VmaFree(hAllocator, ptr); + } +} + +template +static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count) +{ + if(ptr != VMA_NULL) + { + for(size_t i = count; i--; ) + ptr[i].~T(); + VmaFree(hAllocator, ptr); + } +} +#endif // _VMA_MEMORY_FUNCTIONS + +#ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS +VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) + : m_pMetadata(VMA_NULL), + m_MemoryTypeIndex(UINT32_MAX), + m_Id(0), + m_hMemory(VK_NULL_HANDLE), + m_MapCount(0), + m_pMappedData(VMA_NULL) {} + +VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock() +{ + VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped."); + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); +} + +void VmaDeviceMemoryBlock::Init( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm, + VkDeviceSize bufferImageGranularity) +{ + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); + + m_hParentPool = hParentPool; + m_MemoryTypeIndex = newMemoryTypeIndex; + m_Id = id; + m_hMemory = newMemory; + + switch (algorithm) + { + case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + break; + case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + break; + case VMA_POOL_CREATE_TLSF_ALGORITHM_BIT: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + break; + default: + VMA_ASSERT(0); + // Fall-through. + case 0: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + } + m_pMetadata->Init(newSize); +} + +void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator) +{ + // This is the most important assert in the entire library. + // Hitting it means you have some memory leak - unreleased VmaAllocation objects. + VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!"); + + VMA_ASSERT(m_hMemory != VK_NULL_HANDLE); + allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory); + m_hMemory = VK_NULL_HANDLE; + + vma_delete(allocator, m_pMetadata); + m_pMetadata = VMA_NULL; +} + +bool VmaDeviceMemoryBlock::Validate() const +{ + VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) && + (m_pMetadata->GetSize() != 0)); + + return m_pMetadata->Validate(); +} + +VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator) +{ + void* pData = nullptr; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + res = m_pMetadata->CheckCorruption(pData); + + Unmap(hAllocator, 1); + + return res; +} + +VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData) +{ + if (count == 0) + { + return VK_SUCCESS; + } + + VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); + if (m_MapCount != 0) + { + m_MapCount += count; + VMA_ASSERT(m_pMappedData != VMA_NULL); + if (ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + return VK_SUCCESS; + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + &m_pMappedData); + if (result == VK_SUCCESS) + { + if (ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + m_MapCount = count; + } + return result; + } +} + +void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count) +{ + if (count == 0) + { + return; + } + + VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); + if (m_MapCount >= count) + { + m_MapCount -= count; + if (m_MapCount == 0) + { + m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); + } + } + else + { + VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped."); + } +} + +VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN); + VmaWriteMagicValue(pData, allocOffset + allocSize); + + Unmap(hAllocator, 1); + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + if (!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!"); + } + else if (!VmaValidateMagicValue(pData, allocOffset + allocSize)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!"); + } + + Unmap(hAllocator, 1); + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() && + "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?"); + const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset; + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); + return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext); +} + +VkResult VmaDeviceMemoryBlock::BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() && + "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?"); + const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset; + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); + return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext); +} +#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS + +#ifndef _VMA_ALLOCATION_T_FUNCTIONS +VmaAllocation_T::VmaAllocation_T(bool userDataString) + : m_Alignment{ 1 }, + m_Size{ 0 }, + m_pUserData{ VMA_NULL }, + m_MemoryTypeIndex{ 0 }, + m_Type{ (uint8_t)ALLOCATION_TYPE_NONE }, + m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN }, + m_MapCount{ 0 }, + m_Flags{ userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0 } +{ +#if VMA_STATS_STRING_ENABLED + m_BufferImageUsage = 0; +#endif +} + +VmaAllocation_T::~VmaAllocation_T() +{ + VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction."); + + // Check if owned string was freed. + VMA_ASSERT(m_pUserData == VMA_NULL); +} + +void VmaAllocation_T::InitBlockAllocation( + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle, + VkDeviceSize alignment, + VkDeviceSize size, + uint32_t memoryTypeIndex, + VmaSuballocationType suballocationType, + bool mapped) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(block != VMA_NULL); + m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; + m_Alignment = alignment; + m_Size = size; + m_MemoryTypeIndex = memoryTypeIndex; + m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; + m_SuballocationType = (uint8_t)suballocationType; + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_AllocHandle = allocHandle; +} + +void VmaAllocation_T::InitDedicatedAllocation( + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(hMemory != VK_NULL_HANDLE); + m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED; + m_Alignment = 0; + m_Size = size; + m_MemoryTypeIndex = memoryTypeIndex; + m_SuballocationType = (uint8_t)suballocationType; + m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; + m_DedicatedAllocation.m_hParentPool = hParentPool; + m_DedicatedAllocation.m_hMemory = hMemory; + m_DedicatedAllocation.m_pMappedData = pMappedData; + m_DedicatedAllocation.m_Prev = VMA_NULL; + m_DedicatedAllocation.m_Next = VMA_NULL; +} void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData) { - if(IsUserDataString()) + if (IsUserDataString()) { VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData); FreeUserDataString(hAllocator); - if(pUserData != VMA_NULL) + if (pUserData != VMA_NULL) { - const char* const newStrSrc = (char*)pUserData; - const size_t newStrLen = strlen(newStrSrc); - char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1); - memcpy(newStrDst, newStrSrc, newStrLen + 1); - m_pUserData = newStrDst; + m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData); } } else @@ -4385,12 +11638,54 @@ void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData) } } -VkDeviceSize VmaAllocation_T::GetOffset() const +void VmaAllocation_T::ChangeBlockAllocation( + VmaAllocator hAllocator, + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle) { - switch(m_Type) + VMA_ASSERT(block != VMA_NULL); + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + + // Move mapping reference counter from old block to new block. + if (block != m_BlockAllocation.m_Block) + { + uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP; + if (IsPersistentMap()) + ++mapRefCount; + m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount); + block->Map(hAllocator, mapRefCount, VMA_NULL); + } + + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_AllocHandle = allocHandle; +} + +void VmaAllocation_T::ChangeAllocHandle(VmaAllocHandle newAllocHandle) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + m_BlockAllocation.m_AllocHandle = newAllocHandle; +} + +VmaAllocHandle VmaAllocation_T::GetAllocHandle() const +{ + switch (m_Type) { case ALLOCATION_TYPE_BLOCK: - return m_BlockAllocation.m_Offset; + return m_BlockAllocation.m_AllocHandle; + case ALLOCATION_TYPE_DEDICATED: + return VMA_NULL; + default: + VMA_ASSERT(0); + return VMA_NULL; + } +} + +VkDeviceSize VmaAllocation_T::GetOffset() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle); case ALLOCATION_TYPE_DEDICATED: return 0; default: @@ -4399,12 +11694,26 @@ VkDeviceSize VmaAllocation_T::GetOffset() const } } -VkDeviceMemory VmaAllocation_T::GetMemory() const +VmaPool VmaAllocation_T::GetParentPool() const { - switch(m_Type) + switch (m_Type) { case ALLOCATION_TYPE_BLOCK: - return m_BlockAllocation.m_Block->m_hMemory; + return m_BlockAllocation.m_Block->GetParentPool(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_hParentPool; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +VkDeviceMemory VmaAllocation_T::GetMemory() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetDeviceMemory(); case ALLOCATION_TYPE_DEDICATED: return m_DedicatedAllocation.m_hMemory; default: @@ -4413,30 +11722,16 @@ VkDeviceMemory VmaAllocation_T::GetMemory() const } } -uint32_t VmaAllocation_T::GetMemoryTypeIndex() const -{ - switch(m_Type) - { - case ALLOCATION_TYPE_BLOCK: - return m_BlockAllocation.m_Block->m_MemoryTypeIndex; - case ALLOCATION_TYPE_DEDICATED: - return m_DedicatedAllocation.m_MemoryTypeIndex; - default: - VMA_ASSERT(0); - return UINT32_MAX; - } -} - void* VmaAllocation_T::GetMappedData() const { - switch(m_Type) + switch (m_Type) { case ALLOCATION_TYPE_BLOCK: - if(m_MapCount != 0) + if (m_MapCount != 0) { - void* pBlockData = m_BlockAllocation.m_Block->m_Mapping.GetMappedData(); + void* pBlockData = m_BlockAllocation.m_Block->GetMappedData(); VMA_ASSERT(pBlockData != VMA_NULL); - return (char*)pBlockData + m_BlockAllocation.m_Offset; + return (char*)pBlockData + GetOffset(); } else { @@ -4452,75 +11747,24 @@ void* VmaAllocation_T::GetMappedData() const } } -bool VmaAllocation_T::CanBecomeLost() const +void VmaAllocation_T::DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo) { - switch(m_Type) - { - case ALLOCATION_TYPE_BLOCK: - return m_BlockAllocation.m_CanBecomeLost; - case ALLOCATION_TYPE_DEDICATED: - return false; - default: - VMA_ASSERT(0); - return false; - } -} - -VmaPool VmaAllocation_T::GetPool() const -{ - VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); - return m_BlockAllocation.m_hPool; -} - -bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) -{ - VMA_ASSERT(CanBecomeLost()); - - /* - Warning: This is a carefully designed algorithm. - Do not modify unless you really know what you're doing :) - */ - uint32_t localLastUseFrameIndex = GetLastUseFrameIndex(); - for(;;) - { - if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST) - { - VMA_ASSERT(0); - return false; - } - else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex) - { - return false; - } - else // Last use time earlier than current time. - { - if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST)) - { - // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST. - // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock. - return true; - } - } - } -} - -void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator) -{ - VMA_ASSERT(IsUserDataString()); - if(m_pUserData != VMA_NULL) - { - char* const oldStr = (char*)m_pUserData; - const size_t oldStrLen = strlen(oldStr); - vma_delete_array(hAllocator, oldStr, oldStrLen + 1); - m_pUserData = VMA_NULL; - } + VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED); + outInfo.blockCount = 1; + outInfo.allocationCount = 1; + outInfo.unusedRangeCount = 0; + outInfo.usedBytes = m_Size; + outInfo.unusedBytes = 0; + outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size; + outInfo.unusedRangeSizeMin = UINT64_MAX; + outInfo.unusedRangeSizeMax = 0; } void VmaAllocation_T::BlockAllocMap() { VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); - if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F) + if ((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F) { ++m_MapCount; } @@ -4534,7 +11778,7 @@ void VmaAllocation_T::BlockAllocUnmap() { VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); - if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0) + if ((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0) { --m_MapCount; } @@ -4548,9 +11792,9 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa { VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); - if(m_MapCount != 0) + if (m_MapCount != 0) { - if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F) + if ((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F) { VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL); *ppData = m_DedicatedAllocation.m_pMappedData; @@ -4572,7 +11816,7 @@ VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppDa VK_WHOLE_SIZE, 0, // flags ppData); - if(result == VK_SUCCESS) + if (result == VK_SUCCESS) { m_DedicatedAllocation.m_pMappedData = *ppData; m_MapCount = 1; @@ -4585,10 +11829,10 @@ void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator) { VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); - if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0) + if ((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0) { --m_MapCount; - if(m_MapCount == 0) + if (m_MapCount == 0) { m_DedicatedAllocation.m_pMappedData = VMA_NULL; (*hAllocator->GetVulkanFunctions().vkUnmapMemory)( @@ -4603,1249 +11847,84 @@ void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator) } #if VMA_STATS_STRING_ENABLED - -// Correspond to values of enum VmaSuballocationType. -static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = { - "FREE", - "UNKNOWN", - "BUFFER", - "IMAGE_UNKNOWN", - "IMAGE_LINEAR", - "IMAGE_OPTIMAL", -}; - -static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat) +void VmaAllocation_T::InitBufferImageUsage(uint32_t bufferImageUsage) { - json.BeginObject(); - - json.WriteString("Blocks"); - json.WriteNumber(stat.blockCount); - - json.WriteString("Allocations"); - json.WriteNumber(stat.allocationCount); - - json.WriteString("UnusedRanges"); - json.WriteNumber(stat.unusedRangeCount); - - json.WriteString("UsedBytes"); - json.WriteNumber(stat.usedBytes); - - json.WriteString("UnusedBytes"); - json.WriteNumber(stat.unusedBytes); - - if(stat.allocationCount > 1) - { - json.WriteString("AllocationSize"); - json.BeginObject(true); - json.WriteString("Min"); - json.WriteNumber(stat.allocationSizeMin); - json.WriteString("Avg"); - json.WriteNumber(stat.allocationSizeAvg); - json.WriteString("Max"); - json.WriteNumber(stat.allocationSizeMax); - json.EndObject(); - } - - if(stat.unusedRangeCount > 1) - { - json.WriteString("UnusedRangeSize"); - json.BeginObject(true); - json.WriteString("Min"); - json.WriteNumber(stat.unusedRangeSizeMin); - json.WriteString("Avg"); - json.WriteNumber(stat.unusedRangeSizeAvg); - json.WriteString("Max"); - json.WriteNumber(stat.unusedRangeSizeMax); - json.EndObject(); - } - - json.EndObject(); + VMA_ASSERT(m_BufferImageUsage == 0); + m_BufferImageUsage = bufferImageUsage; } -#endif // #if VMA_STATS_STRING_ENABLED - -struct VmaSuballocationItemSizeLess +void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const { - bool operator()( - const VmaSuballocationList::iterator lhs, - const VmaSuballocationList::iterator rhs) const - { - return lhs->size < rhs->size; - } - bool operator()( - const VmaSuballocationList::iterator lhs, - VkDeviceSize rhsSize) const - { - return lhs->size < rhsSize; - } -}; + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]); -//////////////////////////////////////////////////////////////////////////////// -// class VmaBlockMetadata - -VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) : - m_Size(0), - m_FreeCount(0), - m_SumFreeSize(0), - m_Suballocations(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), - m_FreeSuballocationsBySize(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) -{ -} - -VmaBlockMetadata::~VmaBlockMetadata() -{ -} - -void VmaBlockMetadata::Init(VkDeviceSize size) -{ - m_Size = size; - m_FreeCount = 1; - m_SumFreeSize = size; - - VmaSuballocation suballoc = {}; - suballoc.offset = 0; - suballoc.size = size; - suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; - suballoc.hAllocation = VK_NULL_HANDLE; - - m_Suballocations.push_back(suballoc); - VmaSuballocationList::iterator suballocItem = m_Suballocations.end(); - --suballocItem; - m_FreeSuballocationsBySize.push_back(suballocItem); -} - -bool VmaBlockMetadata::Validate() const -{ - if(m_Suballocations.empty()) - { - return false; - } - - // Expected offset of new suballocation as calculates from previous ones. - VkDeviceSize calculatedOffset = 0; - // Expected number of free suballocations as calculated from traversing their list. - uint32_t calculatedFreeCount = 0; - // Expected sum size of free suballocations as calculated from traversing their list. - VkDeviceSize calculatedSumFreeSize = 0; - // Expected number of free suballocations that should be registered in - // m_FreeSuballocationsBySize calculated from traversing their list. - size_t freeSuballocationsToRegister = 0; - // True if previous visisted suballocation was free. - bool prevFree = false; - - for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); - suballocItem != m_Suballocations.cend(); - ++suballocItem) - { - const VmaSuballocation& subAlloc = *suballocItem; - - // Actual offset of this suballocation doesn't match expected one. - if(subAlloc.offset != calculatedOffset) - { - return false; - } - - const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE); - // Two adjacent free suballocations are invalid. They should be merged. - if(prevFree && currFree) - { - return false; - } - prevFree = currFree; - - if(currFree != (subAlloc.hAllocation == VK_NULL_HANDLE)) - { - return false; - } - - if(currFree) - { - calculatedSumFreeSize += subAlloc.size; - ++calculatedFreeCount; - if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) - { - ++freeSuballocationsToRegister; - } - } - - calculatedOffset += subAlloc.size; - } - - // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't - // match expected one. - if(m_FreeSuballocationsBySize.size() != freeSuballocationsToRegister) - { - return false; - } - - VkDeviceSize lastSize = 0; - for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i) - { - VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i]; - - // Only free suballocations can be registered in m_FreeSuballocationsBySize. - if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE) - { - return false; - } - // They must be sorted by size ascending. - if(suballocItem->size < lastSize) - { - return false; - } - - lastSize = suballocItem->size; - } - - // Check if totals match calculacted values. - return - ValidateFreeSuballocationList() && - (calculatedOffset == m_Size) && - (calculatedSumFreeSize == m_SumFreeSize) && - (calculatedFreeCount == m_FreeCount); -} - -VkDeviceSize VmaBlockMetadata::GetUnusedRangeSizeMax() const -{ - if(!m_FreeSuballocationsBySize.empty()) - { - return m_FreeSuballocationsBySize.back()->size; - } - else - { - return 0; - } -} - -bool VmaBlockMetadata::IsEmpty() const -{ - return (m_Suballocations.size() == 1) && (m_FreeCount == 1); -} - -void VmaBlockMetadata::CalcAllocationStatInfo(VmaStatInfo& outInfo) const -{ - outInfo.blockCount = 1; - - const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); - outInfo.allocationCount = rangeCount - m_FreeCount; - outInfo.unusedRangeCount = m_FreeCount; - - outInfo.unusedBytes = m_SumFreeSize; - outInfo.usedBytes = m_Size - outInfo.unusedBytes; - - outInfo.allocationSizeMin = UINT64_MAX; - outInfo.allocationSizeMax = 0; - outInfo.unusedRangeSizeMin = UINT64_MAX; - outInfo.unusedRangeSizeMax = 0; - - for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); - suballocItem != m_Suballocations.cend(); - ++suballocItem) - { - const VmaSuballocation& suballoc = *suballocItem; - if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) - { - outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size); - outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size); - } - else - { - outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size); - outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size); - } - } -} - -void VmaBlockMetadata::AddPoolStats(VmaPoolStats& inoutStats) const -{ - const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); - - inoutStats.size += m_Size; - inoutStats.unusedSize += m_SumFreeSize; - inoutStats.allocationCount += rangeCount - m_FreeCount; - inoutStats.unusedRangeCount += m_FreeCount; - inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax()); -} - -#if VMA_STATS_STRING_ENABLED - -void VmaBlockMetadata::PrintDetailedMap(class VmaJsonWriter& json) const -{ - json.BeginObject(); - - json.WriteString("TotalBytes"); + json.WriteString("Size"); json.WriteNumber(m_Size); - json.WriteString("UnusedBytes"); - json.WriteNumber(m_SumFreeSize); - - json.WriteString("Allocations"); - json.WriteNumber(m_Suballocations.size() - m_FreeCount); - - json.WriteString("UnusedRanges"); - json.WriteNumber(m_FreeCount); - - json.WriteString("Suballocations"); - json.BeginArray(); - size_t i = 0; - for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin(); - suballocItem != m_Suballocations.cend(); - ++suballocItem, ++i) + if (m_pUserData != VMA_NULL) { - json.BeginObject(true); - - json.WriteString("Type"); - json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[suballocItem->type]); - - json.WriteString("Size"); - json.WriteNumber(suballocItem->size); - - json.WriteString("Offset"); - json.WriteNumber(suballocItem->offset); - - if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE) + json.WriteString("UserData"); + if (IsUserDataString()) { - const void* pUserData = suballocItem->hAllocation->GetUserData(); - if(pUserData != VMA_NULL) - { - json.WriteString("UserData"); - if(suballocItem->hAllocation->IsUserDataString()) - { - json.WriteString((const char*)pUserData); - } - else - { - json.BeginString(); - json.ContinueString_Pointer(pUserData); - json.EndString(); - } - } - } - - json.EndObject(); - } - json.EndArray(); - - json.EndObject(); -} - -#endif // #if VMA_STATS_STRING_ENABLED - -/* -How many suitable free suballocations to analyze before choosing best one. -- Set to 1 to use First-Fit algorithm - first suitable free suballocation will - be chosen. -- Set to UINT32_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free - suballocations will be analized and best one will be chosen. -- Any other value is also acceptable. -*/ -//static const uint32_t MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK = 8; - -void VmaBlockMetadata::CreateFirstAllocationRequest(VmaAllocationRequest* pAllocationRequest) -{ - VMA_ASSERT(IsEmpty()); - pAllocationRequest->offset = 0; - pAllocationRequest->sumFreeSize = m_SumFreeSize; - pAllocationRequest->sumItemSize = 0; - pAllocationRequest->item = m_Suballocations.begin(); - pAllocationRequest->itemsToMakeLostCount = 0; -} - -bool VmaBlockMetadata::CreateAllocationRequest( - uint32_t currentFrameIndex, - uint32_t frameInUseCount, - VkDeviceSize bufferImageGranularity, - VkDeviceSize allocSize, - VkDeviceSize allocAlignment, - VmaSuballocationType allocType, - bool canMakeOtherLost, - VmaAllocationRequest* pAllocationRequest) -{ - VMA_ASSERT(allocSize > 0); - VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); - VMA_ASSERT(pAllocationRequest != VMA_NULL); - VMA_HEAVY_ASSERT(Validate()); - - // There is not enough total free space in this block to fullfill the request: Early return. - if(canMakeOtherLost == false && m_SumFreeSize < allocSize) - { - return false; - } - - // New algorithm, efficiently searching freeSuballocationsBySize. - const size_t freeSuballocCount = m_FreeSuballocationsBySize.size(); - if(freeSuballocCount > 0) - { - if(VMA_BEST_FIT) - { - // Find first free suballocation with size not less than allocSize. - VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( - m_FreeSuballocationsBySize.data(), - m_FreeSuballocationsBySize.data() + freeSuballocCount, - allocSize, - VmaSuballocationItemSizeLess()); - size_t index = it - m_FreeSuballocationsBySize.data(); - for(; index < freeSuballocCount; ++index) - { - if(CheckAllocation( - currentFrameIndex, - frameInUseCount, - bufferImageGranularity, - allocSize, - allocAlignment, - allocType, - m_FreeSuballocationsBySize[index], - false, // canMakeOtherLost - &pAllocationRequest->offset, - &pAllocationRequest->itemsToMakeLostCount, - &pAllocationRequest->sumFreeSize, - &pAllocationRequest->sumItemSize)) - { - pAllocationRequest->item = m_FreeSuballocationsBySize[index]; - return true; - } - } + json.WriteString((const char*)m_pUserData); } else { - // Search staring from biggest suballocations. - for(size_t index = freeSuballocCount; index--; ) - { - if(CheckAllocation( - currentFrameIndex, - frameInUseCount, - bufferImageGranularity, - allocSize, - allocAlignment, - allocType, - m_FreeSuballocationsBySize[index], - false, // canMakeOtherLost - &pAllocationRequest->offset, - &pAllocationRequest->itemsToMakeLostCount, - &pAllocationRequest->sumFreeSize, - &pAllocationRequest->sumItemSize)) - { - pAllocationRequest->item = m_FreeSuballocationsBySize[index]; - return true; - } - } + json.BeginString(); + json.ContinueString_Pointer(m_pUserData); + json.EndString(); } } - if(canMakeOtherLost) + if (m_BufferImageUsage != 0) { - // Brute-force algorithm. TODO: Come up with something better. - - pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE; - pAllocationRequest->sumItemSize = VK_WHOLE_SIZE; - - VmaAllocationRequest tmpAllocRequest = {}; - for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin(); - suballocIt != m_Suballocations.end(); - ++suballocIt) - { - if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE || - suballocIt->hAllocation->CanBecomeLost()) - { - if(CheckAllocation( - currentFrameIndex, - frameInUseCount, - bufferImageGranularity, - allocSize, - allocAlignment, - allocType, - suballocIt, - canMakeOtherLost, - &tmpAllocRequest.offset, - &tmpAllocRequest.itemsToMakeLostCount, - &tmpAllocRequest.sumFreeSize, - &tmpAllocRequest.sumItemSize)) - { - tmpAllocRequest.item = suballocIt; - - if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost()) - { - *pAllocationRequest = tmpAllocRequest; - } - } - } - } - - if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE) - { - return true; - } - } - - return false; -} - -bool VmaBlockMetadata::MakeRequestedAllocationsLost( - uint32_t currentFrameIndex, - uint32_t frameInUseCount, - VmaAllocationRequest* pAllocationRequest) -{ - while(pAllocationRequest->itemsToMakeLostCount > 0) - { - if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE) - { - ++pAllocationRequest->item; - } - VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end()); - VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE); - VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost()); - if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) - { - pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item); - --pAllocationRequest->itemsToMakeLostCount; - } - else - { - return false; - } - } - - VMA_HEAVY_ASSERT(Validate()); - VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end()); - VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE); - - return true; -} - -uint32_t VmaBlockMetadata::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) -{ - uint32_t lostAllocationCount = 0; - for(VmaSuballocationList::iterator it = m_Suballocations.begin(); - it != m_Suballocations.end(); - ++it) - { - if(it->type != VMA_SUBALLOCATION_TYPE_FREE && - it->hAllocation->CanBecomeLost() && - it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount)) - { - it = FreeSuballocation(it); - ++lostAllocationCount; - } - } - return lostAllocationCount; -} - -void VmaBlockMetadata::Alloc( - const VmaAllocationRequest& request, - VmaSuballocationType type, - VkDeviceSize allocSize, - VmaAllocation hAllocation) -{ - VMA_ASSERT(request.item != m_Suballocations.end()); - VmaSuballocation& suballoc = *request.item; - // Given suballocation is a free block. - VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); - // Given offset is inside this suballocation. - VMA_ASSERT(request.offset >= suballoc.offset); - const VkDeviceSize paddingBegin = request.offset - suballoc.offset; - VMA_ASSERT(suballoc.size >= paddingBegin + allocSize); - const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize; - - // Unregister this free suballocation from m_FreeSuballocationsBySize and update - // it to become used. - UnregisterFreeSuballocation(request.item); - - suballoc.offset = request.offset; - suballoc.size = allocSize; - suballoc.type = type; - suballoc.hAllocation = hAllocation; - - // If there are any free bytes remaining at the end, insert new free suballocation after current one. - if(paddingEnd) - { - VmaSuballocation paddingSuballoc = {}; - paddingSuballoc.offset = request.offset + allocSize; - paddingSuballoc.size = paddingEnd; - paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; - VmaSuballocationList::iterator next = request.item; - ++next; - const VmaSuballocationList::iterator paddingEndItem = - m_Suballocations.insert(next, paddingSuballoc); - RegisterFreeSuballocation(paddingEndItem); - } - - // If there are any free bytes remaining at the beginning, insert new free suballocation before current one. - if(paddingBegin) - { - VmaSuballocation paddingSuballoc = {}; - paddingSuballoc.offset = request.offset - paddingBegin; - paddingSuballoc.size = paddingBegin; - paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; - const VmaSuballocationList::iterator paddingBeginItem = - m_Suballocations.insert(request.item, paddingSuballoc); - RegisterFreeSuballocation(paddingBeginItem); - } - - // Update totals. - m_FreeCount = m_FreeCount - 1; - if(paddingBegin > 0) - { - ++m_FreeCount; - } - if(paddingEnd > 0) - { - ++m_FreeCount; - } - m_SumFreeSize -= allocSize; -} - -void VmaBlockMetadata::Free(const VmaAllocation allocation) -{ - for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); - suballocItem != m_Suballocations.end(); - ++suballocItem) - { - VmaSuballocation& suballoc = *suballocItem; - if(suballoc.hAllocation == allocation) - { - FreeSuballocation(suballocItem); - VMA_HEAVY_ASSERT(Validate()); - return; - } - } - VMA_ASSERT(0 && "Not found!"); -} - -bool VmaBlockMetadata::ValidateFreeSuballocationList() const -{ - VkDeviceSize lastSize = 0; - for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i) - { - const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i]; - - if(it->type != VMA_SUBALLOCATION_TYPE_FREE) - { - VMA_ASSERT(0); - return false; - } - if(it->size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) - { - VMA_ASSERT(0); - return false; - } - if(it->size < lastSize) - { - VMA_ASSERT(0); - return false; - } - - lastSize = it->size; - } - return true; -} - -bool VmaBlockMetadata::CheckAllocation( - uint32_t currentFrameIndex, - uint32_t frameInUseCount, - VkDeviceSize bufferImageGranularity, - VkDeviceSize allocSize, - VkDeviceSize allocAlignment, - VmaSuballocationType allocType, - VmaSuballocationList::const_iterator suballocItem, - bool canMakeOtherLost, - VkDeviceSize* pOffset, - size_t* itemsToMakeLostCount, - VkDeviceSize* pSumFreeSize, - VkDeviceSize* pSumItemSize) const -{ - VMA_ASSERT(allocSize > 0); - VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); - VMA_ASSERT(suballocItem != m_Suballocations.cend()); - VMA_ASSERT(pOffset != VMA_NULL); - - *itemsToMakeLostCount = 0; - *pSumFreeSize = 0; - *pSumItemSize = 0; - - if(canMakeOtherLost) - { - if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE) - { - *pSumFreeSize = suballocItem->size; - } - else - { - if(suballocItem->hAllocation->CanBecomeLost() && - suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) - { - ++*itemsToMakeLostCount; - *pSumItemSize = suballocItem->size; - } - else - { - return false; - } - } - - // Remaining size is too small for this request: Early return. - if(m_Size - suballocItem->offset < allocSize) - { - return false; - } - - // Start from offset equal to beginning of this suballocation. - *pOffset = suballocItem->offset; - - // Apply VMA_DEBUG_MARGIN at the beginning. - if((VMA_DEBUG_MARGIN > 0) && suballocItem != m_Suballocations.cbegin()) - { - *pOffset += VMA_DEBUG_MARGIN; - } - - // Apply alignment. - const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast(VMA_DEBUG_ALIGNMENT)); - *pOffset = VmaAlignUp(*pOffset, alignment); - - // Check previous suballocations for BufferImageGranularity conflicts. - // Make bigger alignment if necessary. - if(bufferImageGranularity > 1) - { - bool bufferImageGranularityConflict = false; - VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; - while(prevSuballocItem != m_Suballocations.cbegin()) - { - --prevSuballocItem; - const VmaSuballocation& prevSuballoc = *prevSuballocItem; - if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity)) - { - if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) - { - bufferImageGranularityConflict = true; - break; - } - } - else - // Already on previous page. - break; - } - if(bufferImageGranularityConflict) - { - *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity); - } - } - - // Now that we have final *pOffset, check if we are past suballocItem. - // If yes, return false - this function should be called for another suballocItem as starting point. - if(*pOffset >= suballocItem->offset + suballocItem->size) - { - return false; - } - - // Calculate padding at the beginning based on current offset. - const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset; - - // Calculate required margin at the end if this is not last suballocation. - VmaSuballocationList::const_iterator next = suballocItem; - ++next; - const VkDeviceSize requiredEndMargin = - (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0; - - const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin; - // Another early return check. - if(suballocItem->offset + totalSize > m_Size) - { - return false; - } - - // Advance lastSuballocItem until desired size is reached. - // Update itemsToMakeLostCount. - VmaSuballocationList::const_iterator lastSuballocItem = suballocItem; - if(totalSize > suballocItem->size) - { - VkDeviceSize remainingSize = totalSize - suballocItem->size; - while(remainingSize > 0) - { - ++lastSuballocItem; - if(lastSuballocItem == m_Suballocations.cend()) - { - return false; - } - if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE) - { - *pSumFreeSize += lastSuballocItem->size; - } - else - { - VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE); - if(lastSuballocItem->hAllocation->CanBecomeLost() && - lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) - { - ++*itemsToMakeLostCount; - *pSumItemSize += lastSuballocItem->size; - } - else - { - return false; - } - } - remainingSize = (lastSuballocItem->size < remainingSize) ? - remainingSize - lastSuballocItem->size : 0; - } - } - - // Check next suballocations for BufferImageGranularity conflicts. - // If conflict exists, we must mark more allocations lost or fail. - if(bufferImageGranularity > 1) - { - VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem; - ++nextSuballocItem; - while(nextSuballocItem != m_Suballocations.cend()) - { - const VmaSuballocation& nextSuballoc = *nextSuballocItem; - if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) - { - if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) - { - VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE); - if(nextSuballoc.hAllocation->CanBecomeLost() && - nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex) - { - ++*itemsToMakeLostCount; - } - else - { - return false; - } - } - } - else - { - // Already on next page. - break; - } - ++nextSuballocItem; - } - } - } - else - { - const VmaSuballocation& suballoc = *suballocItem; - VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); - - *pSumFreeSize = suballoc.size; - - // Size of this suballocation is too small for this request: Early return. - if(suballoc.size < allocSize) - { - return false; - } - - // Start from offset equal to beginning of this suballocation. - *pOffset = suballoc.offset; - - // Apply VMA_DEBUG_MARGIN at the beginning. - if((VMA_DEBUG_MARGIN > 0) && suballocItem != m_Suballocations.cbegin()) - { - *pOffset += VMA_DEBUG_MARGIN; - } - - // Apply alignment. - const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast(VMA_DEBUG_ALIGNMENT)); - *pOffset = VmaAlignUp(*pOffset, alignment); - - // Check previous suballocations for BufferImageGranularity conflicts. - // Make bigger alignment if necessary. - if(bufferImageGranularity > 1) - { - bool bufferImageGranularityConflict = false; - VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; - while(prevSuballocItem != m_Suballocations.cbegin()) - { - --prevSuballocItem; - const VmaSuballocation& prevSuballoc = *prevSuballocItem; - if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity)) - { - if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) - { - bufferImageGranularityConflict = true; - break; - } - } - else - // Already on previous page. - break; - } - if(bufferImageGranularityConflict) - { - *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity); - } - } - - // Calculate padding at the beginning based on current offset. - const VkDeviceSize paddingBegin = *pOffset - suballoc.offset; - - // Calculate required margin at the end if this is not last suballocation. - VmaSuballocationList::const_iterator next = suballocItem; - ++next; - const VkDeviceSize requiredEndMargin = - (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0; - - // Fail if requested size plus margin before and after is bigger than size of this suballocation. - if(paddingBegin + allocSize + requiredEndMargin > suballoc.size) - { - return false; - } - - // Check next suballocations for BufferImageGranularity conflicts. - // If conflict exists, allocation cannot be made here. - if(bufferImageGranularity > 1) - { - VmaSuballocationList::const_iterator nextSuballocItem = suballocItem; - ++nextSuballocItem; - while(nextSuballocItem != m_Suballocations.cend()) - { - const VmaSuballocation& nextSuballoc = *nextSuballocItem; - if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) - { - if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) - { - return false; - } - } - else - { - // Already on next page. - break; - } - ++nextSuballocItem; - } - } - } - - // All tests passed: Success. pOffset is already filled. - return true; -} - -void VmaBlockMetadata::MergeFreeWithNext(VmaSuballocationList::iterator item) -{ - VMA_ASSERT(item != m_Suballocations.end()); - VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); - - VmaSuballocationList::iterator nextItem = item; - ++nextItem; - VMA_ASSERT(nextItem != m_Suballocations.end()); - VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE); - - item->size += nextItem->size; - --m_FreeCount; - m_Suballocations.erase(nextItem); -} - -VmaSuballocationList::iterator VmaBlockMetadata::FreeSuballocation(VmaSuballocationList::iterator suballocItem) -{ - // Change this suballocation to be marked as free. - VmaSuballocation& suballoc = *suballocItem; - suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; - suballoc.hAllocation = VK_NULL_HANDLE; - - // Update totals. - ++m_FreeCount; - m_SumFreeSize += suballoc.size; - - // Merge with previous and/or next suballocation if it's also free. - bool mergeWithNext = false; - bool mergeWithPrev = false; - - VmaSuballocationList::iterator nextItem = suballocItem; - ++nextItem; - if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)) - { - mergeWithNext = true; - } - - VmaSuballocationList::iterator prevItem = suballocItem; - if(suballocItem != m_Suballocations.begin()) - { - --prevItem; - if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE) - { - mergeWithPrev = true; - } - } - - if(mergeWithNext) - { - UnregisterFreeSuballocation(nextItem); - MergeFreeWithNext(suballocItem); - } - - if(mergeWithPrev) - { - UnregisterFreeSuballocation(prevItem); - MergeFreeWithNext(prevItem); - RegisterFreeSuballocation(prevItem); - return prevItem; - } - else - { - RegisterFreeSuballocation(suballocItem); - return suballocItem; + json.WriteString("Usage"); + json.WriteNumber(m_BufferImageUsage); } } +#endif // VMA_STATS_STRING_ENABLED -void VmaBlockMetadata::RegisterFreeSuballocation(VmaSuballocationList::iterator item) +void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator) { - VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); - VMA_ASSERT(item->size > 0); - - // You may want to enable this validation at the beginning or at the end of - // this function, depending on what do you want to check. - VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); - - if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) - { - if(m_FreeSuballocationsBySize.empty()) - { - m_FreeSuballocationsBySize.push_back(item); - } - else - { - VmaVectorInsertSorted(m_FreeSuballocationsBySize, item); - } - } - - //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + VMA_ASSERT(IsUserDataString()); + VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData); + m_pUserData = VMA_NULL; } +#endif // _VMA_ALLOCATION_T_FUNCTIONS - -void VmaBlockMetadata::UnregisterFreeSuballocation(VmaSuballocationList::iterator item) -{ - VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); - VMA_ASSERT(item->size > 0); - - // You may want to enable this validation at the beginning or at the end of - // this function, depending on what do you want to check. - VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); - - if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER) - { - VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( - m_FreeSuballocationsBySize.data(), - m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(), - item, - VmaSuballocationItemSizeLess()); - for(size_t index = it - m_FreeSuballocationsBySize.data(); - index < m_FreeSuballocationsBySize.size(); - ++index) - { - if(m_FreeSuballocationsBySize[index] == item) - { - VmaVectorRemove(m_FreeSuballocationsBySize, index); - return; - } - VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found."); - } - VMA_ASSERT(0 && "Not found."); - } - - //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); -} - -//////////////////////////////////////////////////////////////////////////////// -// class VmaDeviceMemoryMapping - -VmaDeviceMemoryMapping::VmaDeviceMemoryMapping() : - m_MapCount(0), - m_pMappedData(VMA_NULL) -{ -} - -VmaDeviceMemoryMapping::~VmaDeviceMemoryMapping() -{ - VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped."); -} - -VkResult VmaDeviceMemoryMapping::Map(VmaAllocator hAllocator, VkDeviceMemory hMemory, void **ppData) -{ - VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); - if(m_MapCount != 0) - { - ++m_MapCount; - VMA_ASSERT(m_pMappedData != VMA_NULL); - if(ppData != VMA_NULL) - { - *ppData = m_pMappedData; - } - return VK_SUCCESS; - } - else - { - VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( - hAllocator->m_hDevice, - hMemory, - 0, // offset - VK_WHOLE_SIZE, - 0, // flags - &m_pMappedData); - if(result == VK_SUCCESS) - { - if(ppData != VMA_NULL) - { - *ppData = m_pMappedData; - } - m_MapCount = 1; - } - return result; - } -} - -void VmaDeviceMemoryMapping::Unmap(VmaAllocator hAllocator, VkDeviceMemory hMemory) -{ - VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex); - if(m_MapCount != 0) - { - if(--m_MapCount == 0) - { - m_pMappedData = VMA_NULL; - (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, hMemory); - } - } - else - { - VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped."); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// class VmaDeviceMemoryBlock - -VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) : - m_MemoryTypeIndex(UINT32_MAX), - m_hMemory(VK_NULL_HANDLE), - m_Metadata(hAllocator) -{ -} - -void VmaDeviceMemoryBlock::Init( - uint32_t newMemoryTypeIndex, - VkDeviceMemory newMemory, - VkDeviceSize newSize) -{ - VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); - - m_MemoryTypeIndex = newMemoryTypeIndex; - m_hMemory = newMemory; - - m_Metadata.Init(newSize); -} - -void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator) -{ - // This is the most important assert in the entire library. - // Hitting it means you have some memory leak - unreleased VmaAllocation objects. - VMA_ASSERT(m_Metadata.IsEmpty() && "Some allocations were not freed before destruction of this memory block!"); - - VMA_ASSERT(m_hMemory != VK_NULL_HANDLE); - allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_Metadata.GetSize(), m_hMemory); - m_hMemory = VK_NULL_HANDLE; -} - -bool VmaDeviceMemoryBlock::Validate() const -{ - if((m_hMemory == VK_NULL_HANDLE) || - (m_Metadata.GetSize() == 0)) - { - return false; - } - - return m_Metadata.Validate(); -} - -VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, void** ppData) -{ - return m_Mapping.Map(hAllocator, m_hMemory, ppData); -} - -void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator) -{ - m_Mapping.Unmap(hAllocator, m_hMemory); -} - -static void InitStatInfo(VmaStatInfo& outInfo) -{ - memset(&outInfo, 0, sizeof(outInfo)); - outInfo.allocationSizeMin = UINT64_MAX; - outInfo.unusedRangeSizeMin = UINT64_MAX; -} - -// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo. -static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo) -{ - inoutInfo.blockCount += srcInfo.blockCount; - inoutInfo.allocationCount += srcInfo.allocationCount; - inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount; - inoutInfo.usedBytes += srcInfo.usedBytes; - inoutInfo.unusedBytes += srcInfo.unusedBytes; - inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin); - inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax); - inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin); - inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax); -} - -static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo) -{ - inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ? - VmaRoundDiv(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0; - inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ? - VmaRoundDiv(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0; -} - -VmaPool_T::VmaPool_T( - VmaAllocator hAllocator, - const VmaPoolCreateInfo& createInfo) : - m_BlockVector( - hAllocator, - createInfo.memoryTypeIndex, - createInfo.blockSize, - createInfo.minBlockCount, - createInfo.maxBlockCount, - (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(), - createInfo.frameInUseCount, - true) // isCustomPool -{ -} - -VmaPool_T::~VmaPool_T() -{ -} - -#if VMA_STATS_STRING_ENABLED - -#endif // #if VMA_STATS_STRING_ENABLED - +#ifndef _VMA_BLOCK_VECTOR_FUNCTIONS VmaBlockVector::VmaBlockVector( VmaAllocator hAllocator, + VmaPool hParentPool, uint32_t memoryTypeIndex, VkDeviceSize preferredBlockSize, size_t minBlockCount, size_t maxBlockCount, VkDeviceSize bufferImageGranularity, - uint32_t frameInUseCount, - bool isCustomPool) : - m_hAllocator(hAllocator), + bool explicitBlockSize, + uint32_t algorithm, + float priority, + VkDeviceSize minAllocationAlignment, + void* pMemoryAllocateNext) + : m_hAllocator(hAllocator), + m_hParentPool(hParentPool), m_MemoryTypeIndex(memoryTypeIndex), m_PreferredBlockSize(preferredBlockSize), m_MinBlockCount(minBlockCount), m_MaxBlockCount(maxBlockCount), m_BufferImageGranularity(bufferImageGranularity), - m_FrameInUseCount(frameInUseCount), - m_IsCustomPool(isCustomPool), - m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_ExplicitBlockSize(explicitBlockSize), + m_Algorithm(algorithm), + m_Priority(priority), + m_MinAllocationAlignment(minAllocationAlignment), + m_pMemoryAllocateNext(pMemoryAllocateNext), m_HasEmptyBlock(false), - m_pDefragmentator(VMA_NULL) -{ -} + m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_NextBlockId(0) {} VmaBlockVector::~VmaBlockVector() { - VMA_ASSERT(m_pDefragmentator == VMA_NULL); - - for(size_t i = m_Blocks.size(); i--; ) + for (size_t i = m_Blocks.size(); i--; ) { m_Blocks[i]->Destroy(m_hAllocator); vma_delete(m_hAllocator, m_Blocks[i]); @@ -5854,10 +11933,10 @@ VmaBlockVector::~VmaBlockVector() VkResult VmaBlockVector::CreateMinBlocks() { - for(size_t i = 0; i < m_MinBlockCount; ++i) + for (size_t i = 0; i < m_MinBlockCount; ++i) { VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL); - if(res != VK_SUCCESS) + if (res != VK_SUCCESS) { return res; } @@ -5865,329 +11944,389 @@ VkResult VmaBlockVector::CreateMinBlocks() return VK_SUCCESS; } -void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats) +void VmaBlockVector::AddPoolStats(VmaPoolStats* pStats) { - pStats->size = 0; - pStats->unusedSize = 0; - pStats->allocationCount = 0; - pStats->unusedRangeCount = 0; - pStats->unusedRangeSizeMax = 0; + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); - VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex); + const size_t blockCount = m_Blocks.size(); + pStats->blockCount += blockCount; - for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) { const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; VMA_ASSERT(pBlock); VMA_HEAVY_ASSERT(pBlock->Validate()); - pBlock->m_Metadata.AddPoolStats(*pStats); + pBlock->m_pMetadata->AddPoolStats(*pStats); } } -static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32; +bool VmaBlockVector::IsEmpty() +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + return m_Blocks.empty(); +} + +bool VmaBlockVector::IsCorruptionDetectionEnabled() const +{ + const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + return (VMA_DEBUG_DETECT_CORRUPTION != 0) && + (VMA_DEBUG_MARGIN > 0) && + (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) && + (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags; +} VkResult VmaBlockVector::Allocate( - VmaPool hCurrentPool, - uint32_t currentFrameIndex, - const VkMemoryRequirements& vkMemReq, + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + size_t allocIndex; + VkResult res = VK_SUCCESS; + + alignment = VMA_MAX(alignment, m_MinAllocationAlignment); + + if (IsCorruptionDetectionEnabled()) + { + size = VmaAlignUp(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE)); + alignment = VmaAlignUp(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE)); + } + + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocatePage( + size, + alignment, + createInfo, + suballocType, + pAllocations + allocIndex); + if (res != VK_SUCCESS) + { + break; + } + } + } + + if (res != VK_SUCCESS) + { + // Free all already created allocations. + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + while (allocIndex--) + { + VmaAllocation_T* const alloc = pAllocations[allocIndex]; + const VkDeviceSize allocSize = alloc->GetSize(); + Free(alloc); + m_hAllocator->m_Budget.RemoveAllocation(heapIndex, allocSize); + } + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaBlockVector::AllocatePage( + VkDeviceSize size, + VkDeviceSize alignment, const VmaAllocationCreateInfo& createInfo, VmaSuballocationType suballocType, VmaAllocation* pAllocation) { + const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; - VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex); - - // 1. Search existing allocations. Try to allocate without making other allocations lost. - // Forward order in m_Blocks - prefer blocks with smallest amount of free space. - for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex ) + VkDeviceSize freeMemory; { - VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; - VMA_ASSERT(pCurrBlock); - VmaAllocationRequest currRequest = {}; - if(pCurrBlock->m_Metadata.CreateAllocationRequest( - currentFrameIndex, - m_FrameInUseCount, - m_BufferImageGranularity, - vkMemReq.size, - vkMemReq.alignment, - suballocType, - false, // canMakeOtherLost - &currRequest)) - { - // Allocate from pCurrBlock. - VMA_ASSERT(currRequest.itemsToMakeLostCount == 0); - - if(mapped) - { - VkResult res = pCurrBlock->Map(m_hAllocator, nullptr); - if(res != VK_SUCCESS) - { - return res; - } - } - - // We no longer have an empty Allocation. - if(pCurrBlock->m_Metadata.IsEmpty()) - { - m_HasEmptyBlock = false; - } - - *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString); - pCurrBlock->m_Metadata.Alloc(currRequest, suballocType, vkMemReq.size, *pAllocation); - (*pAllocation)->InitBlockAllocation( - hCurrentPool, - pCurrBlock, - currRequest.offset, - vkMemReq.alignment, - vkMemReq.size, - suballocType, - mapped, - (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0); - VMA_HEAVY_ASSERT(pCurrBlock->Validate()); - VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex); - (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData); - return VK_SUCCESS; - } + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + VmaBudget heapBudget = {}; + m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1); + freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0; } + const bool canFallbackToDedicated = !IsCustomPool(); const bool canCreateNewBlock = ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) && - (m_Blocks.size() < m_MaxBlockCount); + (m_Blocks.size() < m_MaxBlockCount) && + (freeMemory >= size || !canFallbackToDedicated); + uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK; - // 2. Try to create new block. - if(canCreateNewBlock) + // Upper address can only be used with linear allocator and within single memory block. + if (isUpperAddress && + (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1)) { - // 2.1. Start with full preferredBlockSize. - VkDeviceSize blockSize = m_PreferredBlockSize; - size_t newBlockIndex = 0; - VkResult res = CreateBlock(blockSize, &newBlockIndex); - // Allocating blocks of other sizes is allowed only in default pools. - // In custom pools block size is fixed. - if(res < 0 && m_IsCustomPool == false) - { - // 2.2. Try half the size. - blockSize /= 2; - if(blockSize >= vkMemReq.size) - { - res = CreateBlock(blockSize, &newBlockIndex); - if(res < 0) - { - // 2.3. Try quarter the size. - blockSize /= 2; - if(blockSize >= vkMemReq.size) - { - res = CreateBlock(blockSize, &newBlockIndex); - } - } - } - } - if(res == VK_SUCCESS) - { - VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex]; - VMA_ASSERT(pBlock->m_Metadata.GetSize() >= vkMemReq.size); - - if(mapped) - { - res = pBlock->Map(m_hAllocator, nullptr); - if(res != VK_SUCCESS) - { - return res; - } - } - - // Allocate from pBlock. Because it is empty, dstAllocRequest can be trivially filled. - VmaAllocationRequest allocRequest; - pBlock->m_Metadata.CreateFirstAllocationRequest(&allocRequest); - *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString); - pBlock->m_Metadata.Alloc(allocRequest, suballocType, vkMemReq.size, *pAllocation); - (*pAllocation)->InitBlockAllocation( - hCurrentPool, - pBlock, - allocRequest.offset, - vkMemReq.alignment, - vkMemReq.size, - suballocType, - mapped, - (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0); - VMA_HEAVY_ASSERT(pBlock->Validate()); - VMA_DEBUG_LOG(" Created new allocation Size=%llu", allocInfo.allocationSize); - (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData); - return VK_SUCCESS; - } + return VK_ERROR_FEATURE_NOT_PRESENT; } - const bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0; - - // 3. Try to allocate from existing blocks with making other allocations lost. - if(canMakeOtherLost) + // Validate strategy. + switch (strategy) { - uint32_t tryIndex = 0; - for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex) - { - VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL; - VmaAllocationRequest bestRequest = {}; - VkDeviceSize bestRequestCost = VK_WHOLE_SIZE; + case 0: + strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT; + break; + case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT: + case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT: + case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT: + break; + default: + return VK_ERROR_FEATURE_NOT_PRESENT; + } - // 1. Search existing allocations. + // Early reject: requested allocation size is larger that maximum block size for this block vector. + if (size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + + // 1. Search existing allocations. Try to allocate. + if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Use only last block. + if (!m_Blocks.empty()) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back(); + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, + size, + alignment, + createInfo.flags, + createInfo.pUserData, + suballocType, + strategy, + pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId()); + return VK_SUCCESS; + } + } + } + else + { + if (strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT) + { // Forward order in m_Blocks - prefer blocks with smallest amount of free space. - for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex ) + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) { VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; VMA_ASSERT(pCurrBlock); - VmaAllocationRequest currRequest = {}; - if(pCurrBlock->m_Metadata.CreateAllocationRequest( - currentFrameIndex, - m_FrameInUseCount, - m_BufferImageGranularity, - vkMemReq.size, - vkMemReq.alignment, + VkResult res = AllocateFromBlock( + pCurrBlock, + size, + alignment, + createInfo.flags, + createInfo.pUserData, suballocType, - canMakeOtherLost, - &currRequest)) + strategy, + pAllocation); + if (res == VK_SUCCESS) { - const VkDeviceSize currRequestCost = currRequest.CalcCost(); - if(pBestRequestBlock == VMA_NULL || - currRequestCost < bestRequestCost) - { - pBestRequestBlock = pCurrBlock; - bestRequest = currRequest; - bestRequestCost = currRequestCost; - - if(bestRequestCost == 0) - { - break; - } - } - } - } - - if(pBestRequestBlock != VMA_NULL) - { - if(mapped) - { - VkResult res = pBestRequestBlock->Map(m_hAllocator, nullptr); - if(res != VK_SUCCESS) - { - return res; - } - } - - if(pBestRequestBlock->m_Metadata.MakeRequestedAllocationsLost( - currentFrameIndex, - m_FrameInUseCount, - &bestRequest)) - { - // We no longer have an empty Allocation. - if(pBestRequestBlock->m_Metadata.IsEmpty()) - { - m_HasEmptyBlock = false; - } - // Allocate from this pBlock. - *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString); - pBestRequestBlock->m_Metadata.Alloc(bestRequest, suballocType, vkMemReq.size, *pAllocation); - (*pAllocation)->InitBlockAllocation( - hCurrentPool, - pBestRequestBlock, - bestRequest.offset, - vkMemReq.alignment, - vkMemReq.size, - suballocType, - mapped, - (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0); - VMA_HEAVY_ASSERT(pBlock->Validate()); - VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex); - (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData); + VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId()); return VK_SUCCESS; } - // else: Some allocations must have been touched while we are here. Next try. + } + } + else // WORST_FIT, FIRST_FIT + { + // Backward order in m_Blocks - prefer blocks with largest amount of free space. + for (size_t blockIndex = m_Blocks.size(); blockIndex--; ) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, + size, + alignment, + createInfo.flags, + createInfo.pUserData, + suballocType, + strategy, + pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId()); + return VK_SUCCESS; + } + } + } + } + + // 2. Try to create new block. + if (canCreateNewBlock) + { + // Calculate optimal size for new block. + VkDeviceSize newBlockSize = m_PreferredBlockSize; + uint32_t newBlockSizeShift = 0; + const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3; + + if (!m_ExplicitBlockSize) + { + // Allocate 1/8, 1/4, 1/2 as first blocks. + const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize(); + for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + } + else + { + break; + } + } + } + + size_t newBlockIndex = 0; + VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? + CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY; + // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize. + if (!m_ExplicitBlockSize) + { + while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize >= size) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? + CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + else + { + break; + } + } + } + + if (res == VK_SUCCESS) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex]; + VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size); + + res = AllocateFromBlock( + pBlock, + size, + alignment, + createInfo.flags, + createInfo.pUserData, + suballocType, + strategy, + pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize); + return VK_SUCCESS; } else { - // Could not find place in any of the blocks - break outer loop. - break; + // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; } } - /* Maximum number of tries exceeded - a very unlike event when many other - threads are simultaneously touching allocations making it impossible to make - lost at the same time as we try to allocate. */ - if(tryIndex == VMA_ALLOCATION_TRY_COUNT) - { - return VK_ERROR_TOO_MANY_OBJECTS; - } } return VK_ERROR_OUT_OF_DEVICE_MEMORY; } void VmaBlockVector::Free( - VmaAllocation hAllocation) + const VmaAllocation hAllocation) { VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL; + bool budgetExceeded = false; + { + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + VmaBudget heapBudget = {}; + m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1); + budgetExceeded = heapBudget.usage >= heapBudget.budget; + } + // Scope for lock. { - VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex); + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); - if(hAllocation->IsPersistentMap()) + if (IsCorruptionDetectionEnabled()) { - pBlock->m_Mapping.Unmap(m_hAllocator, pBlock->m_hMemory); + VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize()); + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value."); } - pBlock->m_Metadata.Free(hAllocation); + if (hAllocation->IsPersistentMap()) + { + pBlock->Unmap(m_hAllocator, 1); + } + + pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle()); VMA_HEAVY_ASSERT(pBlock->Validate()); - VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex); + VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex); + const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount; // pBlock became empty after this deallocation. - if(pBlock->m_Metadata.IsEmpty()) + if (pBlock->m_pMetadata->IsEmpty()) { - // Already has empty Allocation. We don't want to have two, so delete this one. - if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount) + // Already has empty block. We don't want to have two, so delete this one. + if ((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock) { pBlockToDelete = pBlock; Remove(pBlock); } - // We now have first empty Allocation. - else - { - m_HasEmptyBlock = true; - } + // else: We now have an empty block - leave it. } // pBlock didn't become empty, but we have another empty block - find and free that one. // (This is optional, heuristics.) - else if(m_HasEmptyBlock) + else if (m_HasEmptyBlock && canDeleteBlock) { VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back(); - if(pLastBlock->m_Metadata.IsEmpty() && m_Blocks.size() > m_MinBlockCount) + if (pLastBlock->m_pMetadata->IsEmpty()) { pBlockToDelete = pLastBlock; m_Blocks.pop_back(); - m_HasEmptyBlock = false; } } + UpdateHasEmptyBlock(); IncrementallySortBlocks(); } - // Destruction of a free Allocation. Deferred until this point, outside of mutex + // Destruction of a free block. Deferred until this point, outside of mutex // lock, for performance reason. - if(pBlockToDelete != VMA_NULL) + if (pBlockToDelete != VMA_NULL) { - VMA_DEBUG_LOG(" Deleted empty allocation"); + VMA_DEBUG_LOG(" Deleted empty block #%u", pBlockToDelete->GetId()); pBlockToDelete->Destroy(m_hAllocator); vma_delete(m_hAllocator, pBlockToDelete); } } +VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const +{ + VkDeviceSize result = 0; + for (size_t i = m_Blocks.size(); i--; ) + { + result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize()); + if (result >= m_PreferredBlockSize) + { + break; + } + } + return result; +} + void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock) { - for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) { - if(m_Blocks[blockIndex] == pBlock) + if (m_Blocks[blockIndex] == pBlock) { VmaVectorRemove(m_Blocks, blockIndex); return; @@ -6198,25 +12337,120 @@ void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock) void VmaBlockVector::IncrementallySortBlocks() { - // Bubble sort only until first swap. - for(size_t i = 1; i < m_Blocks.size(); ++i) + if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) { - if(m_Blocks[i - 1]->m_Metadata.GetSumFreeSize() > m_Blocks[i]->m_Metadata.GetSumFreeSize()) + // Bubble sort only until first swap. + for (size_t i = 1; i < m_Blocks.size(); ++i) { - VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]); - return; + if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize()) + { + VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]); + return; + } } } } +VkResult VmaBlockVector::AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation) +{ + const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; + const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; + + VmaAllocationRequest currRequest = {}; + if (pBlock->m_pMetadata->CreateAllocationRequest( + size, + alignment, + isUpperAddress, + suballocType, + strategy, + &currRequest)) + { + // Allocate from pCurrBlock. + if (mapped) + { + VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL); + if (res != VK_SUCCESS) + { + return res; + } + } + + *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isUserDataString); + pBlock->m_pMetadata->Alloc(currRequest, suballocType, *pAllocation); + UpdateHasEmptyBlock(); + (*pAllocation)->InitBlockAllocation( + pBlock, + currRequest.allocHandle, + alignment, + currRequest.size, // Not size, as actual allocation size may be larger than requested! + m_MemoryTypeIndex, + suballocType, + mapped); + VMA_HEAVY_ASSERT(pBlock->Validate()); + (*pAllocation)->SetUserData(m_hAllocator, pUserData); + m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), currRequest.size); + if (VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + if (IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, (*pAllocation)->GetOffset(), currRequest.size); + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); + } + return VK_SUCCESS; + } + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex) { VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocInfo.pNext = m_pMemoryAllocateNext; allocInfo.memoryTypeIndex = m_MemoryTypeIndex; allocInfo.allocationSize = blockSize; + +#if VMA_BUFFER_DEVICE_ADDRESS + // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature. + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR }; + if (m_hAllocator->m_UseKhrBufferDeviceAddress) + { + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo); + } +#endif // VMA_BUFFER_DEVICE_ADDRESS + +#if VMA_MEMORY_PRIORITY + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + if (m_hAllocator->m_UseExtMemoryPriority) + { + priorityInfo.priority = m_Priority; + VmaPnextChainPushFront(&allocInfo, &priorityInfo); + } +#endif // VMA_MEMORY_PRIORITY + +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex); + if (exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // VMA_EXTERNAL_MEMORY + VkDeviceMemory mem = VK_NULL_HANDLE; VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem); - if(res < 0) + if (res < 0) { return res; } @@ -6226,12 +12460,17 @@ VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIn // Create new Allocation for it. VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator); pBlock->Init( + m_hAllocator, + m_hParentPool, m_MemoryTypeIndex, mem, - allocInfo.allocationSize); + allocInfo.allocationSize, + m_NextBlockId++, + m_Algorithm, + m_BufferImageGranularity); m_Blocks.push_back(pBlock); - if(pNewBlockIndex != VMA_NULL) + if (pNewBlockIndex != VMA_NULL) { *pNewBlockIndex = m_Blocks.size() - 1; } @@ -6239,16 +12478,258 @@ VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIn return VK_SUCCESS; } -#if VMA_STATS_STRING_ENABLED +void VmaBlockVector::ApplyDefragmentationMovesCpu( + VmaBlockVectorDefragmentationContext* pDefragCtx, + const VmaVector>& moves) +{ + const size_t blockCount = m_Blocks.size(); + const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex); + enum BLOCK_FLAG + { + BLOCK_FLAG_USED = 0x00000001, + BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002, + }; + + struct BlockInfo + { + uint32_t flags; + void* pMappedData; + }; + VmaVector< BlockInfo, VmaStlAllocator > + blockInfo(blockCount, BlockInfo(), VmaStlAllocator(m_hAllocator->GetAllocationCallbacks())); + memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo)); + + // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED. + const size_t moveCount = moves.size(); + for (size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { + const VmaDefragmentationMove& move = moves[moveIndex]; + blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED; + blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED; + } + + VMA_ASSERT(pDefragCtx->res == VK_SUCCESS); + + // Go over all blocks. Get mapped pointer or map if necessary. + for (size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex) + { + BlockInfo& currBlockInfo = blockInfo[blockIndex]; + VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; + if ((currBlockInfo.flags & BLOCK_FLAG_USED) != 0) + { + currBlockInfo.pMappedData = pBlock->GetMappedData(); + // It is not originally mapped - map it. + if (currBlockInfo.pMappedData == VMA_NULL) + { + pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData); + if (pDefragCtx->res == VK_SUCCESS) + { + currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION; + } + } + } + } + + // Go over all moves. Do actual data transfer. + if (pDefragCtx->res == VK_SUCCESS) + { + const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; + + for (size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { + const VmaDefragmentationMove& move = moves[moveIndex]; + + const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex]; + const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex]; + + VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData); + + // Invalidate source. + if (isNonCoherent) + { + VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex]; + memRange.memory = pSrcBlock->GetDeviceMemory(); + memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize); + memRange.size = VMA_MIN( + VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize), + pSrcBlock->m_pMetadata->GetSize() - memRange.offset); + (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange); + } + + // THE PLACE WHERE ACTUAL DATA COPY HAPPENS. + memmove( + reinterpret_cast(dstBlockInfo.pMappedData) + move.dstOffset, + reinterpret_cast(srcBlockInfo.pMappedData) + move.srcOffset, + static_cast(move.size)); + + if (IsCorruptionDetectionEnabled()) + { + VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN); + VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size); + } + + // Flush destination. + if (isNonCoherent) + { + VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex]; + memRange.memory = pDstBlock->GetDeviceMemory(); + memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize); + memRange.size = VMA_MIN( + VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize), + pDstBlock->m_pMetadata->GetSize() - memRange.offset); + (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange); + } + } + } + + // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation. + // Regardless of pCtx->res == VK_SUCCESS. + for (size_t blockIndex = blockCount; blockIndex--; ) + { + const BlockInfo& currBlockInfo = blockInfo[blockIndex]; + if ((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0) + { + VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; + pBlock->Unmap(m_hAllocator, 1); + } + } +} + +void VmaBlockVector::ApplyDefragmentationMovesGpu( + VmaBlockVectorDefragmentationContext* pDefragCtx, + VmaVector>& moves, + VkCommandBuffer commandBuffer) +{ + const size_t blockCount = m_Blocks.size(); + + pDefragCtx->blockContexts.resize(blockCount); + memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext)); + + // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED. + const size_t moveCount = moves.size(); + for (size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { + const VmaDefragmentationMove& move = moves[moveIndex]; + + //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN) + { + // Old school move still require us to map the whole block + pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED; + pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED; + } + } + + VMA_ASSERT(pDefragCtx->res == VK_SUCCESS); + + // Go over all blocks. Create and bind buffer for whole block if necessary. + { + VkBufferCreateInfo bufCreateInfo; + VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo); + + for (size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex) + { + VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex]; + VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; + if ((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0) + { + bufCreateInfo.size = pBlock->m_pMetadata->GetSize(); + pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)( + m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer); + if (pDefragCtx->res == VK_SUCCESS) + { + pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)( + m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0); + } + } + } + } + + // Go over all moves. Post data transfer commands to command buffer. + if (pDefragCtx->res == VK_SUCCESS) + { + for (size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex) + { + const VmaDefragmentationMove& move = moves[moveIndex]; + + const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex]; + const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex]; + + VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer); + + VkBufferCopy region = { + move.srcOffset, + move.dstOffset, + move.size }; + (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)( + commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion); + } + } + + // Save buffers to defrag context for later destruction. + if (pDefragCtx->res == VK_SUCCESS && moveCount > 0) + { + pDefragCtx->res = VK_NOT_READY; + } +} + +void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats) +{ + for (size_t blockIndex = m_Blocks.size(); blockIndex--; ) + { + VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; + if (pBlock->m_pMetadata->IsEmpty()) + { + if (m_Blocks.size() > m_MinBlockCount) + { + if (pDefragmentationStats != VMA_NULL) + { + ++pDefragmentationStats->deviceMemoryBlocksFreed; + pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize(); + } + + VmaVectorRemove(m_Blocks, blockIndex); + pBlock->Destroy(m_hAllocator); + vma_delete(m_hAllocator, pBlock); + } + else + { + break; + } + } + } + UpdateHasEmptyBlock(); +} + +void VmaBlockVector::UpdateHasEmptyBlock() +{ + m_HasEmptyBlock = false; + for (size_t index = 0, count = m_Blocks.size(); index < count; ++index) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[index]; + if (pBlock->m_pMetadata->IsEmpty()) + { + m_HasEmptyBlock = true; + break; + } + } +} + +#if VMA_STATS_STRING_ENABLED void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json) { - VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex); + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); - json.BeginObject(); - - if(m_IsCustomPool) + if (IsCustomPool()) { + const char* poolName = m_hParentPool->GetName(); + if (poolName != VMA_NULL && poolName[0] != '\0') + { + json.WriteString("Name"); + json.WriteString(poolName); + } + json.WriteString("MemoryTypeIndex"); json.WriteNumber(m_MemoryTypeIndex); @@ -6257,24 +12738,24 @@ void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json) json.WriteString("BlockCount"); json.BeginObject(true); - if(m_MinBlockCount > 0) + if (m_MinBlockCount > 0) { json.WriteString("Min"); - json.WriteNumber(m_MinBlockCount); + json.WriteNumber((uint64_t)m_MinBlockCount); } - if(m_MaxBlockCount < SIZE_MAX) + if (m_MaxBlockCount < SIZE_MAX) { json.WriteString("Max"); - json.WriteNumber(m_MaxBlockCount); + json.WriteNumber((uint64_t)m_MaxBlockCount); } json.WriteString("Cur"); - json.WriteNumber(m_Blocks.size()); + json.WriteNumber((uint64_t)m_Blocks.size()); json.EndObject(); - if(m_FrameInUseCount > 0) + if (m_Algorithm != 0) { - json.WriteString("FrameInUseCount"); - json.WriteNumber(m_FrameInUseCount); + json.WriteString("Algorithm"); + json.WriteString(VmaAlgorithmToStr(m_Algorithm)); } } else @@ -6284,111 +12765,260 @@ void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json) } json.WriteString("Blocks"); - json.BeginArray(); - for(size_t i = 0; i < m_Blocks.size(); ++i) + json.BeginObject(); + for (size_t i = 0; i < m_Blocks.size(); ++i) { - m_Blocks[i]->m_Metadata.PrintDetailedMap(json); - } - json.EndArray(); + json.BeginString(); + json.ContinueString(m_Blocks[i]->GetId()); + json.EndString(); + m_Blocks[i]->m_pMetadata->PrintDetailedMap(json); + } json.EndObject(); } +#endif // VMA_STATS_STRING_ENABLED -#endif // #if VMA_STATS_STRING_ENABLED - -VmaDefragmentator* VmaBlockVector::EnsureDefragmentator( - VmaAllocator hAllocator, - uint32_t currentFrameIndex) +void VmaBlockVector::Defragment( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags, + VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove, + VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove, + VkCommandBuffer commandBuffer) { - if(m_pDefragmentator == VMA_NULL) + pCtx->res = VK_SUCCESS; + + const VkMemoryPropertyFlags memPropFlags = + m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags; + const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; + + const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 && + isHostVisible; + const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 && + !IsCorruptionDetectionEnabled() && + ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0; + + // There are options to defragment this memory type. + if (canDefragmentOnCpu || canDefragmentOnGpu) { - m_pDefragmentator = vma_new(m_hAllocator, VmaDefragmentator)( - hAllocator, - this, - currentFrameIndex); - } - - return m_pDefragmentator; -} - -VkResult VmaBlockVector::Defragment( - VmaDefragmentationStats* pDefragmentationStats, - VkDeviceSize& maxBytesToMove, - uint32_t& maxAllocationsToMove) -{ - if(m_pDefragmentator == VMA_NULL) - { - return VK_SUCCESS; - } - - VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex); - - // Defragment. - VkResult result = m_pDefragmentator->Defragment(maxBytesToMove, maxAllocationsToMove); - - // Accumulate statistics. - if(pDefragmentationStats != VMA_NULL) - { - const VkDeviceSize bytesMoved = m_pDefragmentator->GetBytesMoved(); - const uint32_t allocationsMoved = m_pDefragmentator->GetAllocationsMoved(); - pDefragmentationStats->bytesMoved += bytesMoved; - pDefragmentationStats->allocationsMoved += allocationsMoved; - VMA_ASSERT(bytesMoved <= maxBytesToMove); - VMA_ASSERT(allocationsMoved <= maxAllocationsToMove); - maxBytesToMove -= bytesMoved; - maxAllocationsToMove -= allocationsMoved; - } - - // Free empty blocks. - m_HasEmptyBlock = false; - for(size_t blockIndex = m_Blocks.size(); blockIndex--; ) - { - VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex]; - if(pBlock->m_Metadata.IsEmpty()) + bool defragmentOnGpu; + // There is only one option to defragment this memory type. + if (canDefragmentOnGpu != canDefragmentOnCpu) { - if(m_Blocks.size() > m_MinBlockCount) - { - if(pDefragmentationStats != VMA_NULL) - { - ++pDefragmentationStats->deviceMemoryBlocksFreed; - pDefragmentationStats->bytesFreed += pBlock->m_Metadata.GetSize(); - } + defragmentOnGpu = canDefragmentOnGpu; + } + // Both options are available: Heuristics to choose the best one. + else + { + defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 || + m_hAllocator->IsIntegratedGpu(); + } - VmaVectorRemove(m_Blocks, blockIndex); - pBlock->Destroy(m_hAllocator); - vma_delete(m_hAllocator, pBlock); + bool overlappingMoveSupported = !defragmentOnGpu; + + if (m_hAllocator->m_UseMutex) + { + if (flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL) + { + if (!m_Mutex.TryLockWrite()) + { + pCtx->res = VK_ERROR_INITIALIZATION_FAILED; + return; + } } else { - m_HasEmptyBlock = true; + m_Mutex.LockWrite(); + pCtx->mutexLocked = true; + } + } + + pCtx->Begin(overlappingMoveSupported, flags); + + // Defragment. + + const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove; + const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove; + VmaDefragmentationAlgorithm* algo = pCtx->GetAlgorithm(); + pCtx->res = algo->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags); + + // Accumulate statistics. + if (pStats != VMA_NULL) + { + const VkDeviceSize bytesMoved = algo->GetBytesMoved(); + const uint32_t allocationsMoved = algo->GetAllocationsMoved(); + pStats->bytesMoved += bytesMoved; + pStats->allocationsMoved += allocationsMoved; + VMA_ASSERT(bytesMoved <= maxBytesToMove); + VMA_ASSERT(allocationsMoved <= maxAllocationsToMove); + if (defragmentOnGpu) + { + maxGpuBytesToMove -= bytesMoved; + maxGpuAllocationsToMove -= allocationsMoved; + } + else + { + maxCpuBytesToMove -= bytesMoved; + maxCpuAllocationsToMove -= allocationsMoved; + } + } + + if (flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL) + { + if (m_hAllocator->m_UseMutex) + m_Mutex.UnlockWrite(); + + if (pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty()) + pCtx->res = VK_NOT_READY; + + return; + } + + if (pCtx->res >= VK_SUCCESS) + { + if (defragmentOnGpu) + { + ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer); + } + else + { + ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves); } } } +} +void VmaBlockVector::DefragmentationEnd( + class VmaBlockVectorDefragmentationContext* pCtx, + uint32_t flags, + VmaDefragmentationStats* pStats) +{ + if (flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex) + { + VMA_ASSERT(pCtx->mutexLocked == false); + + // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any + // lock protecting us. Since we mutate state here, we have to take the lock out now + m_Mutex.LockWrite(); + pCtx->mutexLocked = true; + } + + // If the mutex isn't locked we didn't do any work and there is nothing to delete. + if (pCtx->mutexLocked || !m_hAllocator->m_UseMutex) + { + // Destroy buffers. + for (size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;) + { + VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex]; + if (blockCtx.hBuffer) + { + (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks()); + } + } + + if (pCtx->res >= VK_SUCCESS) + { + FreeEmptyBlocks(pStats); + } + } + + if (pCtx->mutexLocked) + { + VMA_ASSERT(m_hAllocator->m_UseMutex); + m_Mutex.UnlockWrite(); + } +} + +uint32_t VmaBlockVector::ProcessDefragmentations( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves) +{ + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + + const uint32_t moveCount = VMA_MIN(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves); + + for (uint32_t i = 0; i < moveCount; ++i) + { + VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i]; + + pMove->allocation = move.hAllocation; + pMove->memory = move.pDstBlock->GetDeviceMemory(); + pMove->offset = move.dstOffset; + + ++pMove; + } + + pCtx->defragmentationMovesProcessed += moveCount; + + return moveCount; +} + +void VmaBlockVector::CommitDefragmentations( + class VmaBlockVectorDefragmentationContext* pCtx, + VmaDefragmentationStats* pStats) +{ + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + + for (uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++i) + { + const VmaDefragmentationMove& move = pCtx->defragmentationMoves[i]; + + move.pSrcBlock->m_pMetadata->Free(move.hAllocation->GetAllocHandle()); + move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstHandle); + } + + pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed; + FreeEmptyBlocks(pStats); +} + +size_t VmaBlockVector::CalcAllocationCount() const +{ + size_t result = 0; + for (size_t i = 0; i < m_Blocks.size(); ++i) + { + result += m_Blocks[i]->m_pMetadata->GetAllocationCount(); + } return result; } -void VmaBlockVector::DestroyDefragmentator() +bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const { - if(m_pDefragmentator != VMA_NULL) + if (m_BufferImageGranularity == 1) { - vma_delete(m_hAllocator, m_pDefragmentator); - m_pDefragmentator = VMA_NULL; + return false; } + VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE; + for (size_t i = 0, count = m_Blocks.size(); i < count; ++i) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[i]; + VMA_ASSERT(m_Algorithm == 0); + VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata; + if (pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType)) + { + return true; + } + } + return false; } -void VmaBlockVector::MakePoolAllocationsLost( - uint32_t currentFrameIndex, - size_t* pLostAllocationCount) +VkResult VmaBlockVector::CheckCorruption() { - VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex); + if (!IsCorruptionDetectionEnabled()) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } - for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) { VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; VMA_ASSERT(pBlock); - pBlock->m_Metadata.MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount); + VkResult res = pBlock->CheckCorruption(m_hAllocator); + if (res != VK_SUCCESS) + { + return res; + } } + return VK_SUCCESS; } void VmaBlockVector::AddStats(VmaStats* pStats) @@ -6396,106 +13026,118 @@ void VmaBlockVector::AddStats(VmaStats* pStats) const uint32_t memTypeIndex = m_MemoryTypeIndex; const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex); - VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex); + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); - for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) { const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; VMA_ASSERT(pBlock); VMA_HEAVY_ASSERT(pBlock->Validate()); VmaStatInfo allocationStatInfo; - pBlock->m_Metadata.CalcAllocationStatInfo(allocationStatInfo); + pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo); VmaAddStatInfo(pStats->total, allocationStatInfo); VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo); VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo); } } +#endif // _VMA_BLOCK_VECTOR_FUNCTIONS -//////////////////////////////////////////////////////////////////////////////// -// VmaDefragmentator members definition - -VmaDefragmentator::VmaDefragmentator( +#ifndef _VMA_DEFRAGMENTATION_ALGORITHM_GENERIC_FUNCTIONS +VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic( VmaAllocator hAllocator, VmaBlockVector* pBlockVector, - uint32_t currentFrameIndex) : - m_hAllocator(hAllocator), - m_pBlockVector(pBlockVector), - m_CurrentFrameIndex(currentFrameIndex), + bool overlappingMoveSupported) + : VmaDefragmentationAlgorithm(hAllocator, pBlockVector), + m_AllocationCount(0), + m_AllAllocations(false), m_BytesMoved(0), m_AllocationsMoved(0), - m_Allocations(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) { + // Create block info for each block. + const size_t blockCount = m_pBlockVector->m_Blocks.size(); + for (size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks()); + pBlockInfo->m_OriginalBlockIndex = blockIndex; + pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex]; + m_Blocks.push_back(pBlockInfo); + } + + // Sort them by m_pBlock pointer value. + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess()); } -VmaDefragmentator::~VmaDefragmentator() +VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic() { - for(size_t i = m_Blocks.size(); i--; ) + for (size_t i = m_Blocks.size(); i--; ) { vma_delete(m_hAllocator, m_Blocks[i]); } } -void VmaDefragmentator::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) +void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { - AllocationInfo allocInfo; - allocInfo.m_hAllocation = hAlloc; - allocInfo.m_pChanged = pChanged; - m_Allocations.push_back(allocInfo); + VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock(); + BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess()); + if (it != m_Blocks.end() && (*it)->m_pBlock == pBlock) + { + AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged); + (*it)->m_Allocations.push_back(allocInfo); + } + else + { + VMA_ASSERT(0); + } + + ++m_AllocationCount; } -VkResult VmaDefragmentator::BlockInfo::EnsureMapping(VmaAllocator hAllocator, void** ppMappedData) -{ - // It has already been mapped for defragmentation. - if(m_pMappedDataForDefragmentation) - { - *ppMappedData = m_pMappedDataForDefragmentation; - return VK_SUCCESS; - } - - // It is originally mapped. - if(m_pBlock->m_Mapping.GetMappedData()) - { - *ppMappedData = m_pBlock->m_Mapping.GetMappedData(); - return VK_SUCCESS; - } - - // Map on first usage. - VkResult res = m_pBlock->Map(hAllocator, &m_pMappedDataForDefragmentation); - *ppMappedData = m_pMappedDataForDefragmentation; - return res; -} - -void VmaDefragmentator::BlockInfo::Unmap(VmaAllocator hAllocator) -{ - if(m_pMappedDataForDefragmentation != VMA_NULL) - { - m_pBlock->Unmap(hAllocator); - } -} - -VkResult VmaDefragmentator::DefragmentRound( +VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, VkDeviceSize maxBytesToMove, - uint32_t maxAllocationsToMove) + uint32_t maxAllocationsToMove, + bool freeOldAllocations) { - if(m_Blocks.empty()) + if (m_Blocks.empty()) { return VK_SUCCESS; } + // This is a choice based on research. + // Option 1: + uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT; + // Option 2: + //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT; + // Option 3: + //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT; + + size_t srcBlockMinIndex = 0; + // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations. + /* + if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT) + { + const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount(); + if(blocksWithNonMovableCount > 0) + { + srcBlockMinIndex = blocksWithNonMovableCount - 1; + } + } + */ + size_t srcBlockIndex = m_Blocks.size() - 1; size_t srcAllocIndex = SIZE_MAX; - for(;;) + for (;;) { // 1. Find next allocation to move. // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source". - // 1.2. Then start from last to first m_Allocations - they are sorted from largest to smallest. - while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size()) + // 1.2. Then start from last to first m_Allocations. + while (srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size()) { - if(m_Blocks[srcBlockIndex]->m_Allocations.empty()) + if (m_Blocks[srcBlockIndex]->m_Allocations.empty()) { // Finished: no more allocations to process. - if(srcBlockIndex == 0) + if (srcBlockIndex == srcBlockMinIndex) { return VK_SUCCESS; } @@ -6510,7 +13152,7 @@ VkResult VmaDefragmentator::DefragmentRound( srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1; } } - + BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex]; AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex]; @@ -6520,57 +13162,50 @@ VkResult VmaDefragmentator::DefragmentRound( const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType(); // 2. Try to find new place for this allocation in preceding or current block. - for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex) + for (size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex) { BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex]; + VmaBlockMetadata* pMetadata = pDstBlockInfo->m_pBlock->m_pMetadata; VmaAllocationRequest dstAllocRequest; - if(pDstBlockInfo->m_pBlock->m_Metadata.CreateAllocationRequest( - m_CurrentFrameIndex, - m_pBlockVector->GetFrameInUseCount(), - m_pBlockVector->GetBufferImageGranularity(), + if (pMetadata->CreateAllocationRequest( size, alignment, + false, // upperAddress suballocType, - false, // canMakeOtherLost + strategy, &dstAllocRequest) && - MoveMakesSense( - dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset)) + MoveMakesSense( + dstBlockIndex, pMetadata->GetAllocationOffset(dstAllocRequest.allocHandle), srcBlockIndex, srcOffset)) { - VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0); - // Reached limit on number of allocations or bytes to move. - if((m_AllocationsMoved + 1 > maxAllocationsToMove) || + if ((m_AllocationsMoved + 1 > maxAllocationsToMove) || (m_BytesMoved + size > maxBytesToMove)) { - return VK_INCOMPLETE; + return VK_SUCCESS; } - void* pDstMappedData = VMA_NULL; - VkResult res = pDstBlockInfo->EnsureMapping(m_hAllocator, &pDstMappedData); - if(res != VK_SUCCESS) + VmaDefragmentationMove move = {}; + move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex; + move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex; + move.srcOffset = srcOffset; + move.dstOffset = pMetadata->GetAllocationOffset(dstAllocRequest.allocHandle); + move.size = size; + move.hAllocation = allocInfo.m_hAllocation; + move.pSrcBlock = pSrcBlockInfo->m_pBlock; + move.pDstBlock = pDstBlockInfo->m_pBlock; + move.dstHandle = dstAllocRequest.allocHandle; + + moves.push_back(move); + + pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(dstAllocRequest, suballocType, allocInfo.m_hAllocation); + + if (freeOldAllocations) { - return res; + pSrcBlockInfo->m_pBlock->m_pMetadata->Free(allocInfo.m_hAllocation->GetAllocHandle()); + allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.allocHandle); } - void* pSrcMappedData = VMA_NULL; - res = pSrcBlockInfo->EnsureMapping(m_hAllocator, &pSrcMappedData); - if(res != VK_SUCCESS) - { - return res; - } - - // THE PLACE WHERE ACTUAL DATA COPY HAPPENS. - memcpy( - reinterpret_cast(pDstMappedData) + dstAllocRequest.offset, - reinterpret_cast(pSrcMappedData) + srcOffset, - static_cast(size)); - - pDstBlockInfo->m_pBlock->m_Metadata.Alloc(dstAllocRequest, suballocType, size, allocInfo.m_hAllocation); - pSrcBlockInfo->m_pBlock->m_Metadata.Free(allocInfo.m_hAllocation); - - allocInfo.m_hAllocation->ChangeBlockAllocation(pDstBlockInfo->m_pBlock, dstAllocRequest.offset); - - if(allocInfo.m_pChanged != VMA_NULL) + if (allocInfo.m_pChanged != VMA_NULL) { *allocInfo.m_pChanged = VK_TRUE; } @@ -6586,13 +13221,13 @@ VkResult VmaDefragmentator::DefragmentRound( // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round. - if(srcAllocIndex > 0) + if (srcAllocIndex > 0) { --srcAllocIndex; } else { - if(srcBlockIndex > 0) + if (srcBlockIndex > 0) { --srcBlockIndex; srcAllocIndex = SIZE_MAX; @@ -6605,125 +13240,1191 @@ VkResult VmaDefragmentator::DefragmentRound( } } -VkResult VmaDefragmentator::Defragment( - VkDeviceSize maxBytesToMove, - uint32_t maxAllocationsToMove) +bool VmaDefragmentationAlgorithm_Generic::AllocationInfoSizeGreater::operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const { - if(m_Allocations.empty()) - { - return VK_SUCCESS; - } - - // Create block info for each block. - const size_t blockCount = m_pBlockVector->m_Blocks.size(); - for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) - { - BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks()); - pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex]; - m_Blocks.push_back(pBlockInfo); - } - - // Sort them by m_pBlock pointer value. - VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess()); - - // Move allocation infos from m_Allocations to appropriate m_Blocks[memTypeIndex].m_Allocations. - for(size_t blockIndex = 0, allocCount = m_Allocations.size(); blockIndex < allocCount; ++blockIndex) - { - AllocationInfo& allocInfo = m_Allocations[blockIndex]; - // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost. - if(allocInfo.m_hAllocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST) - { - VmaDeviceMemoryBlock* pBlock = allocInfo.m_hAllocation->GetBlock(); - BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess()); - if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock) - { - (*it)->m_Allocations.push_back(allocInfo); - } - else - { - VMA_ASSERT(0); - } - } - } - m_Allocations.clear(); - - for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) - { - BlockInfo* pBlockInfo = m_Blocks[blockIndex]; - pBlockInfo->CalcHasNonMovableAllocations(); - pBlockInfo->SortAllocationsBySizeDescecnding(); - } - - // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks. - VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination()); - - // Execute defragmentation rounds (the main part). - VkResult result = VK_SUCCESS; - for(size_t round = 0; (round < 2) && (result == VK_SUCCESS); ++round) - { - result = DefragmentRound(maxBytesToMove, maxAllocationsToMove); - } - - // Unmap blocks that were mapped for defragmentation. - for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) - { - m_Blocks[blockIndex]->Unmap(m_hAllocator); - } - - return result; + return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize(); } -bool VmaDefragmentator::MoveMakesSense( - size_t dstBlockIndex, VkDeviceSize dstOffset, - size_t srcBlockIndex, VkDeviceSize srcOffset) +bool VmaDefragmentationAlgorithm_Generic::AllocationInfoOffsetGreater::operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const { - if(dstBlockIndex < srcBlockIndex) + return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset(); +} + +VmaDefragmentationAlgorithm_Generic::BlockInfo::BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) + : m_OriginalBlockIndex(SIZE_MAX), + m_pBlock(VMA_NULL), + m_HasNonMovableAllocations(true), + m_Allocations(pAllocationCallbacks) {} + +void VmaDefragmentationAlgorithm_Generic::BlockInfo::CalcHasNonMovableAllocations() +{ + const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount(); + const size_t defragmentAllocCount = m_Allocations.size(); + m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount; +} + +void VmaDefragmentationAlgorithm_Generic::BlockInfo::SortAllocationsBySizeDescending() +{ + VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater()); +} + +void VmaDefragmentationAlgorithm_Generic::BlockInfo::SortAllocationsByOffsetDescending() +{ + VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater()); +} + +bool VmaDefragmentationAlgorithm_Generic::BlockPointerLess::operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const +{ + return pLhsBlockInfo->m_pBlock < pRhsBlock; +} +bool VmaDefragmentationAlgorithm_Generic::BlockPointerLess::operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const +{ + return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock; +} + +bool VmaDefragmentationAlgorithm_Generic::BlockInfoCompareMoveDestination::operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const +{ + if (pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations) { return true; } - if(dstBlockIndex > srcBlockIndex) + if (!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations) { return false; } - if(dstOffset < srcOffset) + if (pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize()) { return true; } return false; } -//////////////////////////////////////////////////////////////////////////////// -// VmaAllocator_T +bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense( + size_t dstBlockIndex, VkDeviceSize dstOffset, + size_t srcBlockIndex, VkDeviceSize srcOffset) +{ + if (dstBlockIndex < srcBlockIndex) + { + return true; + } + if (dstBlockIndex > srcBlockIndex) + { + return false; + } + if (dstOffset < srcOffset) + { + return true; + } + return false; +} +size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const +{ + size_t result = 0; + for (size_t i = 0; i < m_Blocks.size(); ++i) + { + if (m_Blocks[i]->m_HasNonMovableAllocations) + { + ++result; + } + } + return result; +} + +VkResult VmaDefragmentationAlgorithm_Generic::Defragment( + VmaVector< VmaDefragmentationMove, VmaStlAllocator >& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove, + VmaDefragmentationFlags flags) +{ + if (!m_AllAllocations && m_AllocationCount == 0) + { + return VK_SUCCESS; + } + + const size_t blockCount = m_Blocks.size(); + for (size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + BlockInfo* pBlockInfo = m_Blocks[blockIndex]; + + if (m_AllAllocations) + { + VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata; + VMA_ASSERT(!pMetadata->IsVirtual()); + for (VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin(); + it != pMetadata->m_Suballocations.end(); + ++it) + { + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + { + AllocationInfo allocInfo = AllocationInfo((VmaAllocation)it->userData, VMA_NULL); + pBlockInfo->m_Allocations.push_back(allocInfo); + } + } + } + + pBlockInfo->CalcHasNonMovableAllocations(); + + // This is a choice based on research. + // Option 1: + pBlockInfo->SortAllocationsByOffsetDescending(); + // Option 2: + //pBlockInfo->SortAllocationsBySizeDescending(); + } + + // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks. + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination()); + + // This is a choice based on research. + const uint32_t roundCount = 2; + + // Execute defragmentation rounds (the main part). + VkResult result = VK_SUCCESS; + for (uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round) + { + result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)); + } + + return result; +} +#endif // _VMA_DEFRAGMENTATION_ALGORITHM_GENERIC_FUNCTIONS + +#ifndef _VMA_DEFRAGMENTATION_ALGORITHM_FAST_FUNCTIONS +VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast( + VmaAllocator hAllocator, + VmaBlockVector* pBlockVector, + bool overlappingMoveSupported) + : VmaDefragmentationAlgorithm(hAllocator, pBlockVector), + m_OverlappingMoveSupported(overlappingMoveSupported), + m_AllocationCount(0), + m_AllAllocations(false), + m_BytesMoved(0), + m_AllocationsMoved(0), + m_BlockInfos(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN == 0); +} + +VkResult VmaDefragmentationAlgorithm_Fast::Defragment( + VmaVector>& moves, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove, + VmaDefragmentationFlags flags) +{ + VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount); + + const size_t blockCount = m_pBlockVector->GetBlockCount(); + if (blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0) + { + return VK_SUCCESS; + } + + PreprocessMetadata(); + + // Sort blocks in order from most destination. + + m_BlockInfos.resize(blockCount); + for (size_t i = 0; i < blockCount; ++i) + { + m_BlockInfos[i].origBlockIndex = i; + } + + VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool { + return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() < + m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize(); + }); + + // THE MAIN ALGORITHM + + FreeSpaceDatabase freeSpaceDb; + + size_t dstBlockInfoIndex = 0; + size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex; + VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex); + VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata; + VkDeviceSize dstBlockSize = pDstMetadata->GetSize(); + VkDeviceSize dstOffset = 0; + + bool end = false; + for (size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex) + { + const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex; + VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex); + VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata; + for (VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin(); + !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); ) + { + VmaAllocation const pAlloc = (VmaAllocation)srcSuballocIt->userData; + const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment(); + const VkDeviceSize srcAllocSize = srcSuballocIt->size; + if (m_AllocationsMoved == maxAllocationsToMove || + m_BytesMoved + srcAllocSize > maxBytesToMove) + { + end = true; + break; + } + const VkDeviceSize srcAllocOffset = srcSuballocIt->offset; + + VmaDefragmentationMove move = {}; + // Try to place it in one of free spaces from the database. + size_t freeSpaceInfoIndex; + VkDeviceSize dstAllocOffset; + if (freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize, + freeSpaceInfoIndex, dstAllocOffset)) + { + size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex; + VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex); + VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata; + + // Same block + if (freeSpaceInfoIndex == srcBlockInfoIndex) + { + VMA_ASSERT(dstAllocOffset <= srcAllocOffset); + + // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset. + + VmaSuballocation suballoc = *srcSuballocIt; + suballoc.offset = dstAllocOffset; + ((VmaAllocation)(suballoc.userData))->ChangeAllocHandle((VmaAllocHandle)dstAllocOffset); + m_BytesMoved += srcAllocSize; + ++m_AllocationsMoved; + + VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; + ++nextSuballocIt; + pSrcMetadata->m_Suballocations.erase(srcSuballocIt); + srcSuballocIt = nextSuballocIt; + + InsertSuballoc(pFreeSpaceMetadata, suballoc); + + move.srcBlockIndex = srcOrigBlockIndex; + move.dstBlockIndex = freeSpaceOrigBlockIndex; + move.srcOffset = srcAllocOffset; + move.dstOffset = dstAllocOffset; + move.dstHandle = (VmaAllocHandle)dstAllocOffset; + move.size = srcAllocSize; + + moves.push_back(move); + } + // Different block + else + { + // MOVE OPTION 2: Move the allocation to a different block. + + VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex); + + VmaSuballocation suballoc = *srcSuballocIt; + suballoc.offset = dstAllocOffset; + ((VmaAllocation)(suballoc.userData))->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, (VmaAllocHandle)dstAllocOffset); + m_BytesMoved += srcAllocSize; + ++m_AllocationsMoved; + + VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; + ++nextSuballocIt; + pSrcMetadata->m_Suballocations.erase(srcSuballocIt); + srcSuballocIt = nextSuballocIt; + + InsertSuballoc(pFreeSpaceMetadata, suballoc); + + move.srcBlockIndex = srcOrigBlockIndex; + move.dstBlockIndex = freeSpaceOrigBlockIndex; + move.srcOffset = srcAllocOffset; + move.dstOffset = dstAllocOffset; + move.dstHandle = (VmaAllocHandle)dstAllocOffset; + move.size = srcAllocSize; + + moves.push_back(move); + } + } + else + { + dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment); + + // If the allocation doesn't fit before the end of dstBlock, forward to next block. + while (dstBlockInfoIndex < srcBlockInfoIndex && + dstAllocOffset + srcAllocSize > dstBlockSize) + { + // But before that, register remaining free space at the end of dst block. + freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset); + + ++dstBlockInfoIndex; + dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex; + pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex); + pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata; + dstBlockSize = pDstMetadata->GetSize(); + dstOffset = 0; + dstAllocOffset = 0; + } + + // Same block + if (dstBlockInfoIndex == srcBlockInfoIndex) + { + VMA_ASSERT(dstAllocOffset <= srcAllocOffset); + + const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset; + + bool skipOver = overlap; + if (overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset) + { + // If destination and source place overlap, skip if it would move it + // by only < 1/64 of its size. + skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize; + } + + if (skipOver) + { + freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset); + + dstOffset = srcAllocOffset + srcAllocSize; + ++srcSuballocIt; + } + // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset. + else + { + srcSuballocIt->offset = dstAllocOffset; + ((VmaAllocation)(srcSuballocIt->userData))->ChangeAllocHandle((VmaAllocHandle)dstAllocOffset); + dstOffset = dstAllocOffset + srcAllocSize; + m_BytesMoved += srcAllocSize; + ++m_AllocationsMoved; + ++srcSuballocIt; + + move.srcBlockIndex = srcOrigBlockIndex; + move.dstBlockIndex = dstOrigBlockIndex; + move.srcOffset = srcAllocOffset; + move.dstOffset = dstAllocOffset; + move.dstHandle = (VmaAllocHandle)dstAllocOffset; + move.size = srcAllocSize; + + moves.push_back(move); + } + } + // Different block + else + { + // MOVE OPTION 2: Move the allocation to a different block. + + VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex); + VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize); + + VmaSuballocation suballoc = *srcSuballocIt; + suballoc.offset = dstAllocOffset; + ((VmaAllocation)(suballoc.userData))->ChangeBlockAllocation(m_hAllocator, pDstBlock, (VmaAllocHandle)dstAllocOffset); + dstOffset = dstAllocOffset + srcAllocSize; + m_BytesMoved += srcAllocSize; + ++m_AllocationsMoved; + + VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt; + ++nextSuballocIt; + pSrcMetadata->m_Suballocations.erase(srcSuballocIt); + srcSuballocIt = nextSuballocIt; + + pDstMetadata->m_Suballocations.push_back(suballoc); + + move.srcBlockIndex = srcOrigBlockIndex; + move.dstBlockIndex = dstOrigBlockIndex; + move.srcOffset = srcAllocOffset; + move.dstOffset = dstAllocOffset; + move.dstHandle = (VmaAllocHandle)dstAllocOffset; + move.size = srcAllocSize; + + moves.push_back(move); + } + } + } + } + + m_BlockInfos.clear(); + + PostprocessMetadata(); + + return VK_SUCCESS; +} + +VmaDefragmentationAlgorithm_Fast::FreeSpaceDatabase::FreeSpaceDatabase() +{ + FreeSpace s = {}; + s.blockInfoIndex = SIZE_MAX; + for (size_t i = 0; i < MAX_COUNT; ++i) + { + m_FreeSpaces[i] = s; + } +} + +void VmaDefragmentationAlgorithm_Fast::FreeSpaceDatabase::Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size) +{ + // Find first invalid or the smallest structure. + size_t bestIndex = SIZE_MAX; + for (size_t i = 0; i < MAX_COUNT; ++i) + { + // Empty structure. + if (m_FreeSpaces[i].blockInfoIndex == SIZE_MAX) + { + bestIndex = i; + break; + } + if (m_FreeSpaces[i].size < size && + (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size)) + { + bestIndex = i; + } + } + + if (bestIndex != SIZE_MAX) + { + m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex; + m_FreeSpaces[bestIndex].offset = offset; + m_FreeSpaces[bestIndex].size = size; + } +} + +bool VmaDefragmentationAlgorithm_Fast::FreeSpaceDatabase::Fetch(VkDeviceSize alignment, VkDeviceSize size, + size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset) +{ + size_t bestIndex = SIZE_MAX; + VkDeviceSize bestFreeSpaceAfter = 0; + for (size_t i = 0; i < MAX_COUNT; ++i) + { + // Structure is valid. + if (m_FreeSpaces[i].blockInfoIndex != SIZE_MAX) + { + const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment); + // Allocation fits into this structure. + if (dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size) + { + const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) - + (dstOffset + size); + if (bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter) + { + bestIndex = i; + bestFreeSpaceAfter = freeSpaceAfter; + } + } + } + } + + if (bestIndex != SIZE_MAX) + { + outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex; + outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment); + + // Leave this structure for remaining empty space. + const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size; + m_FreeSpaces[bestIndex].offset += alignmentPlusSize; + m_FreeSpaces[bestIndex].size -= alignmentPlusSize; + + return true; + } + + return false; +} + +void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata() +{ + const size_t blockCount = m_pBlockVector->GetBlockCount(); + for (size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + VmaBlockMetadata_Generic* const pMetadata = + (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata; + pMetadata->m_FreeCount = 0; + pMetadata->m_SumFreeSize = pMetadata->GetSize(); + pMetadata->m_FreeSuballocationsBySize.clear(); + for (VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin(); + it != pMetadata->m_Suballocations.end(); ) + { + if (it->type == VMA_SUBALLOCATION_TYPE_FREE) + { + VmaSuballocationList::iterator nextIt = it; + ++nextIt; + pMetadata->m_Suballocations.erase(it); + it = nextIt; + } + else + { + ++it; + } + } + } +} + +void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata() +{ + const size_t blockCount = m_pBlockVector->GetBlockCount(); + for (size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + VmaBlockMetadata_Generic* const pMetadata = + (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata; + const VkDeviceSize blockSize = pMetadata->GetSize(); + + // No allocations in this block - entire area is free. + if (pMetadata->m_Suballocations.empty()) + { + pMetadata->m_FreeCount = 1; + //pMetadata->m_SumFreeSize is already set to blockSize. + VmaSuballocation suballoc = { + 0, // offset + blockSize, // size + VMA_NULL, // hAllocation + VMA_SUBALLOCATION_TYPE_FREE }; + pMetadata->m_Suballocations.push_back(suballoc); + pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin()); + } + // There are some allocations in this block. + else + { + VkDeviceSize offset = 0; + VmaSuballocationList::iterator it; + for (it = pMetadata->m_Suballocations.begin(); + it != pMetadata->m_Suballocations.end(); + ++it) + { + VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(it->offset >= offset); + + // Need to insert preceding free space. + if (it->offset > offset) + { + ++pMetadata->m_FreeCount; + const VkDeviceSize freeSize = it->offset - offset; + VmaSuballocation suballoc = { + offset, // offset + freeSize, // size + VMA_NULL, // hAllocation + VMA_SUBALLOCATION_TYPE_FREE }; + VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc); + pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt); + } + + pMetadata->m_SumFreeSize -= it->size; + offset = it->offset + it->size; + } + + // Need to insert trailing free space. + if (offset < blockSize) + { + ++pMetadata->m_FreeCount; + const VkDeviceSize freeSize = blockSize - offset; + VmaSuballocation suballoc = { + offset, // offset + freeSize, // size + VMA_NULL, // hAllocation + VMA_SUBALLOCATION_TYPE_FREE }; + VMA_ASSERT(it == pMetadata->m_Suballocations.end()); + VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc); + pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt); + } + + VMA_SORT( + pMetadata->m_FreeSuballocationsBySize.begin(), + pMetadata->m_FreeSuballocationsBySize.end(), + VmaSuballocationItemSizeLess()); + } + + VMA_HEAVY_ASSERT(pMetadata->Validate()); + } +} + +void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc) +{ + VmaSuballocationList& suballocs = pMetadata->m_Suballocations; + VmaSuballocationList::iterator elementAfter; + const VkDeviceSize last = suballocs.rbegin()->offset; + const VkDeviceSize first = suballocs.begin()->offset; + + if (last <= suballoc.offset) + elementAfter = suballocs.end(); + else if (first >= suballoc.offset) + elementAfter = suballocs.begin(); + else + { + const size_t suballocCount = suballocs.size(); + const VkDeviceSize step = (last - first + suballocs.begin()->size) / suballocCount; + // If offset to be inserted is closer to the end of range, search from the end + if ((suballoc.offset - first) / step > suballocCount / 2) + { + elementAfter = suballocs.begin(); + for (VmaSuballocationList::reverse_iterator suballocItem = ++suballocs.rbegin(); + suballocItem != suballocs.rend(); + ++suballocItem) + { + if (suballocItem->offset <= suballoc.offset) + { + elementAfter = --suballocItem; + break; + } + } + } + else + { + elementAfter = suballocs.end(); + for (VmaSuballocationList::iterator suballocItem = ++suballocs.begin(); + suballocItem != suballocs.end(); + ++suballocItem) + { + if (suballocItem->offset >= suballoc.offset) + { + elementAfter = suballocItem; + break; + } + } + } + } + pMetadata->m_Suballocations.insert(elementAfter, suballoc); +} +#endif // _VMA_DEFRAGMENTATION_ALGORITHM_FAST_FUNCTIONS + +#ifndef _VMA_BLOCK_VECTOR_DEFRAGMENTATION_CONTEXT_FUNCTIONS +VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext( + VmaAllocator hAllocator, + VmaPool hCustomPool, + VmaBlockVector* pBlockVector) + : res(VK_SUCCESS), + mutexLocked(false), + blockContexts(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + defragmentationMoves(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + defragmentationMovesProcessed(0), + defragmentationMovesCommitted(0), + hasDefragmentationPlan(0), + m_hAllocator(hAllocator), + m_hCustomPool(hCustomPool), + m_pBlockVector(pBlockVector), + m_pAlgorithm(VMA_NULL), + m_Allocations(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_AllAllocations(false) {} + +VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext() +{ + vma_delete(m_hAllocator, m_pAlgorithm); +} + +void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) +{ + AllocInfo info = { hAlloc, pChanged }; + m_Allocations.push_back(info); +} + +void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags) +{ + const bool allAllocations = m_AllAllocations || + m_Allocations.size() == m_pBlockVector->CalcAllocationCount(); + + /******************************** + HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM. + ********************************/ + + /* + Fast algorithm is supported only when certain criteria are met: + - VMA_DEBUG_MARGIN is 0. + - All allocations in this block vector are movable. + - There is no possibility of image/buffer granularity conflict. + - The defragmentation is not incremental + */ + if (VMA_DEBUG_MARGIN == 0 && + allAllocations && + !m_pBlockVector->IsBufferImageGranularityConflictPossible() && + !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)) + { + m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)( + m_hAllocator, m_pBlockVector, overlappingMoveSupported); + } + else + { + m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)( + m_hAllocator, m_pBlockVector, overlappingMoveSupported); + } + + if (allAllocations) + { + m_pAlgorithm->AddAll(); + } + else + { + for (size_t i = 0, count = m_Allocations.size(); i < count; ++i) + { + m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged); + } + } +} +#endif // _VMA_BLOCK_VECTOR_DEFRAGMENTATION_CONTEXT_FUNCTIONS + +#ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS +VmaDefragmentationContext_T::VmaDefragmentationContext_T( + VmaAllocator hAllocator, + uint32_t flags, + VmaDefragmentationStats* pStats) + : m_hAllocator(hAllocator), + m_Flags(flags), + m_pStats(pStats), + m_CustomPoolContexts(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) +{ + memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts)); +} + +VmaDefragmentationContext_T::~VmaDefragmentationContext_T() +{ + for (size_t i = m_CustomPoolContexts.size(); i--; ) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i]; + pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats); + vma_delete(m_hAllocator, pBlockVectorCtx); + } + for (size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; ) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i]; + if (pBlockVectorCtx) + { + pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats); + vma_delete(m_hAllocator, pBlockVectorCtx); + } + } +} + +void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools) +{ + for (uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex) + { + VmaPool pool = pPools[poolIndex]; + VMA_ASSERT(pool); + // Pools with algorithm other than default are not defragmented. + if (pool->m_BlockVector.GetAlgorithm() == 0) + { + VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL; + + for (size_t i = m_CustomPoolContexts.size(); i--; ) + { + if (m_CustomPoolContexts[i]->GetCustomPool() == pool) + { + pBlockVectorDefragCtx = m_CustomPoolContexts[i]; + break; + } + } + + if (!pBlockVectorDefragCtx) + { + pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( + m_hAllocator, + pool, + &pool->m_BlockVector); + m_CustomPoolContexts.push_back(pBlockVectorDefragCtx); + } + + pBlockVectorDefragCtx->AddAll(); + } + } +} + +void VmaDefragmentationContext_T::AddAllocations( + uint32_t allocationCount, + const VmaAllocation* pAllocations, + VkBool32* pAllocationsChanged) +{ + // Dispatch pAllocations among defragmentators. Create them when necessary. + for (uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + const VmaAllocation hAlloc = pAllocations[allocIndex]; + VMA_ASSERT(hAlloc); + // DedicatedAlloc cannot be defragmented. + if (hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) + { + VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL; + + const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool(); + // This allocation belongs to custom pool. + if (hAllocPool != VK_NULL_HANDLE) + { + // Pools with algorithm other than default are not defragmented. + if (hAllocPool->m_BlockVector.GetAlgorithm() == 0) + { + for (size_t i = m_CustomPoolContexts.size(); i--; ) + { + if (m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool) + { + pBlockVectorDefragCtx = m_CustomPoolContexts[i]; + break; + } + } + if (!pBlockVectorDefragCtx) + { + pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( + m_hAllocator, + hAllocPool, + &hAllocPool->m_BlockVector); + m_CustomPoolContexts.push_back(pBlockVectorDefragCtx); + } + } + } + // This allocation belongs to default pool. + else + { + const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex(); + pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex]; + if (!pBlockVectorDefragCtx) + { + VMA_ASSERT(m_hAllocator->m_pBlockVectors[memTypeIndex] && "Trying to use unsupported memory type!"); + + pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)( + m_hAllocator, + VMA_NULL, // hCustomPool + m_hAllocator->m_pBlockVectors[memTypeIndex]); + m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx; + } + } + + if (pBlockVectorDefragCtx) + { + VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ? + &pAllocationsChanged[allocIndex] : VMA_NULL; + pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged); + } + } + } +} + +VkResult VmaDefragmentationContext_T::Defragment( + VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove, + VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove, + VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags) +{ + if (pStats) + { + memset(pStats, 0, sizeof(VmaDefragmentationStats)); + } + + if (flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL) + { + // For incremental defragmetnations, we just earmark how much we can move + // The real meat is in the defragmentation steps + m_MaxCpuBytesToMove = maxCpuBytesToMove; + m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove; + + m_MaxGpuBytesToMove = maxGpuBytesToMove; + m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove; + + if (m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 && + m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0) + return VK_SUCCESS; + + return VK_NOT_READY; + } + + if (commandBuffer == VK_NULL_HANDLE) + { + maxGpuBytesToMove = 0; + maxGpuAllocationsToMove = 0; + } + + VkResult res = VK_SUCCESS; + + // Process default pools. + for (uint32_t memTypeIndex = 0; + memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS; + ++memTypeIndex) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex]; + if (pBlockVectorCtx) + { + VMA_ASSERT(pBlockVectorCtx->GetBlockVector()); + pBlockVectorCtx->GetBlockVector()->Defragment( + pBlockVectorCtx, + pStats, flags, + maxCpuBytesToMove, maxCpuAllocationsToMove, + maxGpuBytesToMove, maxGpuAllocationsToMove, + commandBuffer); + if (pBlockVectorCtx->res != VK_SUCCESS) + { + res = pBlockVectorCtx->res; + } + } + } + + // Process custom pools. + for (size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size(); + customCtxIndex < customCtxCount && res >= VK_SUCCESS; + ++customCtxIndex) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex]; + VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector()); + pBlockVectorCtx->GetBlockVector()->Defragment( + pBlockVectorCtx, + pStats, flags, + maxCpuBytesToMove, maxCpuAllocationsToMove, + maxGpuBytesToMove, maxGpuAllocationsToMove, + commandBuffer); + if (pBlockVectorCtx->res != VK_SUCCESS) + { + res = pBlockVectorCtx->res; + } + } + + return res; +} + +VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo) +{ + VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves; + uint32_t movesLeft = pInfo->moveCount; + + // Process default pools. + for (uint32_t memTypeIndex = 0; + memTypeIndex < m_hAllocator->GetMemoryTypeCount(); + ++memTypeIndex) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex]; + if (pBlockVectorCtx) + { + VMA_ASSERT(pBlockVectorCtx->GetBlockVector()); + + if (!pBlockVectorCtx->hasDefragmentationPlan) + { + pBlockVectorCtx->GetBlockVector()->Defragment( + pBlockVectorCtx, + m_pStats, m_Flags, + m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove, + m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove, + VK_NULL_HANDLE); + + if (pBlockVectorCtx->res < VK_SUCCESS) + continue; + + pBlockVectorCtx->hasDefragmentationPlan = true; + } + + const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations( + pBlockVectorCtx, + pCurrentMove, movesLeft); + + movesLeft -= processed; + pCurrentMove += processed; + } + } + + // Process custom pools. + for (size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size(); + customCtxIndex < customCtxCount; + ++customCtxIndex) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex]; + VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector()); + + if (!pBlockVectorCtx->hasDefragmentationPlan) + { + pBlockVectorCtx->GetBlockVector()->Defragment( + pBlockVectorCtx, + m_pStats, m_Flags, + m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove, + m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove, + VK_NULL_HANDLE); + + if (pBlockVectorCtx->res < VK_SUCCESS) + continue; + + pBlockVectorCtx->hasDefragmentationPlan = true; + } + + const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations( + pBlockVectorCtx, + pCurrentMove, movesLeft); + + movesLeft -= processed; + pCurrentMove += processed; + } + + pInfo->moveCount = pInfo->moveCount - movesLeft; + + return VK_SUCCESS; +} + +VkResult VmaDefragmentationContext_T::DefragmentPassEnd() +{ + VkResult res = VK_SUCCESS; + + // Process default pools. + for (uint32_t memTypeIndex = 0; + memTypeIndex < m_hAllocator->GetMemoryTypeCount(); + ++memTypeIndex) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex]; + if (pBlockVectorCtx) + { + VMA_ASSERT(pBlockVectorCtx->GetBlockVector()); + + if (!pBlockVectorCtx->hasDefragmentationPlan) + { + res = VK_NOT_READY; + continue; + } + + pBlockVectorCtx->GetBlockVector()->CommitDefragmentations( + pBlockVectorCtx, m_pStats); + + if (pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted) + res = VK_NOT_READY; + } + } + + // Process custom pools. + for (size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size(); + customCtxIndex < customCtxCount; + ++customCtxIndex) + { + VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex]; + VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector()); + + if (!pBlockVectorCtx->hasDefragmentationPlan) + { + res = VK_NOT_READY; + continue; + } + + pBlockVectorCtx->GetBlockVector()->CommitDefragmentations( + pBlockVectorCtx, m_pStats); + + if (pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted) + res = VK_NOT_READY; + } + + return res; +} +#endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS + +#ifndef _VMA_POOL_T_FUNCTIONS +VmaPool_T::VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize) + : m_BlockVector( + hAllocator, + this, // hParentPool + createInfo.memoryTypeIndex, + createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize, + createInfo.minBlockCount, + createInfo.maxBlockCount, + (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(), + createInfo.blockSize != 0, // explicitBlockSize + createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm + createInfo.priority, + VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment), + createInfo.pMemoryAllocateNext), + m_Id(0), + m_Name(VMA_NULL) {} + +VmaPool_T::~VmaPool_T() +{ + VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL); +} + +void VmaPool_T::SetName(const char* pName) +{ + const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks(); + VmaFreeString(allocs, m_Name); + + if (pName != VMA_NULL) + { + m_Name = VmaCreateStringCopy(allocs, pName); + } + else + { + m_Name = VMA_NULL; + } +} +#endif // _VMA_POOL_T_FUNCTIONS + +#ifndef _VMA_ALLOCATOR_T_FUNCTIONS VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0), + m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0), m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0), - m_PhysicalDevice(pCreateInfo->physicalDevice), + m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0), + m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0), + m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0), + m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0), + m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0), m_hDevice(pCreateInfo->device), + m_hInstance(pCreateInfo->instance), m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ? *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks), + m_AllocationObjectAllocator(&m_AllocationCallbacks), + m_HeapSizeLimitMask(0), + m_DeviceMemoryCount(0), m_PreferredLargeHeapBlockSize(0), - m_PreferredSmallHeapBlockSize(0), - m_CurrentFrameIndex(0), - m_Pools(VmaStlAllocator(GetAllocationCallbacks())) + m_PhysicalDevice(pCreateInfo->physicalDevice), + m_GpuDefragmentationMemoryTypeBits(UINT32_MAX), + m_NextPoolId(0), + m_GlobalMemoryTypeBits(UINT32_MAX) { - VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device); + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_UseKhrDedicatedAllocation = false; + m_UseKhrBindMemory2 = false; + } + + if(VMA_DEBUG_DETECT_CORRUPTION) + { + // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it. + VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0); + } + + VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance); + + if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0)) + { +#if !(VMA_DEDICATED_ALLOCATION) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros."); + } +#endif +#if !(VMA_BIND_MEMORY2) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros."); + } +#endif + } +#if !(VMA_MEMORY_BUDGET) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros."); + } +#endif +#if !(VMA_BUFFER_DEVICE_ADDRESS) + if(m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif +#if VMA_VULKAN_VERSION < 1002000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if VMA_VULKAN_VERSION < 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if !(VMA_MEMORY_PRIORITY) + if(m_UseExtMemoryPriority) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks)); - memset(&m_MemProps, 0, sizeof(m_MemProps)); memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties)); - - memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); - memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations)); + memset(&m_MemProps, 0, sizeof(m_MemProps)); - for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) - { - m_HeapSizeLimit[i] = VK_WHOLE_SIZE; - } + memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); + memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions)); + +#if VMA_EXTERNAL_MEMORY + memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes)); +#endif // #if VMA_EXTERNAL_MEMORY if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) { + m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData; m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate; m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree; } @@ -6733,10 +14434,23 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties); (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps); + VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT)); + VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize)); + m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ? pCreateInfo->preferredLargeHeapBlockSize : static_cast(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); - m_PreferredSmallHeapBlockSize = (pCreateInfo->preferredSmallHeapBlockSize != 0) ? - pCreateInfo->preferredSmallHeapBlockSize : static_cast(VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE); + + m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits(); + +#if VMA_EXTERNAL_MEMORY + if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL) + { + memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes, + sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount()); + } +#endif // #if VMA_EXTERNAL_MEMORY if(pCreateInfo->pHeapSizeLimit != VMA_NULL) { @@ -6745,7 +14459,7 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex]; if(limit != VK_WHOLE_SIZE) { - m_HeapSizeLimit[heapIndex] = limit; + m_HeapSizeLimitMask |= 1u << heapIndex; if(limit < m_MemProps.memoryHeaps[heapIndex].size) { m_MemProps.memoryHeaps[heapIndex].size = limit; @@ -6756,93 +14470,241 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex); - - m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)( - this, - memTypeIndex, - preferredBlockSize, - 0, - SIZE_MAX, - GetBufferImageGranularity(), - pCreateInfo->frameInUseCount, - false); // isCustomPool - // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here, - // becase minBlockCount is 0. - m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator(GetAllocationCallbacks())); + // Create only supported types + if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0) + { + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex); + m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)( + this, + VK_NULL_HANDLE, // hParentPool + memTypeIndex, + preferredBlockSize, + 0, + SIZE_MAX, + GetBufferImageGranularity(), + false, // explicitBlockSize + 0, // algorithm + 0.5f, // priority (0.5 is the default per Vulkan spec) + GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment + VMA_NULL); // // pMemoryAllocateNext + // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here, + // becase minBlockCount is 0. + } } } +VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) +{ + VkResult res = VK_SUCCESS; + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + UpdateVulkanBudget(); + } +#endif // #if VMA_MEMORY_BUDGET + + return res; +} + VmaAllocator_T::~VmaAllocator_T() { - VMA_ASSERT(m_Pools.empty()); + VMA_ASSERT(m_Pools.IsEmpty()); - for(size_t i = GetMemoryTypeCount(); i--; ) + for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; ) { - vma_delete(this, m_pDedicatedAllocations[i]); - vma_delete(this, m_pBlockVectors[i]); + vma_delete(this, m_pBlockVectors[memTypeIndex]); } } void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions) { #if VMA_STATIC_VULKAN_FUNCTIONS == 1 - m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties; - m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties; - m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory; - m_VulkanFunctions.vkFreeMemory = &vkFreeMemory; - m_VulkanFunctions.vkMapMemory = &vkMapMemory; - m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory; - m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory; - m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory; - m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements; - m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements; - m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer; - m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer; - m_VulkanFunctions.vkCreateImage = &vkCreateImage; - m_VulkanFunctions.vkDestroyImage = &vkDestroyImage; - if(m_UseKhrDedicatedAllocation) + ImportVulkanFunctions_Static(); +#endif + + if(pVulkanFunctions != VMA_NULL) { - m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = - (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR"); - m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = - (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR"); + ImportVulkanFunctions_Custom(pVulkanFunctions); } -#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1 + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + ImportVulkanFunctions_Dynamic(); +#endif + + ValidateVulkanFunctions(); +} + +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Static() +{ + // Vulkan 1.0 + m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr; + m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr; + m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties; + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties; + m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory; + m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory; + m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory; + m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory; + m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges; + m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges; + m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory; + m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory; + m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements; + m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements; + m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer; + m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer; + m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage; + m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage; + m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer; + + // Vulkan 1.1 +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2; + m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2; + m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2; + m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2; + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2; + } +#endif +} + +#endif // VMA_STATIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions) +{ + VMA_ASSERT(pVulkanFunctions != VMA_NULL); #define VMA_COPY_IF_NOT_NULL(funcName) \ if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName; - if(pVulkanFunctions != VMA_NULL) - { - VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties); - VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties); - VMA_COPY_IF_NOT_NULL(vkAllocateMemory); - VMA_COPY_IF_NOT_NULL(vkFreeMemory); - VMA_COPY_IF_NOT_NULL(vkMapMemory); - VMA_COPY_IF_NOT_NULL(vkUnmapMemory); - VMA_COPY_IF_NOT_NULL(vkBindBufferMemory); - VMA_COPY_IF_NOT_NULL(vkBindImageMemory); - VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements); - VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements); - VMA_COPY_IF_NOT_NULL(vkCreateBuffer); - VMA_COPY_IF_NOT_NULL(vkDestroyBuffer); - VMA_COPY_IF_NOT_NULL(vkCreateImage); - VMA_COPY_IF_NOT_NULL(vkDestroyImage); - VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR); - VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR); - } + VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr); + VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties); + VMA_COPY_IF_NOT_NULL(vkAllocateMemory); + VMA_COPY_IF_NOT_NULL(vkFreeMemory); + VMA_COPY_IF_NOT_NULL(vkMapMemory); + VMA_COPY_IF_NOT_NULL(vkUnmapMemory); + VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory); + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkCreateBuffer); + VMA_COPY_IF_NOT_NULL(vkDestroyBuffer); + VMA_COPY_IF_NOT_NULL(vkCreateImage); + VMA_COPY_IF_NOT_NULL(vkDestroyImage); + VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer); + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR); +#endif + +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR); +#endif + +#if VMA_MEMORY_BUDGET + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR); +#endif #undef VMA_COPY_IF_NOT_NULL +} - // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1 - // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions. +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Dynamic() +{ + VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr && + "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass " + "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. " + "Other members can be null."); + +#define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \ + if(m_VulkanFunctions.memberName == VMA_NULL) \ + m_VulkanFunctions.memberName = \ + (functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString); +#define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \ + if(m_VulkanFunctions.memberName == VMA_NULL) \ + m_VulkanFunctions.memberName = \ + (functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString); + + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties"); + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties"); + VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory"); + VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory"); + VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory"); + VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory"); + VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges"); + VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges"); + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory"); + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer"); + VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer"); + VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage"); + VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage"); + VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer"); + +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2"); + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2"); + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2"); + } +#endif + +#if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR"); + } +#endif + +#if VMA_BIND_MEMORY2 + if(m_UseKhrBindMemory2) + { + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR"); + } +#endif // #if VMA_BIND_MEMORY2 + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR"); + } +#endif // #if VMA_MEMORY_BUDGET + +#undef VMA_FETCH_DEVICE_FUNC +#undef VMA_FETCH_INSTANCE_FUNC +} + +#endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ValidateVulkanFunctions() +{ VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL); @@ -6851,161 +14713,336 @@ void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunc VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL); - if(m_UseKhrDedicatedAllocation) + VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL); + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation) { VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL); VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL); } +#endif + +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2) + { + VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL); + } +#endif + +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL); + } +#endif } VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex) { const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; - return (heapSize <= VMA_SMALL_HEAP_MAX_SIZE) ? - m_PreferredSmallHeapBlockSize : m_PreferredLargeHeapBlockSize; + const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE; + return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32); } VkResult VmaAllocator_T::AllocateMemoryOfType( - const VkMemoryRequirements& vkMemReq, - bool dedicatedAllocation, + VmaPool pool, + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedPreferred, VkBuffer dedicatedBuffer, + VkBufferUsageFlags dedicatedBufferUsage, VkImage dedicatedImage, const VmaAllocationCreateInfo& createInfo, uint32_t memTypeIndex, VmaSuballocationType suballocType, - VmaAllocation* pAllocation) + VmaDedicatedAllocationList& dedicatedAllocations, + VmaBlockVector& blockVector, + size_t allocationCount, + VmaAllocation* pAllocations) { - VMA_ASSERT(pAllocation != VMA_NULL); - VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, Size=%llu", memTypeIndex, vkMemReq.size); + VMA_ASSERT(pAllocations != VMA_NULL); + VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size); VmaAllocationCreateInfo finalCreateInfo = createInfo; - - // If memory type is not HOST_VISIBLE, disable MAPPED. - if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && - (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) - { - finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; - } - - VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex]; - VMA_ASSERT(blockVector); - - const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize(); - bool preferDedicatedMemory = - VMA_DEBUG_ALWAYS_DEDICATED_MEMORY || - dedicatedAllocation || - // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size. - vkMemReq.size > preferredBlockSize / 2; - - if(preferDedicatedMemory && - (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 && - finalCreateInfo.pool == VK_NULL_HANDLE) - { - finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; - } + VkResult res = CalcMemTypeParams( + finalCreateInfo, + memTypeIndex, + size, + allocationCount); + if(res != VK_SUCCESS) + return res; if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) { - if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) - { - return VK_ERROR_OUT_OF_DEVICE_MEMORY; - } - else - { - return AllocateDedicatedMemory( - vkMemReq.size, - suballocType, - memTypeIndex, - (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, - (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, - finalCreateInfo.pUserData, - dedicatedBuffer, - dedicatedImage, - pAllocation); - } + return AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedBufferUsage, + dedicatedImage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); } else { - VkResult res = blockVector->Allocate( - VK_NULL_HANDLE, // hCurrentPool - m_CurrentFrameIndex.load(), - vkMemReq, - finalCreateInfo, - suballocType, - pAllocation); - if(res == VK_SUCCESS) + const bool canAllocateDedicated = + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 && + (pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize()); + + if(canAllocateDedicated) { - return res; + // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size. + if(size > blockVector.GetPreferredBlockSize() / 2) + { + dedicatedPreferred = true; + } + // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget, + // which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above + // 3/4 of the maximum allocation count. + if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4) + { + dedicatedPreferred = false; + } + + if(dedicatedPreferred) + { + res = AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedBufferUsage, + dedicatedImage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } + } } - // 5. Try dedicated memory. - if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) - { - return VK_ERROR_OUT_OF_DEVICE_MEMORY; - } - else + res = blockVector.Allocate( + size, + alignment, + finalCreateInfo, + suballocType, + allocationCount, + pAllocations); + if(res == VK_SUCCESS) + return VK_SUCCESS; + + // Try dedicated memory. + if(canAllocateDedicated && !dedicatedPreferred) { res = AllocateDedicatedMemory( - vkMemReq.size, + pool, + size, suballocType, + dedicatedAllocations, memTypeIndex, (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, finalCreateInfo.pUserData, + finalCreateInfo.priority, dedicatedBuffer, + dedicatedBufferUsage, dedicatedImage, - pAllocation); + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); if(res == VK_SUCCESS) { // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here. VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); return VK_SUCCESS; } - else - { - // Everything failed: Return error code. - VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); - return res; - } } + // Everything failed: Return error code. + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; } } VkResult VmaAllocator_T::AllocateDedicatedMemory( + VmaPool pool, VkDeviceSize size, VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, uint32_t memTypeIndex, bool map, bool isUserDataString, + bool canAliasMemory, void* pUserData, + float priority, VkBuffer dedicatedBuffer, + VkBufferUsageFlags dedicatedBufferUsage, VkImage dedicatedImage, - VmaAllocation* pAllocation) + size_t allocationCount, + VmaAllocation* pAllocations, + const void* pNextChain) { - VMA_ASSERT(pAllocation); + VMA_ASSERT(allocationCount > 0 && pAllocations); VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; allocInfo.memoryTypeIndex = memTypeIndex; allocInfo.allocationSize = size; + allocInfo.pNext = pNextChain; +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR }; - if(m_UseKhrDedicatedAllocation) + if(!canAliasMemory) { + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + if(dedicatedBuffer != VK_NULL_HANDLE) + { + VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE); + dedicatedAllocInfo.buffer = dedicatedBuffer; + VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo); + } + else if(dedicatedImage != VK_NULL_HANDLE) + { + dedicatedAllocInfo.image = dedicatedImage; + VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo); + } + } + } +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + +#if VMA_BUFFER_DEVICE_ADDRESS + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR }; + if(m_UseKhrBufferDeviceAddress) + { + bool canContainBufferWithDeviceAddress = true; if(dedicatedBuffer != VK_NULL_HANDLE) { - VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE); - dedicatedAllocInfo.buffer = dedicatedBuffer; - allocInfo.pNext = &dedicatedAllocInfo; + canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown + (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0; } else if(dedicatedImage != VK_NULL_HANDLE) { - dedicatedAllocInfo.image = dedicatedImage; - allocInfo.pNext = &dedicatedAllocInfo; + canContainBufferWithDeviceAddress = false; + } + if(canContainBufferWithDeviceAddress) + { + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo); + } + } +#endif // #if VMA_BUFFER_DEVICE_ADDRESS + +#if VMA_MEMORY_PRIORITY + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + if(m_UseExtMemoryPriority) + { + priorityInfo.priority = priority; + VmaPnextChainPushFront(&allocInfo, &priorityInfo); + } +#endif // #if VMA_MEMORY_PRIORITY + +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex); + if(exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // #if VMA_EXTERNAL_MEMORY + + size_t allocIndex; + VkResult res = VK_SUCCESS; + for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocateDedicatedMemoryPage( + pool, + size, + suballocType, + memTypeIndex, + allocInfo, + map, + isUserDataString, + pUserData, + pAllocations + allocIndex); + if(res != VK_SUCCESS) + { + break; } } - // Allocate VkDeviceMemory. + if(res == VK_SUCCESS) + { + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + dedicatedAllocations.Register(pAllocations[allocIndex]); + } + VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex); + } + else + { + // Free all already created allocations. + while(allocIndex--) + { + VmaAllocation currAlloc = pAllocations[allocIndex]; + VkDeviceMemory hMemory = currAlloc->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(currAlloc->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory); + m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize()); + currAlloc->SetUserData(this, VMA_NULL); + m_AllocationObjectAllocator.Free(currAlloc); + } + + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaAllocator_T::AllocateDedicatedMemoryPage( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + void* pUserData, + VmaAllocation* pAllocation) +{ VkDeviceMemory hMemory = VK_NULL_HANDLE; VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory); if(res < 0) @@ -7014,7 +15051,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory( return res; } - void* pMappedData = nullptr; + void* pMappedData = VMA_NULL; if(map) { res = (*m_VulkanFunctions.vkMapMemory)( @@ -7032,20 +15069,15 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory( } } - *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString); - (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size); + *pAllocation = m_AllocationObjectAllocator.Allocate(isUserDataString); + (*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size); (*pAllocation)->SetUserData(this, pUserData); - - // Register it in m_pDedicatedAllocations. + m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size); + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) { - VmaMutexLock lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); - AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; - VMA_ASSERT(pDedicatedAllocations); - VmaVectorInsertSorted(*pDedicatedAllocations, *pAllocation); + FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); } - VMA_DEBUG_LOG(" Allocated DedicatedMemory MemoryTypeIndex=#%u", memTypeIndex); - return VK_SUCCESS; } @@ -7055,7 +15087,8 @@ void VmaAllocator_T::GetBufferMemoryRequirements( bool& requiresDedicatedAllocation, bool& prefersDedicatedAllocation) const { - if(m_UseKhrDedicatedAllocation) +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) { VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR }; memReqInfo.buffer = hBuffer; @@ -7063,7 +15096,7 @@ void VmaAllocator_T::GetBufferMemoryRequirements( VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; - memReq2.pNext = &memDedicatedReq; + VmaPnextChainPushFront(&memReq2, &memDedicatedReq); (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); @@ -7072,6 +15105,7 @@ void VmaAllocator_T::GetBufferMemoryRequirements( prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); } else +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 { (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq); requiresDedicatedAllocation = false; @@ -7085,7 +15119,8 @@ void VmaAllocator_T::GetImageMemoryRequirements( bool& requiresDedicatedAllocation, bool& prefersDedicatedAllocation) const { - if(m_UseKhrDedicatedAllocation) +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) { VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR }; memReqInfo.image = hImage; @@ -7093,7 +15128,7 @@ void VmaAllocator_T::GetImageMemoryRequirements( VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; - memReq2.pNext = &memDedicatedReq; + VmaPnextChainPushFront(&memReq2, &memDedicatedReq); (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); @@ -7102,6 +15137,7 @@ void VmaAllocator_T::GetImageMemoryRequirements( prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); } else +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 { (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq); requiresDedicatedAllocation = false; @@ -7109,183 +15145,237 @@ void VmaAllocator_T::GetImageMemoryRequirements( } } +VkResult VmaAllocator_T::CalcMemTypeParams( + VmaAllocationCreateInfo& inoutCreateInfo, + uint32_t memTypeIndex, + VkDeviceSize size, + size_t allocationCount) +{ + // If memory type is not HOST_VISIBLE, disable MAPPED. + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && + (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0) + { + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + VmaBudget heapBudget = {}; + GetHeapBudgets(&heapBudget, heapIndex, 1); + if(heapBudget.usage + size * allocationCount > heapBudget.budget) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::CalcAllocationParams( + VmaAllocationCreateInfo& inoutCreateInfo, + bool dedicatedRequired, + bool dedicatedPreferred) +{ + if(dedicatedRequired || + // If memory is lazily allocated, it should be always dedicated. + inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + if(inoutCreateInfo.pool != VK_NULL_HANDLE) + { + if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations."); + return VK_ERROR_FEATURE_NOT_PRESENT; + } + inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority(); + } + + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense."); + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + return VK_SUCCESS; +} + VkResult VmaAllocator_T::AllocateMemory( const VkMemoryRequirements& vkMemReq, bool requiresDedicatedAllocation, bool prefersDedicatedAllocation, VkBuffer dedicatedBuffer, + VkBufferUsageFlags dedicatedBufferUsage, VkImage dedicatedImage, const VmaAllocationCreateInfo& createInfo, VmaSuballocationType suballocType, - VmaAllocation* pAllocation) + size_t allocationCount, + VmaAllocation* pAllocations) { - if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && - (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + + VMA_ASSERT(VmaIsPow2(vkMemReq.alignment)); + + if(vkMemReq.size == 0) { - VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense."); - return VK_ERROR_OUT_OF_DEVICE_MEMORY; - } - if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && - (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0) - { - VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid."); - return VK_ERROR_OUT_OF_DEVICE_MEMORY; - } - if(requiresDedicatedAllocation) - { - if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) - { - VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required."); - return VK_ERROR_OUT_OF_DEVICE_MEMORY; - } - if(createInfo.pool != VK_NULL_HANDLE) - { - VMA_ASSERT(0 && "Pool specified while dedicated allocation is required."); - return VK_ERROR_OUT_OF_DEVICE_MEMORY; - } - } - if((createInfo.pool != VK_NULL_HANDLE) && - ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0)) - { - VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid."); - return VK_ERROR_OUT_OF_DEVICE_MEMORY; + return VK_ERROR_INITIALIZATION_FAILED; } - if(createInfo.pool != VK_NULL_HANDLE) + VmaAllocationCreateInfo createInfoFinal = createInfo; + VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation); + if(res != VK_SUCCESS) + return res; + + if(createInfoFinal.pool != VK_NULL_HANDLE) { - return createInfo.pool->m_BlockVector.Allocate( - createInfo.pool, - m_CurrentFrameIndex.load(), - vkMemReq, - createInfo, + VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector; + return AllocateMemoryOfType( + createInfoFinal.pool, + vkMemReq.size, + vkMemReq.alignment, + prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedBufferUsage, + dedicatedImage, + createInfoFinal, + blockVector.GetMemoryTypeIndex(), suballocType, - pAllocation); + createInfoFinal.pool->m_DedicatedAllocations, + blockVector, + allocationCount, + pAllocations); } else { // Bit mask of memory Vulkan types acceptable for this allocation. uint32_t memoryTypeBits = vkMemReq.memoryTypeBits; uint32_t memTypeIndex = UINT32_MAX; - VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex); - if(res == VK_SUCCESS) + res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex); + // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT. + if(res != VK_SUCCESS) + return res; + do { + VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(blockVector && "Trying to use unsupported memory type!"); res = AllocateMemoryOfType( - vkMemReq, + VK_NULL_HANDLE, + vkMemReq.size, + vkMemReq.alignment, requiresDedicatedAllocation || prefersDedicatedAllocation, dedicatedBuffer, + dedicatedBufferUsage, dedicatedImage, - createInfo, + createInfoFinal, memTypeIndex, suballocType, - pAllocation); - // Succeeded on first try. + m_DedicatedAllocations[memTypeIndex], + *blockVector, + allocationCount, + pAllocations); + // Allocation succeeded if(res == VK_SUCCESS) - { - return res; - } - // Allocation from this memory type failed. Try other compatible memory types. - else - { - for(;;) - { - // Remove old memTypeIndex from list of possibilities. - memoryTypeBits &= ~(1u << memTypeIndex); - // Find alternative memTypeIndex. - res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex); - if(res == VK_SUCCESS) - { - res = AllocateMemoryOfType( - vkMemReq, - requiresDedicatedAllocation || prefersDedicatedAllocation, - dedicatedBuffer, - dedicatedImage, - createInfo, - memTypeIndex, - suballocType, - pAllocation); - // Allocation from this alternative memory type succeeded. - if(res == VK_SUCCESS) - { - return res; - } - // else: Allocation from this memory type failed. Try next one - next loop iteration. - } - // No other matching memory type index could be found. - else - { - // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once. - return VK_ERROR_OUT_OF_DEVICE_MEMORY; - } - } - } - } - // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT. - else - return res; + return VK_SUCCESS; + + // Remove old memTypeIndex from list of possibilities. + memoryTypeBits &= ~(1u << memTypeIndex); + // Find alternative memTypeIndex. + res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex); + } while(res == VK_SUCCESS); + + // No other matching memory type index could be found. + // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; } } -void VmaAllocator_T::FreeMemory(const VmaAllocation allocation) +void VmaAllocator_T::FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations) { - VMA_ASSERT(allocation); + VMA_ASSERT(pAllocations); - if(allocation->CanBecomeLost() == false || - allocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST) + for(size_t allocIndex = allocationCount; allocIndex--; ) { - switch(allocation->GetType()) + VmaAllocation allocation = pAllocations[allocIndex]; + + if(allocation != VK_NULL_HANDLE) { - case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) { - VmaBlockVector* pBlockVector = VMA_NULL; - VmaPool hPool = allocation->GetPool(); - if(hPool != VK_NULL_HANDLE) - { - pBlockVector = &hPool->m_BlockVector; - } - else - { - const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); - pBlockVector = m_pBlockVectors[memTypeIndex]; - } - pBlockVector->Free(allocation); + FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED); } - break; - case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: - FreeDedicatedMemory(allocation); - break; - default: - VMA_ASSERT(0); + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaBlockVector* pBlockVector = VMA_NULL; + VmaPool hPool = allocation->GetParentPool(); + if(hPool != VK_NULL_HANDLE) + { + pBlockVector = &hPool->m_BlockVector; + } + else + { + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + pBlockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!"); + } + pBlockVector->Free(allocation); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + FreeDedicatedMemory(allocation); + break; + default: + VMA_ASSERT(0); + } + + m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize()); + allocation->SetUserData(this, VMA_NULL); + m_AllocationObjectAllocator.Free(allocation); } } - - allocation->SetUserData(this, VMA_NULL); - vma_delete(this, allocation); } void VmaAllocator_T::CalculateStats(VmaStats* pStats) { // Initialize. - InitStatInfo(pStats->total); + VmaInitStatInfo(pStats->total); for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) - InitStatInfo(pStats->memoryType[i]); + VmaInitStatInfo(pStats->memoryType[i]); for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) - InitStatInfo(pStats->memoryHeap[i]); - + VmaInitStatInfo(pStats->memoryHeap[i]); + // Process default pools. for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; - VMA_ASSERT(pBlockVector); - pBlockVector->AddStats(pStats); + if (pBlockVector != VMA_NULL) + pBlockVector->AddStats(pStats); } // Process custom pools. { - VmaMutexLock lock(m_PoolsMutex, m_UseMutex); - for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex) + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) { - m_Pools[poolIndex]->GetBlockVector().AddStats(pStats); + VmaBlockVector& blockVector = pool->m_BlockVector; + blockVector.AddStats(pStats); + const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex(); + const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + pool->m_DedicatedAllocations.AddStats(pStats, memTypeIndex, memHeapIndex); } } @@ -7293,17 +15383,7 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats) for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); - VmaMutexLock dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); - AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex]; - VMA_ASSERT(pDedicatedAllocVector); - for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex) - { - VmaStatInfo allocationStatInfo; - (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo); - VmaAddStatInfo(pStats->total, allocationStatInfo); - VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo); - VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo); - } + m_DedicatedAllocations[memTypeIndex].AddStats(pStats, memTypeIndex, memHeapIndex); } // Postprocess. @@ -7314,189 +15394,153 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats) VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]); } -static const uint32_t VMA_VENDOR_ID_AMD = 4098; - -VkResult VmaAllocator_T::Defragment( - VmaAllocation* pAllocations, - size_t allocationCount, - VkBool32* pAllocationsChanged, - const VmaDefragmentationInfo* pDefragmentationInfo, - VmaDefragmentationStats* pDefragmentationStats) +void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount) { - if(pAllocationsChanged != VMA_NULL) +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) { - memset(pAllocationsChanged, 0, sizeof(*pAllocationsChanged)); - } - if(pDefragmentationStats != VMA_NULL) - { - memset(pDefragmentationStats, 0, sizeof(*pDefragmentationStats)); - } - - const uint32_t currentFrameIndex = m_CurrentFrameIndex.load(); - - VmaMutexLock poolsLock(m_PoolsMutex, m_UseMutex); - - const size_t poolCount = m_Pools.size(); - - // Dispatch pAllocations among defragmentators. Create them in BlockVectors when necessary. - for(size_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) - { - VmaAllocation hAlloc = pAllocations[allocIndex]; - VMA_ASSERT(hAlloc); - const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex(); - // DedicatedAlloc cannot be defragmented. - if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) && - // Only HOST_VISIBLE memory types can be defragmented. - ((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) && - // Lost allocation cannot be defragmented. - (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)) + if(m_Budget.m_OperationsSinceBudgetFetch < 30) { - VmaBlockVector* pAllocBlockVector = nullptr; - - const VmaPool hAllocPool = hAlloc->GetPool(); - // This allocation belongs to custom pool. - if(hAllocPool != VK_NULL_HANDLE) + VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex); + for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets) { - pAllocBlockVector = &hAllocPool->GetBlockVector(); + const uint32_t heapIndex = firstHeap + i; + + outBudgets->blockBytes = m_Budget.m_BlockBytes[heapIndex]; + outBudgets->allocationBytes = m_Budget.m_AllocationBytes[heapIndex]; + + if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]) + { + outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] + + outBudgets->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]; + } + else + { + outBudgets->usage = 0; + } + + // Have to take MIN with heap size because explicit HeapSizeLimit is included in it. + outBudgets->budget = VMA_MIN( + m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size); } - // This allocation belongs to general pool. - else - { - pAllocBlockVector = m_pBlockVectors[memTypeIndex]; - } - - VmaDefragmentator* const pDefragmentator = pAllocBlockVector->EnsureDefragmentator(this, currentFrameIndex); - - VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ? - &pAllocationsChanged[allocIndex] : VMA_NULL; - pDefragmentator->AddAllocation(hAlloc, pChanged); } - } - - VkResult result = VK_SUCCESS; - - // ======== Main processing. - - VkDeviceSize maxBytesToMove = SIZE_MAX; - uint32_t maxAllocationsToMove = UINT32_MAX; - if(pDefragmentationInfo != VMA_NULL) - { - maxBytesToMove = pDefragmentationInfo->maxBytesToMove; - maxAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove; - } - - // Process standard memory. - for(uint32_t memTypeIndex = 0; - (memTypeIndex < GetMemoryTypeCount()) && (result == VK_SUCCESS); - ++memTypeIndex) - { - // Only HOST_VISIBLE memory types can be defragmented. - if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + else { - result = m_pBlockVectors[memTypeIndex]->Defragment( - pDefragmentationStats, - maxBytesToMove, - maxAllocationsToMove); + UpdateVulkanBudget(); // Outside of mutex lock + GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion } } - - // Process custom pools. - for(size_t poolIndex = 0; (poolIndex < poolCount) && (result == VK_SUCCESS); ++poolIndex) + else +#endif { - result = m_Pools[poolIndex]->GetBlockVector().Defragment( - pDefragmentationStats, - maxBytesToMove, - maxAllocationsToMove); - } - - // ======== Destroy defragmentators. - - // Process custom pools. - for(size_t poolIndex = poolCount; poolIndex--; ) - { - m_Pools[poolIndex]->GetBlockVector().DestroyDefragmentator(); - } - - // Process standard memory. - for(uint32_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; ) - { - if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets) { - m_pBlockVectors[memTypeIndex]->DestroyDefragmentator(); + const uint32_t heapIndex = firstHeap + i; + + outBudgets->blockBytes = m_Budget.m_BlockBytes[heapIndex]; + outBudgets->allocationBytes = m_Budget.m_AllocationBytes[heapIndex]; + + outBudgets->usage = outBudgets->blockBytes; + outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics. } } +} - return result; +VkResult VmaAllocator_T::DefragmentationBegin( + const VmaDefragmentationInfo2& info, + VmaDefragmentationStats* pStats, + VmaDefragmentationContext* pContext) +{ + if(info.pAllocationsChanged != VMA_NULL) + { + memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32)); + } + + *pContext = vma_new(this, VmaDefragmentationContext_T)( + this, info.flags, pStats); + + (*pContext)->AddPools(info.poolCount, info.pPools); + (*pContext)->AddAllocations( + info.allocationCount, info.pAllocations, info.pAllocationsChanged); + + VkResult res = (*pContext)->Defragment( + info.maxCpuBytesToMove, info.maxCpuAllocationsToMove, + info.maxGpuBytesToMove, info.maxGpuAllocationsToMove, + info.commandBuffer, pStats, info.flags); + + if(res != VK_NOT_READY) + { + vma_delete(this, *pContext); + *pContext = VMA_NULL; + } + + return res; +} + +VkResult VmaAllocator_T::DefragmentationEnd( + VmaDefragmentationContext context) +{ + vma_delete(this, context); + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::DefragmentationPassBegin( + VmaDefragmentationPassInfo* pInfo, + VmaDefragmentationContext context) +{ + return context->DefragmentPassBegin(pInfo); +} + +VkResult VmaAllocator_T::DefragmentationPassEnd( + VmaDefragmentationContext context) +{ + return context->DefragmentPassEnd(); } void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo) { - if(hAllocation->CanBecomeLost()) - { - /* - Warning: This is a carefully designed algorithm. - Do not modify unless you really know what you're doing :) - */ - uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load(); - uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex(); - for(;;) - { - if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST) - { - pAllocationInfo->memoryType = UINT32_MAX; - pAllocationInfo->deviceMemory = VK_NULL_HANDLE; - pAllocationInfo->offset = 0; - pAllocationInfo->size = hAllocation->GetSize(); - pAllocationInfo->pMappedData = VMA_NULL; - pAllocationInfo->pUserData = hAllocation->GetUserData(); - return; - } - else if(localLastUseFrameIndex == localCurrFrameIndex) - { - pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); - pAllocationInfo->deviceMemory = hAllocation->GetMemory(); - pAllocationInfo->offset = hAllocation->GetOffset(); - pAllocationInfo->size = hAllocation->GetSize(); - pAllocationInfo->pMappedData = VMA_NULL; - pAllocationInfo->pUserData = hAllocation->GetUserData(); - return; - } - else // Last use time earlier than current time. - { - if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex)) - { - localLastUseFrameIndex = localCurrFrameIndex; - } - } - } - } - else - { - pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); - pAllocationInfo->deviceMemory = hAllocation->GetMemory(); - pAllocationInfo->offset = hAllocation->GetOffset(); - pAllocationInfo->size = hAllocation->GetSize(); - pAllocationInfo->pMappedData = hAllocation->GetMappedData(); - pAllocationInfo->pUserData = hAllocation->GetUserData(); - } + pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); + pAllocationInfo->deviceMemory = hAllocation->GetMemory(); + pAllocationInfo->offset = hAllocation->GetOffset(); + pAllocationInfo->size = hAllocation->GetSize(); + pAllocationInfo->pMappedData = hAllocation->GetMappedData(); + pAllocationInfo->pUserData = hAllocation->GetUserData(); } VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool) { - VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u", pCreateInfo->memoryTypeIndex); + VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags); VmaPoolCreateInfo newCreateInfo = *pCreateInfo; + // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash. + if(pCreateInfo->pMemoryAllocateNext) + { + VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0); + } + if(newCreateInfo.maxBlockCount == 0) { newCreateInfo.maxBlockCount = SIZE_MAX; } - if(newCreateInfo.blockSize == 0) + if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount) { - newCreateInfo.blockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex); + return VK_ERROR_INITIALIZATION_FAILED; + } + // Memory type index out of range or forbidden. + if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() || + ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + if(newCreateInfo.minAllocationAlignment > 0) + { + VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment)); } - *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo); + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex); + + *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize); VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks(); if(res != VK_SUCCESS) @@ -7508,8 +15552,9 @@ VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPoo // Add to m_Pools. { - VmaMutexLock lock(m_PoolsMutex, m_UseMutex); - VmaVectorInsertSorted(m_Pools, *pPool); + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + (*pPool)->SetId(m_NextPoolId++); + m_Pools.PushBack(*pPool); } return VK_SUCCESS; @@ -7519,9 +15564,8 @@ void VmaAllocator_T::DestroyPool(VmaPool pool) { // Remove from m_Pools. { - VmaMutexLock lock(m_PoolsMutex, m_UseMutex); - bool success = VmaVectorRemoveSorted(m_Pools, pool); - VMA_ASSERT(success && "Pool not found in Allocator."); + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + m_Pools.Remove(pool); } vma_delete(this, pool); @@ -7529,58 +15573,138 @@ void VmaAllocator_T::DestroyPool(VmaPool pool) void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats) { - pool->m_BlockVector.GetPoolStats(pPoolStats); + pPoolStats->size = 0; + pPoolStats->unusedSize = 0; + pPoolStats->allocationCount = 0; + pPoolStats->unusedRangeCount = 0; + pPoolStats->blockCount = 0; + + pool->m_BlockVector.AddPoolStats(pPoolStats); + pool->m_DedicatedAllocations.AddPoolStats(pPoolStats); } void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex) { m_CurrentFrameIndex.store(frameIndex); + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + UpdateVulkanBudget(); + } +#endif // #if VMA_MEMORY_BUDGET } -void VmaAllocator_T::MakePoolAllocationsLost( - VmaPool hPool, - size_t* pLostAllocationCount) +VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool) { - hPool->m_BlockVector.MakePoolAllocationsLost( - m_CurrentFrameIndex.load(), - pLostAllocationCount); + return hPool->m_BlockVector.CheckCorruption(); } -void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation) +VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits) { - *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false); - (*pAllocation)->InitLost(); + VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT; + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + if(pBlockVector != VMA_NULL) + { + VkResult localRes = pBlockVector->CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0) + { + VkResult localRes = pool->m_BlockVector.CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + } + + return finalRes; } VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory) { + AtomicTransactionalIncrement deviceMemoryCountIncrement; + const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount); +#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount) + { + return VK_ERROR_TOO_MANY_OBJECTS; + } +#endif + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex); - VkResult res; - if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE) + // HeapSizeLimit is in effect for this heap. + if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0) { - VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex); - if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize) + const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; + VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex]; + for(;;) { - res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); - if(res == VK_SUCCESS) + const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize; + if(blockBytesAfterAllocation > heapSize) { - m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize; + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation)) + { + break; } - } - else - { - res = VK_ERROR_OUT_OF_DEVICE_MEMORY; } } else { - res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); + m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize; } - if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) + // VULKAN CALL vkAllocateMemory. + VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); + + if(res == VK_SUCCESS) { - (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize); +#if VMA_MEMORY_BUDGET + ++m_Budget.m_OperationsSinceBudgetFetch; +#endif + + // Informative callback. + if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData); + } + + deviceMemoryCountIncrement.Commit(); + } + else + { + m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize; } return res; @@ -7588,35 +15712,91 @@ VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAlloc void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory) { + // Informative callback. if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL) { - (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size); + (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData); } + // VULKAN CALL vkFreeMemory. (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks()); - const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType); - if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE) + m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size; + + --m_DeviceMemoryCount; +} + +VkResult VmaAllocator_T::BindVulkanBuffer( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkBuffer buffer, + const void* pNext) +{ + if(pNext != VMA_NULL) { - VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex); - m_HeapSizeLimit[heapIndex] += size; +#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) && + m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL) + { + VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR }; + bindBufferMemoryInfo.pNext = pNext; + bindBufferMemoryInfo.buffer = buffer; + bindBufferMemoryInfo.memory = memory; + bindBufferMemoryInfo.memoryOffset = memoryOffset; + return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo); + } + else +#endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + else + { + return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset); + } +} + +VkResult VmaAllocator_T::BindVulkanImage( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkImage image, + const void* pNext) +{ + if(pNext != VMA_NULL) + { +#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) && + m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL) + { + VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR }; + bindBufferMemoryInfo.pNext = pNext; + bindBufferMemoryInfo.image = image; + bindBufferMemoryInfo.memory = memory; + bindBufferMemoryInfo.memoryOffset = memoryOffset; + return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo); + } + else +#endif // #if VMA_BIND_MEMORY2 + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + else + { + return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset); } } VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData) { - if(hAllocation->CanBecomeLost()) - { - return VK_ERROR_MEMORY_MAP_FAILED; - } - switch(hAllocation->GetType()) { case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: { VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); - char *pBytes = nullptr; - VkResult res = pBlock->Map(this, (void**)&pBytes); + char *pBytes = VMA_NULL; + VkResult res = pBlock->Map(this, 1, (void**)&pBytes); if(res == VK_SUCCESS) { *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset(); @@ -7640,7 +15820,7 @@ void VmaAllocator_T::Unmap(VmaAllocation hAllocation) { VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); hAllocation->BlockAllocUnmap(); - pBlock->Unmap(this); + pBlock->Unmap(this, 1); } break; case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: @@ -7651,42 +15831,346 @@ void VmaAllocator_T::Unmap(VmaAllocation hAllocation) } } -void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation) +VkResult VmaAllocator_T::BindBufferMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext) +{ + VkResult res = VK_SUCCESS; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext); + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block."); + res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::BindImageMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext) +{ + VkResult res = VK_SUCCESS; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext); + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block."); + res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op) +{ + VkResult res = VK_SUCCESS; + + VkMappedMemoryRange memRange = {}; + if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange)) + { + switch(op) + { + case VMA_CACHE_FLUSH: + res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + case VMA_CACHE_INVALIDATE: + res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. + return res; +} + +VkResult VmaAllocator_T::FlushOrInvalidateAllocations( + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, const VkDeviceSize* sizes, + VMA_CACHE_OPERATION op) +{ + typedef VmaStlAllocator RangeAllocator; + typedef VmaSmallVector RangeVector; + RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks())); + + for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + const VmaAllocation alloc = allocations[allocIndex]; + const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0; + const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE; + VkMappedMemoryRange newRange; + if(GetFlushOrInvalidateRange(alloc, offset, size, newRange)) + { + ranges.push_back(newRange); + } + } + + VkResult res = VK_SUCCESS; + if(!ranges.empty()) + { + switch(op) + { + case VMA_CACHE_FLUSH: + res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data()); + break; + case VMA_CACHE_INVALIDATE: + res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data()); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. + return res; +} + +void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation) { VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + VmaPool parentPool = allocation->GetParentPool(); + if(parentPool == VK_NULL_HANDLE) { - VmaMutexLock lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); - AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex]; - VMA_ASSERT(pDedicatedAllocations); - bool success = VmaVectorRemoveSorted(*pDedicatedAllocations, allocation); - VMA_ASSERT(success); + // Default pool + m_DedicatedAllocations[memTypeIndex].Unregister(allocation); + } + else + { + // Custom pool + parentPool->m_DedicatedAllocations.Unregister(allocation); } VkDeviceMemory hMemory = allocation->GetMemory(); - + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + if(allocation->GetMappedData() != VMA_NULL) { (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); } - + */ + FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory); VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex); } -#if VMA_STATS_STRING_ENABLED +uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const +{ + VkBufferCreateInfo dummyBufCreateInfo; + VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo); + uint32_t memoryTypeBits = 0; + + // Create buffer. + VkBuffer buf = VK_NULL_HANDLE; + VkResult res = (*GetVulkanFunctions().vkCreateBuffer)( + m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf); + if(res == VK_SUCCESS) + { + // Query for supported memory types. + VkMemoryRequirements memReq; + (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq); + memoryTypeBits = memReq.memoryTypeBits; + + // Destroy buffer. + (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks()); + } + + return memoryTypeBits; +} + +uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const +{ + // Make sure memory information is already fetched. + VMA_ASSERT(GetMemoryTypeCount() > 0); + + uint32_t memoryTypeBits = UINT32_MAX; + + if(!m_UseAmdDeviceCoherentMemory) + { + // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0) + { + memoryTypeBits &= ~(1u << memTypeIndex); + } + } + } + + return memoryTypeBits; +} + +bool VmaAllocator_T::GetFlushOrInvalidateRange( + VmaAllocation allocation, + VkDeviceSize offset, VkDeviceSize size, + VkMappedMemoryRange& outRange) const +{ + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex)) + { + const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + const VkDeviceSize allocationSize = allocation->GetSize(); + VMA_ASSERT(offset <= allocationSize); + + outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + outRange.pNext = VMA_NULL; + outRange.memory = allocation->GetMemory(); + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + outRange.size = allocationSize - outRange.offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + outRange.size = VMA_MIN( + VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize), + allocationSize - outRange.offset); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + // 1. Still within this allocation. + outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + size = allocationSize - offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + } + outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize); + + // 2. Adjust to whole block. + const VkDeviceSize allocationOffset = allocation->GetOffset(); + VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0); + const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize(); + outRange.offset += allocationOffset; + outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset); + + break; + } + default: + VMA_ASSERT(0); + } + return true; + } + return false; +} + +#if VMA_MEMORY_BUDGET +void VmaAllocator_T::UpdateVulkanBudget() +{ + VMA_ASSERT(m_UseExtMemoryBudget); + + VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR }; + + VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT }; + VmaPnextChainPushFront(&memProps, &budgetProps); + + GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps); + + { + VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex); + + for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) + { + m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex]; + m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex]; + m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load(); + + // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size. + if(m_Budget.m_VulkanBudget[heapIndex] == 0) + { + m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics. + } + else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size) + { + m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size; + } + if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0) + { + m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]; + } + } + m_Budget.m_OperationsSinceBudgetFetch = 0; + } +} +#endif // VMA_MEMORY_BUDGET + +void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern) +{ + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS && + (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + void* pData = VMA_NULL; + VkResult res = Map(hAllocation, &pData); + if(res == VK_SUCCESS) + { + memset(pData, (int)pattern, (size_t)hAllocation->GetSize()); + FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH); + Unmap(hAllocation); + } + else + { + VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation."); + } + } +} + +uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits() +{ + uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load(); + if(memoryTypeBits == UINT32_MAX) + { + memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits(); + m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits); + } + return memoryTypeBits; +} + +#if VMA_STATS_STRING_ENABLED void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) { bool dedicatedAllocationsStarted = false; for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - VmaMutexLock dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex); - AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex]; - VMA_ASSERT(pDedicatedAllocVector); - if(pDedicatedAllocVector->empty() == false) + VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex]; + if(!dedicatedAllocList.IsEmpty()) { if(dedicatedAllocationsStarted == false) { @@ -7698,40 +16182,8 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) json.BeginString("Type "); json.ContinueString(memTypeIndex); json.EndString(); - - json.BeginArray(); - for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i) - { - const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i]; - json.BeginObject(true); - - json.WriteString("Type"); - json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[hAlloc->GetSuballocationType()]); - - json.WriteString("Size"); - json.WriteNumber(hAlloc->GetSize()); - - const void* pUserData = hAlloc->GetUserData(); - if(pUserData != VMA_NULL) - { - json.WriteString("UserData"); - if(hAlloc->IsUserDataString()) - { - json.WriteString((const char*)pUserData); - } - else - { - json.BeginString(); - json.ContinueString_Pointer(pUserData); - json.EndString(); - } - } - - json.EndObject(); - } - - json.EndArray(); + dedicatedAllocList.BuildStatsString(json); } } if(dedicatedAllocationsStarted) @@ -7743,20 +16195,26 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) bool allocationsStarted = false; for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false) + VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex]; + if(pBlockVector != VMA_NULL) { - if(allocationsStarted == false) + if (pBlockVector->IsEmpty() == false) { - allocationsStarted = true; - json.WriteString("DefaultPools"); + if (allocationsStarted == false) + { + allocationsStarted = true; + json.WriteString("DefaultPools"); + json.BeginObject(); + } + + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + json.BeginObject(); + pBlockVector->PrintDetailedMap(json); + json.EndObject(); } - - json.BeginString("Type "); - json.ContinueString(memTypeIndex); - json.EndString(); - - m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json); } } if(allocationsStarted) @@ -7765,75 +16223,76 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) } } + // Custom pools { - VmaMutexLock lock(m_PoolsMutex, m_UseMutex); - const size_t poolCount = m_Pools.size(); - if(poolCount > 0) + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + if(!m_Pools.IsEmpty()) { json.WriteString("Pools"); - json.BeginArray(); - for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex) + json.BeginObject(); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) { - m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json); + json.BeginString(); + json.ContinueString(pool->GetId()); + json.EndString(); + + json.BeginObject(); + pool->m_BlockVector.PrintDetailedMap(json); + + if (!pool->m_DedicatedAllocations.IsEmpty()) + { + json.WriteString("DedicatedAllocations"); + pool->m_DedicatedAllocations.BuildStatsString(json); + } + json.EndObject(); } - json.EndArray(); + json.EndObject(); } } } +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_ALLOCATOR_T_FUNCTIONS -#endif // #if VMA_STATS_STRING_ENABLED -static VkResult AllocateMemoryForImage( - VmaAllocator allocator, - VkImage image, - const VmaAllocationCreateInfo* pAllocationCreateInfo, - VmaSuballocationType suballocType, - VmaAllocation* pAllocation) -{ - VMA_ASSERT(allocator && (image != VK_NULL_HANDLE) && pAllocationCreateInfo && pAllocation); - - VkMemoryRequirements vkMemReq = {}; - bool requiresDedicatedAllocation = false; - bool prefersDedicatedAllocation = false; - allocator->GetImageMemoryRequirements(image, vkMemReq, - requiresDedicatedAllocation, prefersDedicatedAllocation); - - return allocator->AllocateMemory( - vkMemReq, - requiresDedicatedAllocation, - prefersDedicatedAllocation, - VK_NULL_HANDLE, // dedicatedBuffer - image, // dedicatedImage - *pAllocationCreateInfo, - suballocType, - pAllocation); -} - -//////////////////////////////////////////////////////////////////////////////// -// Public interface - -VkResult vmaCreateAllocator( +#ifndef _VMA_PUBLIC_INTERFACE +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator( const VmaAllocatorCreateInfo* pCreateInfo, VmaAllocator* pAllocator) { VMA_ASSERT(pCreateInfo && pAllocator); + VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 || + (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2)); VMA_DEBUG_LOG("vmaCreateAllocator"); *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo); - return VK_SUCCESS; + VkResult result = (*pAllocator)->Init(pCreateInfo); + if(result < 0) + { + vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator); + *pAllocator = VK_NULL_HANDLE; + } + return result; } -void vmaDestroyAllocator( +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator( VmaAllocator allocator) { if(allocator != VK_NULL_HANDLE) { VMA_DEBUG_LOG("vmaDestroyAllocator"); - VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; + VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying. vma_delete(&allocationCallbacks, allocator); } } -void vmaGetPhysicalDeviceProperties( +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo) +{ + VMA_ASSERT(allocator && pAllocatorInfo); + pAllocatorInfo->instance = allocator->m_hInstance; + pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice(); + pAllocatorInfo->device = allocator->m_hDevice; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties( VmaAllocator allocator, const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties) { @@ -7841,7 +16300,7 @@ void vmaGetPhysicalDeviceProperties( *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties; } -void vmaGetMemoryProperties( +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties( VmaAllocator allocator, const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties) { @@ -7849,7 +16308,7 @@ void vmaGetMemoryProperties( *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps; } -void vmaGetMemoryTypeProperties( +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties( VmaAllocator allocator, uint32_t memoryTypeIndex, VkMemoryPropertyFlags* pFlags) @@ -7859,19 +16318,18 @@ void vmaGetMemoryTypeProperties( *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags; } -void vmaSetCurrentFrameIndex( +VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex( VmaAllocator allocator, uint32_t frameIndex) { VMA_ASSERT(allocator); - VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST); VMA_DEBUG_GLOBAL_MUTEX_LOCK allocator->SetCurrentFrameIndex(frameIndex); } -void vmaCalculateStats( +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats( VmaAllocator allocator, VmaStats* pStats) { @@ -7880,9 +16338,18 @@ void vmaCalculateStats( allocator->CalculateStats(pStats); } +VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets( + VmaAllocator allocator, + VmaBudget* pBudgets) +{ + VMA_ASSERT(allocator && pBudgets); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount()); +} + #if VMA_STATS_STRING_ENABLED -void vmaBuildStatsString( +VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString( VmaAllocator allocator, char** ppStatsString, VkBool32 detailedMap) @@ -7890,17 +16357,20 @@ void vmaBuildStatsString( VMA_ASSERT(allocator && ppStatsString); VMA_DEBUG_GLOBAL_MUTEX_LOCK - VmaStringBuilder sb(allocator); + VmaStringBuilder sb(allocator->GetAllocationCallbacks()); { VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb); json.BeginObject(); + VmaBudget budgets[VK_MAX_MEMORY_HEAPS]; + allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount()); + VmaStats stats; allocator->CalculateStats(&stats); json.WriteString("Total"); VmaPrintStatInfo(json, stats.total); - + for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex) { json.BeginString("Heap "); @@ -7919,6 +16389,20 @@ void vmaBuildStatsString( } json.EndArray(); + json.WriteString("Budget"); + json.BeginObject(); + { + json.WriteString("BlockBytes"); + json.WriteNumber(budgets[heapIndex].blockBytes); + json.WriteString("AllocationBytes"); + json.WriteNumber(budgets[heapIndex].allocationBytes); + json.WriteString("Usage"); + json.WriteNumber(budgets[heapIndex].usage); + json.WriteString("Budget"); + json.WriteNumber(budgets[heapIndex].budget); + } + json.EndObject(); + if(stats.memoryHeap[heapIndex].blockCount > 0) { json.WriteString("Stats"); @@ -7958,6 +16442,22 @@ void vmaBuildStatsString( { json.WriteString("LAZILY_ALLOCATED"); } +#if VMA_VULKAN_VERSION >= 1001000 + if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) + { + json.WriteString("PROTECTED"); + } +#endif // #if VMA_VULKAN_VERSION >= 1001000 +#if VK_AMD_device_coherent_memory + if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0) + { + json.WriteString("DEVICE_COHERENT"); + } + if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0) + { + json.WriteString("DEVICE_UNCACHED"); + } +#endif // #if VK_AMD_device_coherent_memory json.EndArray(); if(stats.memoryType[typeIndex].blockCount > 0) @@ -7980,34 +16480,26 @@ void vmaBuildStatsString( json.EndObject(); } - const size_t len = sb.GetLength(); - char* const pChars = vma_new_array(allocator, char, len + 1); - if(len > 0) - { - memcpy(pChars, sb.GetData(), len); - } - pChars[len] = '\0'; - *ppStatsString = pChars; + *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength()); } -void vmaFreeStatsString( +VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString( VmaAllocator allocator, char* pStatsString) { if(pStatsString != VMA_NULL) { VMA_ASSERT(allocator); - size_t len = strlen(pStatsString); - vma_delete_array(allocator, pStatsString, len + 1); + VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString); } } -#endif // #if VMA_STATS_STRING_ENABLED +#endif // VMA_STATS_STRING_ENABLED /* This function is not protected by any mutex because it just reads immutable data. */ -VkResult vmaFindMemoryTypeIndex( +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex( VmaAllocator allocator, uint32_t memoryTypeBits, const VmaAllocationCreateInfo* pAllocationCreateInfo, @@ -8017,13 +16509,16 @@ VkResult vmaFindMemoryTypeIndex( VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + memoryTypeBits &= allocator->GetGlobalMemoryTypeBits(); + if(pAllocationCreateInfo->memoryTypeBits != 0) { memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits; } - + uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags; uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags; + uint32_t notPreferredFlags = 0; // Convert usage to requiredFlags and preferredFlags. switch(pAllocationCreateInfo->usage) @@ -8031,23 +16526,43 @@ VkResult vmaFindMemoryTypeIndex( case VMA_MEMORY_USAGE_UNKNOWN: break; case VMA_MEMORY_USAGE_GPU_ONLY: - preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } break; case VMA_MEMORY_USAGE_CPU_ONLY: requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; break; case VMA_MEMORY_USAGE_CPU_TO_GPU: requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } break; case VMA_MEMORY_USAGE_GPU_TO_CPU: requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + break; + case VMA_MEMORY_USAGE_CPU_COPY: + notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED: + requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; break; default: + VMA_ASSERT(0); break; } + // Avoid DEVICE_COHERENT unless explicitly requested. + if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) & + (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0) + { + notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY; + } + *pMemoryTypeIndex = UINT32_MAX; uint32_t minCost = UINT32_MAX; for(uint32_t memTypeIndex = 0, memTypeBit = 1; @@ -8063,7 +16578,8 @@ VkResult vmaFindMemoryTypeIndex( if((requiredFlags & ~currFlags) == 0) { // Calculate cost as number of bits from preferredFlags not present in this memory type. - uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags); + uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) + + VmaCountBitsSet(currFlags & notPreferredFlags); // Remember memory type with lowest cost. if(currCost < minCost) { @@ -8080,10 +16596,78 @@ VkResult vmaFindMemoryTypeIndex( return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT; } -VkResult vmaCreatePool( - VmaAllocator allocator, - const VmaPoolCreateInfo* pCreateInfo, - VmaPool* pPool) +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pBufferCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + VkBuffer hBuffer = VK_NULL_HANDLE; + const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions(); + VkResult res = funcs->vkCreateBuffer( + hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + funcs->vkGetBufferMemoryRequirements( + hDev, hBuffer, &memReq); + + res = vmaFindMemoryTypeIndex( + allocator, + memReq.memoryTypeBits, + pAllocationCreateInfo, + pMemoryTypeIndex); + + funcs->vkDestroyBuffer( + hDev, hBuffer, allocator->GetAllocationCallbacks()); + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pImageCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + VkImage hImage = VK_NULL_HANDLE; + const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions(); + VkResult res = funcs->vkCreateImage( + hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + funcs->vkGetImageMemoryRequirements( + hDev, hImage, &memReq); + + res = vmaFindMemoryTypeIndex( + allocator, + memReq.memoryTypeBits, + pAllocationCreateInfo, + pMemoryTypeIndex); + + funcs->vkDestroyImage( + hDev, hImage, allocator->GetAllocationCallbacks()); + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool( + VmaAllocator allocator, + const VmaPoolCreateInfo* pCreateInfo, + VmaPool* pPool) { VMA_ASSERT(allocator && pCreateInfo && pPool); @@ -8094,7 +16678,7 @@ VkResult vmaCreatePool( return allocator->CreatePool(pCreateInfo, pPool); } -void vmaDestroyPool( +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool( VmaAllocator allocator, VmaPool pool) { @@ -8112,7 +16696,7 @@ void vmaDestroyPool( allocator->DestroyPool(pool); } -void vmaGetPoolStats( +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats( VmaAllocator allocator, VmaPool pool, VmaPoolStats* pPoolStats) @@ -8124,19 +16708,46 @@ void vmaGetPoolStats( allocator->GetPoolStats(pool, pPoolStats); } -void vmaMakePoolAllocationsLost( - VmaAllocator allocator, - VmaPool pool, - size_t* pLostAllocationCount) +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool) { VMA_ASSERT(allocator && pool); VMA_DEBUG_GLOBAL_MUTEX_LOCK - allocator->MakePoolAllocationsLost(pool, pLostAllocationCount); + VMA_DEBUG_LOG("vmaCheckPoolCorruption"); + + return allocator->CheckPoolCorruption(pool); } -VkResult vmaAllocateMemory( +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName( + VmaAllocator allocator, + VmaPool pool, + const char** ppName) +{ + VMA_ASSERT(allocator && pool && ppName); + + VMA_DEBUG_LOG("vmaGetPoolName"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *ppName = pool->GetName(); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName( + VmaAllocator allocator, + VmaPool pool, + const char* pName) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_LOG("vmaSetPoolName"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + pool->SetName(pName); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory( VmaAllocator allocator, const VkMemoryRequirements* pVkMemoryRequirements, const VmaAllocationCreateInfo* pCreateInfo, @@ -8149,25 +16760,69 @@ VkResult vmaAllocateMemory( VMA_DEBUG_GLOBAL_MUTEX_LOCK - VkResult result = allocator->AllocateMemory( + VkResult result = allocator->AllocateMemory( *pVkMemoryRequirements, false, // requiresDedicatedAllocation false, // prefersDedicatedAllocation VK_NULL_HANDLE, // dedicatedBuffer + UINT32_MAX, // dedicatedBufferUsage VK_NULL_HANDLE, // dedicatedImage *pCreateInfo, VMA_SUBALLOCATION_TYPE_UNKNOWN, + 1, // allocationCount pAllocation); - if(pAllocationInfo && result == VK_SUCCESS) + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } - return result; + return result; } -VkResult vmaAllocateMemoryForBuffer( +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + size_t allocationCount, + VmaAllocation* pAllocations, + VmaAllocationInfo* pAllocationInfo) +{ + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations); + + VMA_DEBUG_LOG("vmaAllocateMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + UINT32_MAX, // dedicatedBufferUsage + VK_NULL_HANDLE, // dedicatedImage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + allocationCount, + pAllocations); + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + for(size_t i = 0; i < allocationCount; ++i) + { + allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i); + } + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer( VmaAllocator allocator, VkBuffer buffer, const VmaAllocationCreateInfo* pCreateInfo, @@ -8192,9 +16847,11 @@ VkResult vmaAllocateMemoryForBuffer( requiresDedicatedAllocation, prefersDedicatedAllocation, buffer, // dedicatedBuffer + UINT32_MAX, // dedicatedBufferUsage VK_NULL_HANDLE, // dedicatedImage *pCreateInfo, VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount pAllocation); if(pAllocationInfo && result == VK_SUCCESS) @@ -8202,10 +16859,10 @@ VkResult vmaAllocateMemoryForBuffer( allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } - return result; + return result; } -VkResult vmaAllocateMemoryForImage( +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage( VmaAllocator allocator, VkImage image, const VmaAllocationCreateInfo* pCreateInfo, @@ -8218,11 +16875,22 @@ VkResult vmaAllocateMemoryForImage( VMA_DEBUG_GLOBAL_MUTEX_LOCK - VkResult result = AllocateMemoryForImage( - allocator, - image, - pCreateInfo, + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(image, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + UINT32_MAX, // dedicatedBufferUsage + image, // dedicatedImage + *pCreateInfo, VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, + 1, // allocationCount pAllocation); if(pAllocationInfo && result == VK_SUCCESS) @@ -8230,23 +16898,49 @@ VkResult vmaAllocateMemoryForImage( allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } - return result; + return result; } -void vmaFreeMemory( +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory( VmaAllocator allocator, VmaAllocation allocation) { - VMA_ASSERT(allocator && allocation); + VMA_ASSERT(allocator); + + if(allocation == VK_NULL_HANDLE) + { + return; + } VMA_DEBUG_LOG("vmaFreeMemory"); VMA_DEBUG_GLOBAL_MUTEX_LOCK - allocator->FreeMemory(allocation); + allocator->FreeMemory( + 1, // allocationCount + &allocation); } -void vmaGetAllocationInfo( +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages( + VmaAllocator allocator, + size_t allocationCount, + const VmaAllocation* pAllocations) +{ + if(allocationCount == 0) + { + return; + } + + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaFreeMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FreeMemory(allocationCount, pAllocations); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo( VmaAllocator allocator, VmaAllocation allocation, VmaAllocationInfo* pAllocationInfo) @@ -8258,7 +16952,7 @@ void vmaGetAllocationInfo( allocator->GetAllocationInfo(allocation, pAllocationInfo); } -void vmaSetAllocationUserData( +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData( VmaAllocator allocator, VmaAllocation allocation, void* pUserData) @@ -8270,18 +16964,17 @@ void vmaSetAllocationUserData( allocation->SetUserData(allocator, pUserData); } -void vmaCreateLostAllocation( - VmaAllocator allocator, - VmaAllocation* pAllocation) +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags) { - VMA_ASSERT(allocator && pAllocation); - - VMA_DEBUG_GLOBAL_MUTEX_LOCK; - - allocator->CreateLostAllocation(pAllocation); + VMA_ASSERT(allocator && allocation && pFlags); + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + *pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags; } -VkResult vmaMapMemory( +VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory( VmaAllocator allocator, VmaAllocation allocation, void** ppData) @@ -8293,7 +16986,7 @@ VkResult vmaMapMemory( return allocator->Map(allocation, ppData); } -void vmaUnmapMemory( +VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory( VmaAllocator allocator, VmaAllocation allocation) { @@ -8304,24 +16997,282 @@ void vmaUnmapMemory( allocator->Unmap(allocation); } -VkResult vmaDefragment( +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation( VmaAllocator allocator, - VmaAllocation* pAllocations, + VmaAllocation allocation, + VkDeviceSize offset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaFlushAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize offset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaInvalidateAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations( + VmaAllocator allocator, + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, + const VkDeviceSize* sizes) +{ + VMA_ASSERT(allocator); + + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocations); + + VMA_DEBUG_LOG("vmaFlushAllocations"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations( + VmaAllocator allocator, + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, + const VkDeviceSize* sizes) +{ + VMA_ASSERT(allocator); + + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocations); + + VMA_DEBUG_LOG("vmaInvalidateAllocations"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption( + VmaAllocator allocator, + uint32_t memoryTypeBits) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaCheckCorruption"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CheckCorruption(memoryTypeBits); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment( + VmaAllocator allocator, + const VmaAllocation* pAllocations, size_t allocationCount, VkBool32* pAllocationsChanged, const VmaDefragmentationInfo *pDefragmentationInfo, VmaDefragmentationStats* pDefragmentationStats) { - VMA_ASSERT(allocator && pAllocations); + // Deprecated interface, reimplemented using new one. - VMA_DEBUG_LOG("vmaDefragment"); + VmaDefragmentationInfo2 info2 = {}; + info2.allocationCount = (uint32_t)allocationCount; + info2.pAllocations = pAllocations; + info2.pAllocationsChanged = pAllocationsChanged; + if(pDefragmentationInfo != VMA_NULL) + { + info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove; + info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove; + } + else + { + info2.maxCpuAllocationsToMove = UINT32_MAX; + info2.maxCpuBytesToMove = VK_WHOLE_SIZE; + } + // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero. + + VmaDefragmentationContext ctx; + VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx); + if(res == VK_NOT_READY) + { + res = vmaDefragmentationEnd( allocator, ctx); + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin( + VmaAllocator allocator, + const VmaDefragmentationInfo2* pInfo, + VmaDefragmentationStats* pStats, + VmaDefragmentationContext *pContext) +{ + VMA_ASSERT(allocator && pInfo && pContext); + + // Degenerate case: Nothing to defragment. + if(pInfo->allocationCount == 0 && pInfo->poolCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL); + VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL); + VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations)); + VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools)); + + VMA_DEBUG_LOG("vmaDefragmentationBegin"); VMA_DEBUG_GLOBAL_MUTEX_LOCK - return allocator->Defragment(pAllocations, allocationCount, pAllocationsChanged, pDefragmentationInfo, pDefragmentationStats); + VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext); + + return res; } -VkResult vmaCreateBuffer( +VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd( + VmaAllocator allocator, + VmaDefragmentationContext context) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaDefragmentationEnd"); + + if(context != VK_NULL_HANDLE) + { + VMA_DEBUG_GLOBAL_MUTEX_LOCK + return allocator->DefragmentationEnd(context); + } + else + { + return VK_SUCCESS; + } +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass( + VmaAllocator allocator, + VmaDefragmentationContext context, + VmaDefragmentationPassInfo* pInfo + ) +{ + VMA_ASSERT(allocator); + VMA_ASSERT(pInfo); + + VMA_DEBUG_LOG("vmaBeginDefragmentationPass"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(context == VK_NULL_HANDLE) + { + pInfo->moveCount = 0; + return VK_SUCCESS; + } + + return allocator->DefragmentationPassBegin(pInfo, context); +} +VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass( + VmaAllocator allocator, + VmaDefragmentationContext context) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaEndDefragmentationPass"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(context == VK_NULL_HANDLE) + return VK_SUCCESS; + + return allocator->DefragmentationPassEnd(context); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkBuffer buffer) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize allocationLocalOffset, + VkBuffer buffer, + const void* pNext) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory2"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkImage image) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, 0, image, VMA_NULL); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize allocationLocalOffset, + VkImage image, + const void* pNext) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory2"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer( VmaAllocator allocator, const VkBufferCreateInfo* pBufferCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, @@ -8330,9 +17281,20 @@ VkResult vmaCreateBuffer( VmaAllocationInfo* pAllocationInfo) { VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation); - + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + VMA_DEBUG_LOG("vmaCreateBuffer"); - + VMA_DEBUG_GLOBAL_MUTEX_LOCK *pBuffer = VK_NULL_HANDLE; @@ -8353,52 +17315,42 @@ VkResult vmaCreateBuffer( allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, requiresDedicatedAllocation, prefersDedicatedAllocation); - // Make sure alignment requirements for specific buffer usages reported - // in Physical Device Properties are included in alignment reported by memory requirements. - if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0) - { - VMA_ASSERT(vkMemReq.alignment % - allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0); - } - if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0) - { - VMA_ASSERT(vkMemReq.alignment % - allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0); - } - if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0) - { - VMA_ASSERT(vkMemReq.alignment % - allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0); - } - // 3. Allocate memory using allocator. res = allocator->AllocateMemory( vkMemReq, requiresDedicatedAllocation, prefersDedicatedAllocation, *pBuffer, // dedicatedBuffer + pBufferCreateInfo->usage, // dedicatedBufferUsage VK_NULL_HANDLE, // dedicatedImage *pAllocationCreateInfo, VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount pAllocation); + if(res >= 0) { // 3. Bind buffer with memory. - res = (*allocator->GetVulkanFunctions().vkBindBufferMemory)( - allocator->m_hDevice, - *pBuffer, - (*pAllocation)->GetMemory(), - (*pAllocation)->GetOffset()); + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } if(res >= 0) { // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage); + #endif if(pAllocationInfo != VMA_NULL) { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } + return VK_SUCCESS; } - allocator->FreeMemory(*pAllocation); + allocator->FreeMemory( + 1, // allocationCount + pAllocation); *pAllocation = VK_NULL_HANDLE; (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); *pBuffer = VK_NULL_HANDLE; @@ -8411,26 +17363,131 @@ VkResult vmaCreateBuffer( return res; } -void vmaDestroyBuffer( +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateBufferWithAlignment"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // 2a. Include minAlignment + vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment); + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + pBufferCreateInfo->usage, // dedicatedBufferUsage + VK_NULL_HANDLE, // dedicatedImage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind buffer with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer( VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation) { + VMA_ASSERT(allocator); + + if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + if(buffer != VK_NULL_HANDLE) { - VMA_ASSERT(allocator); - - VMA_DEBUG_LOG("vmaDestroyBuffer"); - - VMA_DEBUG_GLOBAL_MUTEX_LOCK - (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks()); - - allocator->FreeMemory(allocation); + } + + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); } } -VkResult vmaCreateImage( +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage( VmaAllocator allocator, const VkImageCreateInfo* pImageCreateInfo, const VmaAllocationCreateInfo* pAllocationCreateInfo, @@ -8440,6 +17497,15 @@ VkResult vmaCreateImage( { VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation); + if(pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + VMA_DEBUG_LOG("vmaCreateImage"); VMA_DEBUG_GLOBAL_MUTEX_LOCK @@ -8458,27 +17524,49 @@ VkResult vmaCreateImage( VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; - + // 2. Allocate memory using allocator. - res = AllocateMemoryForImage(allocator, *pImage, pAllocationCreateInfo, suballocType, pAllocation); + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(*pImage, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + UINT32_MAX, // dedicatedBufferUsage + *pImage, // dedicatedImage + *pAllocationCreateInfo, + suballocType, + 1, // allocationCount + pAllocation); + if(res >= 0) { // 3. Bind image with memory. - res = (*allocator->GetVulkanFunctions().vkBindImageMemory)( - allocator->m_hDevice, - *pImage, - (*pAllocation)->GetMemory(), - (*pAllocation)->GetOffset()); + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL); + } if(res >= 0) { // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage); + #endif if(pAllocationInfo != VMA_NULL) { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } + return VK_SUCCESS; } - allocator->FreeMemory(*pAllocation); + allocator->FreeMemory( + 1, // allocationCount + pAllocation); *pAllocation = VK_NULL_HANDLE; (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); *pImage = VK_NULL_HANDLE; @@ -8491,23 +17579,2088 @@ VkResult vmaCreateImage( return res; } -void vmaDestroyImage( +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage( VmaAllocator allocator, VkImage image, VmaAllocation allocation) { + VMA_ASSERT(allocator); + + if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + if(image != VK_NULL_HANDLE) { - VMA_ASSERT(allocator); - - VMA_DEBUG_LOG("vmaDestroyImage"); - - VMA_DEBUG_GLOBAL_MUTEX_LOCK - (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks()); - - allocator->FreeMemory(allocation); + } + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); } } -#endif // #ifdef VMA_IMPLEMENTATION +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( + const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock) +{ + VMA_ASSERT(pCreateInfo && pVirtualBlock); + VMA_ASSERT(pCreateInfo->size > 0); + VMA_DEBUG_LOG("vmaCreateVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo); + VkResult res = (*pVirtualBlock)->Init(); + if(res < 0) + { + vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock); + *pVirtualBlock = VK_NULL_HANDLE; + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock) +{ + if(virtualBlock != VK_NULL_HANDLE) + { + VMA_DEBUG_LOG("vmaDestroyVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying. + vma_delete(&allocationCallbacks, virtualBlock); + } +} + +VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL); + VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation* VMA_NOT_NULL pAllocation, + VkDeviceSize* VMA_NULLABLE pOffset) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL); + VMA_DEBUG_LOG("vmaVirtualAllocate"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE allocation) +{ + if(virtualBlock != VMA_NULL) + { + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaVirtualFree"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Free(allocation); + } +} + +VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaClearVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Clear(); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL allocation, void* VMA_NULLABLE pUserData) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->SetAllocationUserData(allocation, pUserData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStats(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaStatInfo* VMA_NOT_NULL pStatInfo) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStatInfo != VMA_NULL); + VMA_DEBUG_LOG("vmaCalculateVirtualBlockStats"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->CalculateStats(*pStatInfo); +} + +#if VMA_STATS_STRING_ENABLED + +VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks(); + VmaStringBuilder sb(allocationCallbacks); + virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb); + *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength()); +} + +#endif // VMA_STATS_STRING_ENABLED + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE pStatsString) +{ + if(pStatsString != VMA_NULL) + { + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString); + } +} +#endif // _VMA_PUBLIC_INTERFACE +#endif // VMA_IMPLEMENTATION + +/** +\page quick_start Quick start + +\section quick_start_project_setup Project setup + +Vulkan Memory Allocator comes in form of a "stb-style" single header file. +You don't need to build it as a separate library project. +You can add this file directly to your project and submit it to code repository next to your other source files. + +"Single header" doesn't mean that everything is contained in C/C++ declarations, +like it tends to be in case of inline functions or C++ templates. +It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro. +If you don't do it properly, you will get linker errors. + +To do it properly: + +-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library. + This includes declarations of all members of the library. +-# In exactly one CPP file define following macro before this include. + It enables also internal definitions. + +\code +#define VMA_IMPLEMENTATION +#include "vk_mem_alloc.h" +\endcode + +It may be a good idea to create dedicated CPP file just for this purpose. + +Note on language: This library is written in C++, but has C-compatible interface. +Thus you can include and use vk_mem_alloc.h in C or C++ code, but full +implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C. + +Please note that this library includes header ``, which in turn +includes `` on Windows. If you need some specific macros defined +before including these headers (like `WIN32_LEAN_AND_MEAN` or +`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define +them before every `#include` of this library. + +You may need to configure the way you import Vulkan functions. + +- By default, VMA assumes you you link statically with Vulkan API. If this is not the case, + `#define VMA_STATIC_VULKAN_FUNCTIONS 0` before `#include` of the VMA implementation and use another way. +- You can `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1` and pass only pointers to `vkGetInstanceProcAddr` and + `vkGetDeviceProcAddr` functions through VmaAllocatorCreateInfo::pVulkanFunctions. + All the remaining Vulkan functions will be fetched automatically. +- Finally, you can provide your own pointers to all Vulkan functions needed by VMA using structure member + VmaAllocatorCreateInfo::pVulkanFunctions, if you fetched them in some custom way e.g. using some loader like [Volk](https://github.com/zeux/volk). + + +\section quick_start_initialization Initialization + +At program startup: + +-# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object. +-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by + calling vmaCreateAllocator(). + +\code +VmaAllocatorCreateInfo allocatorInfo = {}; +allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_2; +allocatorInfo.physicalDevice = physicalDevice; +allocatorInfo.device = device; +allocatorInfo.instance = instance; + +VmaAllocator allocator; +vmaCreateAllocator(&allocatorInfo, &allocator); +\endcode + +Only members `physicalDevice`, `device`, `instance` are required. +However, you should inform the library which Vulkan version do you use by setting +VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable +by setting VmaAllocatorCreateInfo::flags (like #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT for VK_KHR_buffer_device_address). +Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions. + + +\section quick_start_resource_allocation Resource allocation + +When you want to create a buffer or image: + +-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure. +-# Fill VmaAllocationCreateInfo structure. +-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory + already allocated and bound to it. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +Don't forget to destroy your objects when no longer needed: + +\code +vmaDestroyBuffer(allocator, buffer, allocation); +vmaDestroyAllocator(allocator); +\endcode + + +\page choosing_memory_type Choosing memory type + +Physical devices in Vulkan support various combinations of memory heaps and +types. Help with choosing correct and optimal memory type for your specific +resource is one of the key features of this library. You can use it by filling +appropriate members of VmaAllocationCreateInfo structure, as described below. +You can also combine multiple methods. + +-# If you just want to find memory type index that meets your requirements, you + can use function: vmaFindMemoryTypeIndex(), vmaFindMemoryTypeIndexForBufferInfo(), + vmaFindMemoryTypeIndexForImageInfo(). +-# If you want to allocate a region of device memory without association with any + specific image or buffer, you can use function vmaAllocateMemory(). Usage of + this function is not recommended and usually not needed. + vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once, + which may be useful for sparse binding. +-# If you already have a buffer or an image created, you want to allocate memory + for it and then you will bind it yourself, you can use function + vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(). + For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory() + or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2(). +-# If you want to create a buffer or an image, allocate memory for it and bind + them together, all in one call, you can use function vmaCreateBuffer(), + vmaCreateImage(). This is the easiest and recommended way to use this library. + +When using 3. or 4., the library internally queries Vulkan for memory types +supported for that buffer or image (function `vkGetBufferMemoryRequirements()`) +and uses only one of these types. + +If no memory type can be found that meets all the requirements, these functions +return `VK_ERROR_FEATURE_NOT_PRESENT`. + +You can leave VmaAllocationCreateInfo structure completely filled with zeros. +It means no requirements are specified for memory type. +It is valid, although not very useful. + +\section choosing_memory_type_usage Usage + +The easiest way to specify memory requirements is to fill member +VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage. +It defines high level, common usage types. +For more details, see description of this enum. + +For example, if you want to create a uniform buffer that will be filled using +transfer only once or infrequently and used for rendering every frame, you can +do it using following code: + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +\section choosing_memory_type_required_preferred_flags Required and preferred flags + +You can specify more detailed requirements by filling members +VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags +with a combination of bits from enum `VkMemoryPropertyFlags`. For example, +if you want to create a buffer that will be persistently mapped on host (so it +must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`, +use following code: + +\code +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; +allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +A memory type is chosen that has all the required flags and as many preferred +flags set as possible. + +If you use VmaAllocationCreateInfo::usage, it is just internally converted to +a set of required and preferred flags. + +\section choosing_memory_type_explicit_memory_types Explicit memory types + +If you inspected memory types available on the physical device and you have +a preference for memory types that you want to use, you can fill member +VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set +means that a memory type with that index is allowed to be used for the +allocation. Special value 0, just like `UINT32_MAX`, means there are no +restrictions to memory type index. + +Please note that this member is NOT just a memory type index. +Still you can use it to choose just one, specific memory type. +For example, if you already determined that your buffer should be created in +memory type 2, use following code: + +\code +uint32_t memoryTypeIndex = 2; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.memoryTypeBits = 1u << memoryTypeIndex; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + + +\section choosing_memory_type_custom_memory_pools Custom memory pools + +If you allocate from custom memory pool, all the ways of specifying memory +requirements described above are not applicable and the aforementioned members +of VmaAllocationCreateInfo structure are ignored. Memory type is selected +explicitly when creating the pool and then used to make all the allocations from +that pool. For further details, see \ref custom_memory_pools. + +\section choosing_memory_type_dedicated_allocations Dedicated allocations + +Memory for allocations is reserved out of larger block of `VkDeviceMemory` +allocated from Vulkan internally. That is the main feature of this whole library. +You can still request a separate memory block to be created for an allocation, +just like you would do in a trivial solution without using any allocator. +In that case, a buffer or image is always bound to that memory at offset 0. +This is called a "dedicated allocation". +You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +The library can also internally decide to use dedicated allocation in some cases, e.g.: + +- When the size of the allocation is large. +- When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled + and it reports that dedicated allocation is required or recommended for the resource. +- When allocation of next big memory block fails due to not enough device memory, + but allocation with the exact requested size succeeds. + + +\page memory_mapping Memory mapping + +To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`, +to be able to read from it or write to it in CPU code. +Mapping is possible only of memory allocated from a memory type that has +`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag. +Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose. +You can use them directly with memory allocated by this library, +but it is not recommended because of following issue: +Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed. +This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan. +Because of this, Vulkan Memory Allocator provides following facilities: + +\section memory_mapping_mapping_functions Mapping functions + +The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory(). +They are safer and more convenient to use than standard Vulkan functions. +You can map an allocation multiple times simultaneously - mapping is reference-counted internally. +You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block. +The way it is implemented is that the library always maps entire memory block, not just region of the allocation. +For further details, see description of vmaMapMemory() function. +Example: + +\code +// Having these objects initialized: + +struct ConstantBuffer +{ + ... +}; +ConstantBuffer constantBufferData; + +VmaAllocator allocator; +VkBuffer constantBuffer; +VmaAllocation constantBufferAllocation; + +// You can map and fill your buffer using following code: + +void* mappedData; +vmaMapMemory(allocator, constantBufferAllocation, &mappedData); +memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); +vmaUnmapMemory(allocator, constantBufferAllocation); +\endcode + +When mapping, you may see a warning from Vulkan validation layer similar to this one: + +Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used. + +It happens because the library maps entire `VkDeviceMemory` block, where different +types of images and buffers may end up together, especially on GPUs with unified memory like Intel. +You can safely ignore it if you are sure you access only memory of the intended +object that you wanted to map. + + +\section memory_mapping_persistently_mapped_memory Persistently mapped memory + +Kepping your memory persistently mapped is generally OK in Vulkan. +You don't need to unmap it before using its data on the GPU. +The library provides a special feature designed for that: +Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in +VmaAllocationCreateInfo::flags stay mapped all the time, +so you can just access CPU pointer to it any time +without a need to call any "map" or "unmap" function. +Example: + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +// Buffer is already mapped. You can access its memory. +memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); +\endcode + +There are some exceptions though, when you should consider mapping memory only for a short period of time: + +- When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2), + device is discrete AMD GPU, + and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory + (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU), + then whenever a memory block allocated from this memory type stays mapped + for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this + block is migrated by WDDM to system RAM, which degrades performance. It doesn't + matter if that particular memory block is actually used by the command buffer + being submitted. +- Keeping many large memory blocks mapped may impact performance or stability of some debugging tools. + +\section memory_mapping_cache_control Cache flush and invalidate + +Memory in Vulkan doesn't need to be unmapped before using it on GPU, +but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set, +you need to manually **invalidate** cache before reading of mapped pointer +and **flush** cache after writing to mapped pointer. +Map/unmap operations don't do that automatically. +Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`, +`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient +functions that refer to given allocation object: vmaFlushAllocation(), +vmaInvalidateAllocation(), +or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations(). + +Regions of memory specified for flush/invalidate must be aligned to +`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library. +In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations +within blocks are aligned to this value, so their offsets are always multiply of +`nonCoherentAtomSize` and two different allocations never share same "line" of this size. + +Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`. + +Also, Windows drivers from all 3 **PC** GPU vendors (AMD, Intel, NVIDIA) +currently provide `HOST_COHERENT` flag on all memory types that are +`HOST_VISIBLE`, so on this platform you may not need to bother. + +\section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable + +It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping) +despite it wasn't explicitly requested. +For example, application may work on integrated graphics with unified memory (like Intel) or +allocation from video memory might have failed, so the library chose system memory as fallback. + +You can detect this case and map such allocation to access its memory on CPU directly, +instead of launching a transfer operation. +In order to do that: call vmaGetAllocationMemoryProperties() +and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + +VkBuffer buf; +VmaAllocation alloc; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr); + +VkMemoryPropertyFlags memFlags; +vmaGetAllocationMemoryProperties(allocator, alloc, &memFlags); +if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) +{ + // Allocation ended up in mappable memory. You can map it and access it directly. + void* mappedData; + vmaMapMemory(allocator, alloc, &mappedData); + memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); + vmaUnmapMemory(allocator, alloc); +} +else +{ + // Allocation ended up in non-mappable memory. + // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer. +} +\endcode + +You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations +that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY). +If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly. +If not, the flag is just ignored. +Example: + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +if(allocInfo.pMappedData != nullptr) +{ + // Allocation ended up in mappable memory. + // It is persistently mapped. You can access it directly. + memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); +} +else +{ + // Allocation ended up in non-mappable memory. + // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer. +} +\endcode + + +\page staying_within_budget Staying within budget + +When developing a graphics-intensive game or program, it is important to avoid allocating +more GPU memory than it is physically available. When the memory is over-committed, +various bad things can happen, depending on the specific GPU, graphics driver, and +operating system: + +- It may just work without any problems. +- The application may slow down because some memory blocks are moved to system RAM + and the GPU has to access them through PCI Express bus. +- A new allocation may take very long time to complete, even few seconds, and possibly + freeze entire system. +- The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +- It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST` + returned somewhere later. + +\section staying_within_budget_querying_for_budget Querying for budget + +To query for current memory usage and available budget, use function vmaGetHeapBudgets(). +Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap. + +Please note that this function returns different information and works faster than +vmaCalculateStats(). vmaGetHeapBudgets() can be called every frame or even before every +allocation, while vmaCalculateStats() is intended to be used rarely, +only to obtain statistical information, e.g. for debugging purposes. + +It is recommended to use VK_EXT_memory_budget device extension to obtain information +about the budget from Vulkan device. VMA is able to use this extension automatically. +When not enabled, the allocator behaves same way, but then it estimates current usage +and available budget based on its internal information and Vulkan memory heap sizes, +which may be less precise. In order to use this extension: + +1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2 + required by it are available and enable them. Please note that the first is a device + extension and the second is instance extension! +2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object. +3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from + Vulkan inside of it to avoid overhead of querying it with every allocation. + +\section staying_within_budget_controlling_memory_usage Controlling memory usage + +There are many ways in which you can try to stay within the budget. + +First, when making new allocation requires allocating a new memory block, the library +tries not to exceed the budget automatically. If a block with default recommended size +(e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even +dedicated memory for just this resource. + +If the size of the requested resource plus current memory usage is more than the +budget, by default the library still tries to create it, leaving it to the Vulkan +implementation whether the allocation succeeds or fails. You can change this behavior +by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is +not made if it would exceed the budget or if the budget is already exceeded. +The allocation then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag +when creating resources that are not essential for the application (e.g. the texture +of a specific object) and not to pass it when creating critically important resources +(e.g. render targets). + +Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure +a new allocation is created only when it fits inside one of the existing memory blocks. +If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +This also ensures that the function call is very fast because it never goes to Vulkan +to obtain a new block. + +Please note that creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount +set to more than 0 will try to allocate memory blocks without checking whether they +fit within budget. + + +\page resource_aliasing Resource aliasing (overlap) + +New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory +management, give an opportunity to alias (overlap) multiple resources in the +same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL). +It can be useful to save video memory, but it must be used with caution. + +For example, if you know the flow of your whole render frame in advance, you +are going to use some intermediate textures or buffers only during a small range of render passes, +and you know these ranges don't overlap in time, you can bind these resources to +the same place in memory, even if they have completely different parameters (width, height, format etc.). + +![Resource aliasing (overlap)](../gfx/Aliasing.png) + +Such scenario is possible using VMA, but you need to create your images manually. +Then you need to calculate parameters of an allocation to be made using formula: + +- allocation size = max(size of each image) +- allocation alignment = max(alignment of each image) +- allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image) + +Following example shows two different images bound to the same place in memory, +allocated to fit largest of them. + +\code +// A 512x512 texture to be sampled. +VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +img1CreateInfo.imageType = VK_IMAGE_TYPE_2D; +img1CreateInfo.extent.width = 512; +img1CreateInfo.extent.height = 512; +img1CreateInfo.extent.depth = 1; +img1CreateInfo.mipLevels = 10; +img1CreateInfo.arrayLayers = 1; +img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; +img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; +img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +// A full screen texture to be used as color attachment. +VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +img2CreateInfo.imageType = VK_IMAGE_TYPE_2D; +img2CreateInfo.extent.width = 1920; +img2CreateInfo.extent.height = 1080; +img2CreateInfo.extent.depth = 1; +img2CreateInfo.mipLevels = 1; +img2CreateInfo.arrayLayers = 1; +img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VkImage img1; +res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1); +VkImage img2; +res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2); + +VkMemoryRequirements img1MemReq; +vkGetImageMemoryRequirements(device, img1, &img1MemReq); +VkMemoryRequirements img2MemReq; +vkGetImageMemoryRequirements(device, img2, &img2MemReq); + +VkMemoryRequirements finalMemReq = {}; +finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size); +finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment); +finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits; +// Validate if(finalMemReq.memoryTypeBits != 0) + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + +VmaAllocation alloc; +res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr); + +res = vmaBindImageMemory(allocator, alloc, img1); +res = vmaBindImageMemory(allocator, alloc, img2); + +// You can use img1, img2 here, but not at the same time! + +vmaFreeMemory(allocator, alloc); +vkDestroyImage(allocator, img2, nullptr); +vkDestroyImage(allocator, img1, nullptr); +\endcode + +Remember that using resources that alias in memory requires proper synchronization. +You need to issue a memory barrier to make sure commands that use `img1` and `img2` +don't overlap on GPU timeline. +You also need to treat a resource after aliasing as uninitialized - containing garbage data. +For example, if you use `img1` and then want to use `img2`, you need to issue +an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`. + +Additional considerations: + +- Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases. +See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag. +- You can create more complex layout where different images and buffers are bound +at different offsets inside one large allocation. For example, one can imagine +a big texture used in some render passes, aliasing with a set of many small buffers +used between in some further passes. To bind a resource at non-zero offset of an allocation, +use vmaBindBufferMemory2() / vmaBindImageMemory2(). +- Before allocating memory for the resources you want to alias, check `memoryTypeBits` +returned in memory requirements of each resource to make sure the bits overlap. +Some GPUs may expose multiple memory types suitable e.g. only for buffers or +images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your +resources may be disjoint. Aliasing them is not possible in that case. + + +\page custom_memory_pools Custom memory pools + +A memory pool contains a number of `VkDeviceMemory` blocks. +The library automatically creates and manages default pool for each memory type available on the device. +Default memory pool automatically grows in size. +Size of allocated blocks is also variable and managed automatically. + +You can create custom pool and allocate memory out of it. +It can be useful if you want to: + +- Keep certain kind of allocations separate from others. +- Enforce particular, fixed size of Vulkan memory blocks. +- Limit maximum amount of Vulkan memory allocated for that pool. +- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool. +- Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in + #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain. + +To use custom memory pools: + +-# Fill VmaPoolCreateInfo structure. +-# Call vmaCreatePool() to obtain #VmaPool handle. +-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle. + You don't need to specify any other parameters of this structure, like `usage`. + +Example: + +\code +// Create a pool that can have at most 2 blocks, 128 MiB each. +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = ... +poolCreateInfo.blockSize = 128ull * 1024 * 1024; +poolCreateInfo.maxBlockCount = 2; + +VmaPool pool; +vmaCreatePool(allocator, &poolCreateInfo, &pool); + +// Allocate a buffer out of it. +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 1024; +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.pool = pool; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); +\endcode + +You have to free all allocations made from this pool before destroying it. + +\code +vmaDestroyBuffer(allocator, buf, alloc); +vmaDestroyPool(allocator, pool); +\endcode + +New versions of this library support creating dedicated allocations in custom pools. +It is supported only when VmaPoolCreateInfo::blockSize = 0. +To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and +VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + +\section custom_memory_pools_MemTypeIndex Choosing memory type index + +When creating a pool, you must explicitly specify memory type index. +To find the one suitable for your buffers or images, you can use helper functions +vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo(). +You need to provide structures with example parameters of buffers or images +that you are going to create in that pool. + +\code +VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +exampleBufCreateInfo.size = 1024; // Whatever. +exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed. + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed. + +uint32_t memTypeIndex; +vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex); + +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = memTypeIndex; +// ... +\endcode + +When creating buffers/images allocated in that pool, provide following parameters: + +- `VkBufferCreateInfo`: Prefer to pass same parameters as above. + Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior. + Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers + or the other way around. +- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member. + Other members are ignored anyway. + +\section linear_algorithm Linear allocation algorithm + +Each Vulkan memory block managed by this library has accompanying metadata that +keeps track of used and unused regions. By default, the metadata structure and +algorithm tries to find best place for new allocations among free regions to +optimize memory usage. This way you can allocate and free objects in any order. + +![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png) + +Sometimes there is a need to use simpler, linear allocation algorithm. You can +create custom pool that uses such algorithm by adding flag +#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating +#VmaPool object. Then an alternative metadata management is used. It always +creates new allocations after last one and doesn't reuse free regions after +allocations freed in the middle. It results in better allocation performance and +less memory consumed by metadata. + +![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png) + +With this one flag, you can create a custom pool that can be used in many ways: +free-at-once, stack, double stack, and ring buffer. See below for details. +You don't need to specify explicitly which of these options you are going to use - it is detected automatically. + +\subsection linear_algorithm_free_at_once Free-at-once + +In a pool that uses linear algorithm, you still need to free all the allocations +individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free +them in any order. New allocations are always made after last one - free space +in the middle is not reused. However, when you release all the allocation and +the pool becomes empty, allocation starts from the beginning again. This way you +can use linear algorithm to speed up creation of allocations that you are going +to release all at once. + +![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_stack Stack + +When you free an allocation that was created last, its space can be reused. +Thanks to this, if you always release allocations in the order opposite to their +creation (LIFO - Last In First Out), you can achieve behavior of a stack. + +![Stack](../gfx/Linear_allocator_4_stack.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_double_stack Double stack + +The space reserved by a custom pool with linear algorithm may be used by two +stacks: + +- First, default one, growing up from offset 0. +- Second, "upper" one, growing down from the end towards lower offsets. + +To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT +to VmaAllocationCreateInfo::flags. + +![Double stack](../gfx/Linear_allocator_7_double_stack.png) + +Double stack is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +When the two stacks' ends meet so there is not enough space between them for a +new allocation, such allocation fails with usual +`VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + +\subsection linear_algorithm_ring_buffer Ring buffer + +When you free some allocations from the beginning and there is not enough free space +for a new one at the end of a pool, allocator's "cursor" wraps around to the +beginning and starts allocation there. Thanks to this, if you always release +allocations in the same order as you created them (FIFO - First In First Out), +you can achieve behavior of a ring buffer / queue. + +![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png) + +Ring buffer is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +\section buddy_algorithm Buddy allocation algorithm + +There is another allocation algorithm that can be used with custom pools, called +"buddy". Its internal data structure is based on a binary tree of blocks, each having +size that is a power of two and a half of its parent's size. When you want to +allocate memory of certain size, a free node in the tree is located. If it is too +large, it is recursively split into two halves (called "buddies"). However, if +requested allocation size is not a power of two, the size of the allocation is +aligned up to the nearest power of two and the remaining space is wasted. When +two buddy nodes become free, they are merged back into one larger node. + +![Buddy allocator](../gfx/Buddy_allocator.png) + +The advantage of buddy allocation algorithm over default algorithm is faster +allocation and deallocation, as well as smaller external fragmentation. The +disadvantage is more wasted space (internal fragmentation). +For more information, please search the Internet for "Buddy memory allocation" - +sources that describe this concept in general. + +To use buddy allocation algorithm with a custom pool, add flag +#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating +#VmaPool object. + +Several limitations apply to pools that use buddy algorithm: + +- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two. + Otherwise, only largest power of two smaller than the size is used for + allocations. The remaining space always stays unused. +- [Margins](@ref debugging_memory_usage_margins) and + [corruption detection](@ref debugging_memory_usage_corruption_detection) + don't work in such pools. +- [Defragmentation](@ref defragmentation) doesn't work with allocations made from + such pool. + +\page defragmentation Defragmentation + +Interleaved allocations and deallocations of many objects of varying size can +cause fragmentation over time, which can lead to a situation where the library is unable +to find a continuous range of free memory for a new allocation despite there is +enough free space, just scattered across many small free ranges between existing +allocations. + +To mitigate this problem, you can use defragmentation feature: +structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd(). +Given set of allocations, +this function can move them to compact used memory, ensure more continuous free +space and possibly also free some `VkDeviceMemory` blocks. + +What the defragmentation does is: + +- Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset. + After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or + VmaAllocationInfo::offset changes. You must query them again using + vmaGetAllocationInfo() if you need them. +- Moves actual data in memory. + +What it doesn't do, so you need to do it yourself: + +- Recreate buffers and images that were bound to allocations that were defragmented and + bind them with their new places in memory. + You must use `vkDestroyBuffer()`, `vkDestroyImage()`, + `vkCreateBuffer()`, `vkCreateImage()`, vmaBindBufferMemory(), vmaBindImageMemory() + for that purpose and NOT vmaDestroyBuffer(), + vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to + destroy or create allocation objects! +- Recreate views and update descriptors that point to these buffers and images. + +\section defragmentation_cpu Defragmenting CPU memory + +Following example demonstrates how you can run defragmentation on CPU. +Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented. +Others are ignored. + +The way it works is: + +- It temporarily maps entire memory blocks when necessary. +- It moves data using `memmove()` function. + +\code +// Given following variables already initialized: +VkDevice device; +VmaAllocator allocator; +std::vector buffers; +std::vector allocations; + + +const uint32_t allocCount = (uint32_t)allocations.size(); +std::vector allocationsChanged(allocCount); + +VmaDefragmentationInfo2 defragInfo = {}; +defragInfo.allocationCount = allocCount; +defragInfo.pAllocations = allocations.data(); +defragInfo.pAllocationsChanged = allocationsChanged.data(); +defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit. +defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit. + +VmaDefragmentationContext defragCtx; +vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx); +vmaDefragmentationEnd(allocator, defragCtx); + +for(uint32_t i = 0; i < allocCount; ++i) +{ + if(allocationsChanged[i]) + { + // Destroy buffer that is immutably bound to memory region which is no longer valid. + vkDestroyBuffer(device, buffers[i], nullptr); + + // Create new buffer with same parameters. + VkBufferCreateInfo bufferInfo = ...; + vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]); + + // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning. + + // Bind new buffer to new memory region. Data contained in it is already moved. + VmaAllocationInfo allocInfo; + vmaGetAllocationInfo(allocator, allocations[i], &allocInfo); + vmaBindBufferMemory(allocator, allocations[i], buffers[i]); + } +} +\endcode + +Setting VmaDefragmentationInfo2::pAllocationsChanged is optional. +This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index +has been modified during defragmentation. +You can pass null, but you then need to query every allocation passed to defragmentation +for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it. + +If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools), +you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools +instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations +to defragment all allocations in given pools. +You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case. +You can also combine both methods. + +\section defragmentation_gpu Defragmenting GPU memory + +It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`. +To do that, you need to pass a command buffer that meets requirements as described in +VmaDefragmentationInfo2::commandBuffer. The way it works is: + +- It creates temporary buffers and binds them to entire memory blocks when necessary. +- It issues `vkCmdCopyBuffer()` to passed command buffer. + +Example: + +\code +// Given following variables already initialized: +VkDevice device; +VmaAllocator allocator; +VkCommandBuffer commandBuffer; +std::vector buffers; +std::vector allocations; + + +const uint32_t allocCount = (uint32_t)allocations.size(); +std::vector allocationsChanged(allocCount); + +VkCommandBufferBeginInfo cmdBufBeginInfo = ...; +vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo); + +VmaDefragmentationInfo2 defragInfo = {}; +defragInfo.allocationCount = allocCount; +defragInfo.pAllocations = allocations.data(); +defragInfo.pAllocationsChanged = allocationsChanged.data(); +defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it is "GPU" this time. +defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it is "GPU" this time. +defragInfo.commandBuffer = commandBuffer; + +VmaDefragmentationContext defragCtx; +vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx); + +vkEndCommandBuffer(commandBuffer); + +// Submit commandBuffer. +// Wait for a fence that ensures commandBuffer execution finished. + +vmaDefragmentationEnd(allocator, defragCtx); + +for(uint32_t i = 0; i < allocCount; ++i) +{ + if(allocationsChanged[i]) + { + // Destroy buffer that is immutably bound to memory region which is no longer valid. + vkDestroyBuffer(device, buffers[i], nullptr); + + // Create new buffer with same parameters. + VkBufferCreateInfo bufferInfo = ...; + vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]); + + // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning. + + // Bind new buffer to new memory region. Data contained in it is already moved. + VmaAllocationInfo allocInfo; + vmaGetAllocationInfo(allocator, allocations[i], &allocInfo); + vmaBindBufferMemory(allocator, allocations[i], buffers[i]); + } +} +\endcode + +You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters. +The library automatically chooses best method to defragment each memory pool. + +You may try not to block your entire program to wait until defragmentation finishes, +but do it in the background, as long as you carefully fullfill requirements described +in function vmaDefragmentationBegin(). + +\section defragmentation_additional_notes Additional notes + +It is only legal to defragment allocations bound to: + +- buffers +- images created with `VK_IMAGE_CREATE_ALIAS_BIT`, `VK_IMAGE_TILING_LINEAR`, and + being currently in `VK_IMAGE_LAYOUT_GENERAL` or `VK_IMAGE_LAYOUT_PREINITIALIZED`. + +Defragmentation of images created with `VK_IMAGE_TILING_OPTIMAL` or in any other +layout may give undefined results. + +If you defragment allocations bound to images, new images to be bound to new +memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED` +and then transitioned to their original layout from before defragmentation if +needed using an image memory barrier. + +While using defragmentation, you may experience validation layer warnings, which you just need to ignore. +See [Validation layer warnings](@ref general_considerations_validation_layer_warnings). + +Please don't expect memory to be fully compacted after defragmentation. +Algorithms inside are based on some heuristics that try to maximize number of Vulkan +memory blocks to make totally empty to release them, as well as to maximize continuous +empty space inside remaining blocks, while minimizing the number and size of allocations that +need to be moved. Some fragmentation may still remain - this is normal. + +\section defragmentation_custom_algorithm Writing custom defragmentation algorithm + +If you want to implement your own, custom defragmentation algorithm, +there is infrastructure prepared for that, +but it is not exposed through the library API - you need to hack its source code. +Here are steps needed to do this: + +-# Main thing you need to do is to define your own class derived from base abstract + class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods. + See definition and comments of this class for details. +-# Your code needs to interact with device memory block metadata. + If you need more access to its data than it is provided by its public interface, + declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`. +-# If you want to create a flag that would enable your algorithm or pass some additional + flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in + VmaDefragmentationInfo2::flags. +-# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object + of your new class whenever needed. + + +\page statistics Statistics + +This library contains functions that return information about its internal state, +especially the amount of memory allocated from Vulkan. +Please keep in mind that these functions need to traverse all internal data structures +to gather these information, so they may be quite time-consuming. +Don't call them too often. + +\section statistics_numeric_statistics Numeric statistics + +You can query for overall statistics of the allocator using function vmaCalculateStats(). +Information are returned using structure #VmaStats. +It contains #VmaStatInfo - number of allocated blocks, number of allocations +(occupied ranges in these blocks), number of unused (free) ranges in these blocks, +number of bytes used and unused (but still allocated from Vulkan) and other information. +They are summed across memory heaps, memory types and total for whole allocator. + +You can query for statistics of a custom pool using function vmaGetPoolStats(). +Information are returned using structure #VmaPoolStats. + +You can query for information about specific allocation using function vmaGetAllocationInfo(). +It fill structure #VmaAllocationInfo. + +\section statistics_json_dump JSON dump + +You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString(). +The result is guaranteed to be correct JSON. +It uses ANSI encoding. +Any strings provided by user (see [Allocation names](@ref allocation_names)) +are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding, +this JSON string can be treated as using this encoding. +It must be freed using function vmaFreeStatsString(). + +The format of this JSON string is not part of official documentation of the library, +but it will not change in backward-incompatible way without increasing library major version number +and appropriate mention in changelog. + +The JSON string contains all the data that can be obtained using vmaCalculateStats(). +It can also contain detailed map of allocated memory blocks and their regions - +free and occupied by allocations. +This allows e.g. to visualize the memory or assess fragmentation. + + +\page allocation_annotation Allocation names and user data + +\section allocation_user_data Allocation user data + +You can annotate allocations with your own information, e.g. for debugging purposes. +To do that, fill VmaAllocationCreateInfo::pUserData field when creating +an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer, +some handle, index, key, ordinal number or any other value that would associate +the allocation with your custom metadata. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +// Fill bufferInfo... + +MyBufferMetadata* pMetadata = CreateBufferMetadata(); + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +allocCreateInfo.pUserData = pMetadata; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr); +\endcode + +The pointer may be later retrieved as VmaAllocationInfo::pUserData: + +\code +VmaAllocationInfo allocInfo; +vmaGetAllocationInfo(allocator, allocation, &allocInfo); +MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData; +\endcode + +It can also be changed using function vmaSetAllocationUserData(). + +Values of (non-zero) allocations' `pUserData` are printed in JSON report created by +vmaBuildStatsString(), in hexadecimal form. + +\section allocation_names Allocation names + +There is alternative mode available where `pUserData` pointer is used to point to +a null-terminated string, giving a name to the allocation. To use this mode, +set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags. +Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to +vmaSetAllocationUserData() must be either null or pointer to a null-terminated string. +The library creates internal copy of the string, so the pointer you pass doesn't need +to be valid for whole lifetime of the allocation. You can free it after the call. + +\code +VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +// Fill imageInfo... + +std::string imageName = "Texture: "; +imageName += fileName; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT; +allocCreateInfo.pUserData = imageName.c_str(); + +VkImage image; +VmaAllocation allocation; +vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr); +\endcode + +The value of `pUserData` pointer of the allocation will be different than the one +you passed when setting allocation's name - pointing to a buffer managed +internally that holds copy of the string. + +\code +VmaAllocationInfo allocInfo; +vmaGetAllocationInfo(allocator, allocation, &allocInfo); +const char* imageName = (const char*)allocInfo.pUserData; +printf("Image name: %s\n", imageName); +\endcode + +That string is also printed in JSON report created by vmaBuildStatsString(). + +\note Passing string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it. +You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library. + + +\page virtual_allocator Virtual allocator + +As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator". +It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block". +You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan. +A common use case is sub-allocation of pieces of one large GPU buffer. + +\section virtual_allocator_creating_virtual_block Creating virtual block + +To use this functionality, there is no main "allocator" object. +You don't need to have #VmaAllocator object created. +All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator: + +-# Fill in #VmaVirtualBlockCreateInfo structure. +-# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object. + +Example: + +\code +VmaVirtualBlockCreateInfo blockCreateInfo = {}; +blockCreateInfo.size = 1048576; // 1 MB + +VmaVirtualBlock block; +VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block); +\endcode + +\section virtual_allocator_making_virtual_allocations Making virtual allocations + +#VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions +using the same code as the main Vulkan memory allocator. +Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type +that represents an opaque handle to an allocation withing the virtual block. + +In order to make such allocation: + +-# Fill in #VmaVirtualAllocationCreateInfo structure. +-# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation. + You can also receive `VkDeviceSize offset` that was assigned to the allocation. + +Example: + +\code +VmaVirtualAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.size = 4096; // 4 KB + +VmaVirtualAllocation alloc; +VkDeviceSize offset; +res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset); +if(res == VK_SUCCESS) +{ + // Use the 4 KB of your memory starting at offset. +} +else +{ + // Allocation failed - no space for it could be found. Handle this error! +} +\endcode + +\section virtual_allocator_deallocation Deallocation + +When no longer needed, an allocation can be freed by calling vmaVirtualFree(). +You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate() +called for the same #VmaVirtualBlock. + +When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock(). +All allocations must be freed before the block is destroyed, which is checked internally by an assert. +However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once - +a feature not available in normal Vulkan memory allocator. Example: + +\code +vmaVirtualFree(block, alloc); +vmaDestroyVirtualBlock(block); +\endcode + +\section virtual_allocator_allocation_parameters Allocation parameters + +You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData(). +Its default value is null. +It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some +larger data structure containing more information. Example: + +\code +struct CustomAllocData +{ + std::string m_AllocName; +}; +CustomAllocData* allocData = new CustomAllocData(); +allocData->m_AllocName = "My allocation 1"; +vmaSetVirtualAllocationUserData(block, alloc, allocData); +\endcode + +The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function +vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo. +If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation! +Example: + +\code +VmaVirtualAllocationInfo allocInfo; +vmaGetVirtualAllocationInfo(block, alloc, &allocInfo); +delete (CustomAllocData*)allocInfo.pUserData; + +vmaVirtualFree(block, alloc); +\endcode + +\section virtual_allocator_alignment_and_units Alignment and units + +It feels natural to express sizes and offsets in bytes. +If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member +VmaVirtualAllocationCreateInfo::alignment to request it. Example: + +\code +VmaVirtualAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.size = 4096; // 4 KB +allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B + +VmaVirtualAllocation alloc; +res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr); +\endcode + +Alignments of different allocations made from one block may vary. +However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`, +you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes. +It might be more convenient, but you need to make sure to use this new unit consistently in all the places: + +- VmaVirtualBlockCreateInfo::size +- VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment +- Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset + +\section virtual_allocator_statistics Statistics + +You can obtain statistics of a virtual block using vmaCalculateVirtualBlockStats(). +The function fills structure #VmaStatInfo - same as used by the normal Vulkan memory allocator. +Example: + +\code +VmaStatInfo statInfo; +vmaCalculateVirtualBlockStats(block, &statInfo); +printf("My virtual block has %llu bytes used by %u virtual allocations\n", + statInfo.usedBytes, statInfo.allocationCount); +\endcode + +You can also request a full list of allocations and free regions as a string in JSON format by calling +vmaBuildVirtualBlockStatsString(). +Returned string must be later freed using vmaFreeVirtualBlockStatsString(). +The format of this string differs from the one returned by the main Vulkan allocator, but it is similar. + +\section virtual_allocator_additional_considerations Additional considerations + +The "virtual allocator" functionality is implemented on a level of individual memory blocks. +Keeping track of a whole collection of blocks, allocating new ones when out of free space, +deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user. + +Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory. +See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT). +You can find their description in chapter \ref custom_memory_pools. +Allocation strategies are also supported. +See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT). + +Following features are supported only by the allocator of the real GPU memory and not by virtual allocations: +buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`. + + +\page debugging_memory_usage Debugging incorrect memory usage + +If you suspect a bug with memory usage, like usage of uninitialized memory or +memory being overwritten out of bounds of an allocation, +you can use debug features of this library to verify this. + +\section debugging_memory_usage_initialization Memory initialization + +If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used, +you can enable automatic memory initialization to verify this. +To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1. + +\code +#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 +#include "vk_mem_alloc.h" +\endcode + +It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`. +Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`. +Memory is automatically mapped and unmapped if necessary. + +If you find these values while debugging your program, good chances are that you incorrectly +read Vulkan memory that is allocated but not initialized, or already freed, respectively. + +Memory initialization works only with memory types that are `HOST_VISIBLE`. +It works also with dedicated allocations. + +\section debugging_memory_usage_margins Margins + +By default, allocations are laid out in memory blocks next to each other if possible +(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`). + +![Allocations without margin](../gfx/Margins_1.png) + +Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified +number of bytes as a margin before and after every allocation. + +\code +#define VMA_DEBUG_MARGIN 16 +#include "vk_mem_alloc.h" +\endcode + +![Allocations with margin](../gfx/Margins_2.png) + +If your bug goes away after enabling margins, it means it may be caused by memory +being overwritten outside of allocation boundaries. It is not 100% certain though. +Change in application behavior may also be caused by different order and distribution +of allocations across memory blocks after margins are applied. + +The margin is applied also before first and after last allocation in a block. +It may occur only once between two adjacent allocations. + +Margins work with all types of memory. + +Margin is applied only to allocations made out of memory blocks and not to dedicated +allocations, which have their own memory block of specific size. +It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag +or those automatically decided to put into dedicated allocations, e.g. due to its +large size or recommended by VK_KHR_dedicated_allocation extension. +Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag. + +Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space. + +Note that enabling margins increases memory usage and fragmentation. + +\section debugging_memory_usage_corruption_detection Corruption detection + +You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation +of contents of the margins. + +\code +#define VMA_DEBUG_MARGIN 16 +#define VMA_DEBUG_DETECT_CORRUPTION 1 +#include "vk_mem_alloc.h" +\endcode + +When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN` +(it must be multiply of 4) before and after every allocation is filled with a magic number. +This idea is also know as "canary". +Memory is automatically mapped and unmapped if necessary. + +This number is validated automatically when the allocation is destroyed. +If it is not equal to the expected value, `VMA_ASSERT()` is executed. +It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation, +which indicates a serious bug. + +You can also explicitly request checking margins of all allocations in all memory blocks +that belong to specified memory types by using function vmaCheckCorruption(), +or in memory blocks that belong to specified custom pool, by using function +vmaCheckPoolCorruption(). + +Margin validation (corruption detection) works only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. + + +\page opengl_interop OpenGL Interop + +VMA provides some features that help with interoperability with OpenGL. + +\section opengl_interop_exporting_memory Exporting memory + +If you want to attach `VkExportMemoryAllocateInfoKHR` structure to `pNext` chain of memory allocations made by the library: + +It is recommended to create \ref custom_memory_pools for such allocations. +Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext +while creating the custom pool. +Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool, +not only while creating it, as no copy of the structure is made, +but its original pointer is used for each allocation instead. + +If you want to export all memory allocated by the library from certain memory types, +also dedicated allocations or other allocations made from default pools, +an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes. +It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library +through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type. +Please note that new versions of the library also support dedicated allocations created in custom pools. + +You should not mix these two methods in a way that allows to apply both to the same memory type. +Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`. + + +\section opengl_interop_custom_alignment Custom alignment + +Buffers or images exported to a different API like OpenGL may require a different alignment, +higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`. +To impose such alignment: + +It is recommended to create \ref custom_memory_pools for such allocations. +Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation +to be made out of this pool. +The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image +from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically. + +If you want to create a buffer with a specific minimum alignment out of default pools, +use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`. + +Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated +allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block. +Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation. + + +\page usage_patterns Recommended usage patterns + +See also slides from talk: +[Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New) + + +\section usage_patterns_common_mistakes Common mistakes + +Use of CPU_TO_GPU instead of CPU_ONLY memory + +#VMA_MEMORY_USAGE_CPU_TO_GPU is recommended only for resources that will be +mapped and written by the CPU, as well as read directly by the GPU - like some +buffers or textures updated every frame (dynamic). If you create a staging copy +of a resource to be written by CPU and then used as a source of transfer to +another resource placed in the GPU memory, that staging resource should be +created with #VMA_MEMORY_USAGE_CPU_ONLY. Please read the descriptions of these +enums carefully for details. + +Unnecessary use of custom pools + +\ref custom_memory_pools may be useful for special purposes - when you want to +keep certain type of resources separate e.g. to reserve minimum amount of memory +for them or limit maximum amount of memory they can occupy. For most +resources this is not needed and so it is not recommended to create #VmaPool +objects and allocations out of them. Allocating from the default pool is sufficient. + +\section usage_patterns_simple Simple patterns + +\subsection usage_patterns_simple_render_targets Render targets + +When: +Any resources that you frequently write and read on GPU, +e.g. images used as color attachments (aka "render targets"), depth-stencil attachments, +images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)"). + +What to do: +Create them in video memory that is fastest to access from GPU using +#VMA_MEMORY_USAGE_GPU_ONLY. + +Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension +and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, +especially if they are large or if you plan to destroy and recreate them e.g. when +display resolution changes. +Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later. + +\subsection usage_patterns_simple_immutable_resources Immutable resources + +When: +Any resources that you fill on CPU only once (aka "immutable") or infrequently +and then read frequently on GPU, +e.g. textures, vertex and index buffers, constant buffers that don't change often. + +What to do: +Create them in video memory that is fastest to access from GPU using +#VMA_MEMORY_USAGE_GPU_ONLY. + +To initialize content of such resource, create a CPU-side (aka "staging") copy of it +in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it, +and submit a transfer from it to the GPU resource. +You can keep the staging copy if you need it for another upload transfer in the future. +If you don't, you can destroy it or reuse this buffer for uploading different resource +after the transfer finishes. + +Prefer to create just buffers in system memory rather than images, even for uploading textures. +Use `vkCmdCopyBufferToImage()`. +Dont use images with `VK_IMAGE_TILING_LINEAR`. + +\subsection usage_patterns_dynamic_resources Dynamic resources + +When: +Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call, +written on CPU, read on GPU. + +What to do: +Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU. +You can map it and write to it directly on CPU, as well as read from it on GPU. + +This is a more complex situation. Different solutions are possible, +and the best one depends on specific GPU type, but you can use this simple approach for the start. +Prefer to write to such resource sequentially (e.g. using `memcpy`). +Don't perform random access or any reads from it on CPU, as it may be very slow. +Also note that textures written directly from the host through a mapped pointer need to be in LINEAR not OPTIMAL layout. + +\subsection usage_patterns_readback Readback + +When: +Resources that contain data written by GPU that you want to read back on CPU, +e.g. results of some computations. + +What to do: +Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU. +You can write to them directly on GPU, as well as map and read them on CPU. + +\section usage_patterns_advanced Advanced patterns + +\subsection usage_patterns_integrated_graphics Detecting integrated graphics + +You can support integrated graphics (like Intel HD Graphics, AMD APU) better +by detecting it in Vulkan. +To do it, call `vkGetPhysicalDeviceProperties()`, inspect +`VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`. +When you find it, you can assume that memory is unified and all memory types are comparably fast +to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + +You can then sum up sizes of all available memory heaps and treat them as useful for +your GPU resources, instead of only `DEVICE_LOCAL` ones. +You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them +directly instead of submitting explicit transfer (see below). + +\subsection usage_patterns_direct_vs_transfer Direct access versus transfer + +For resources that you frequently write on CPU and read on GPU, many solutions are possible: + +-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY, + second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit transfer each time. +-# Create just a single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU, + read it directly on GPU. +-# Create just a single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU, + read it directly on GPU. + +Which solution is the most efficient depends on your resource and especially on the GPU. +It is best to measure it and then make the decision. +Some general recommendations: + +- On integrated graphics use (2) or (3) to avoid unnecessary time and memory overhead + related to using a second copy and making transfer. +- For small resources (e.g. constant buffers) use (2). + Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable. + Even if the resource ends up in system memory, its data may be cached on GPU after first + fetch over PCIe bus. +- For larger resources (e.g. textures), decide between (1) and (2). + You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is + both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1). + +Similarly, for resources that you frequently write on GPU and read on CPU, multiple +solutions are possible: + +-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY, + second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time. +-# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU, + map it and read it on CPU. + +You should take some measurements to decide which option is faster in case of your specific +resource. + +Note that textures accessed directly from the host through a mapped pointer need to be in LINEAR layout, +which may slow down their usage on the device. +Textures accessed only by the device and transfer operations can use OPTIMAL layout. + +If you don't want to specialize your code for specific types of GPUs, you can still make +an simple optimization for cases when your resource ends up in mappable memory to use it +directly in this case instead of creating CPU-side staging copy. +For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable). + + +\page configuration Configuration + +Please check "CONFIGURATION SECTION" in the code to find macros that you can define +before each include of this file or change directly in this file to provide +your own implementation of basic facilities like assert, `min()` and `max()` functions, +mutex, atomic etc. +The library uses its own implementation of containers by default, but you can switch to using +STL containers instead. + +For example, define `VMA_ASSERT(expr)` before including the library to provide +custom implementation of the assertion, compatible with your project. +By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration +and empty otherwise. + +\section config_Vulkan_functions Pointers to Vulkan functions + +There are multiple ways to import pointers to Vulkan functions in the library. +In the simplest case you don't need to do anything. +If the compilation or linking of your program or the initialization of the #VmaAllocator +doesn't work for you, you can try to reconfigure it. + +First, the allocator tries to fetch pointers to Vulkan functions linked statically, +like this: + +\code +m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory; +\endcode + +If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`. + +Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions. +You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or +by using a helper library like [volk](https://github.com/zeux/volk). + +Third, VMA tries to fetch remaining pointers that are still null by calling +`vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own. +If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`. + +Finally, all the function pointers required by the library (considering selected +Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null. + + +\section custom_memory_allocator Custom host memory allocator + +If you use custom allocator for CPU memory rather than default operator `new` +and `delete` from C++, you can make this library using your allocator as well +by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These +functions will be passed to Vulkan, as well as used by the library itself to +make any CPU-side allocations. + +\section allocation_callbacks Device memory allocation callbacks + +The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally. +You can setup callbacks to be informed about these calls, e.g. for the purpose +of gathering some statistics. To do it, fill optional member +VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. + +\section heap_memory_limit Device heap memory limit + +When device memory of certain heap runs out of free space, new allocations may +fail (returning error code) or they may succeed, silently pushing some existing +memory blocks from GPU VRAM to system RAM (which degrades performance). This +behavior is implementation-dependent - it depends on GPU vendor and graphics +driver. + +On AMD cards it can be controlled while creating Vulkan device object by using +VK_AMD_memory_overallocation_behavior extension, if available. + +Alternatively, if you want to test how your program behaves with limited amount of Vulkan device +memory available without switching your graphics card to one that really has +smaller VRAM, you can use a feature of this library intended for this purpose. +To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit. + + + +\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation + +VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve +performance on some GPUs. It augments Vulkan API with possibility to query +driver whether it prefers particular buffer or image to have its own, dedicated +allocation (separate `VkDeviceMemory` block) for better efficiency - to be able +to do some internal optimizations. + +The extension is supported by this library. It will be used automatically when +enabled. To enable it: + +1 . When creating Vulkan device, check if following 2 device extensions are +supported (call `vkEnumerateDeviceExtensionProperties()`). +If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`). + +- VK_KHR_get_memory_requirements2 +- VK_KHR_dedicated_allocation + +If you enabled these extensions: + +2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating +your #VmaAllocator`to inform the library that you enabled required extensions +and you want the library to use them. + +\code +allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; + +vmaCreateAllocator(&allocatorInfo, &allocator); +\endcode + +That is all. The extension will be automatically used whenever you create a +buffer using vmaCreateBuffer() or image using vmaCreateImage(). + +When using the extension together with Vulkan Validation Layer, you will receive +warnings like this: + + vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer. + +It is OK, you should just ignore it. It happens because you use function +`vkGetBufferMemoryRequirements2KHR()` instead of standard +`vkGetBufferMemoryRequirements()`, while the validation layer seems to be +unaware of it. + +To learn more about this extension, see: + +- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation) +- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5) + + + +\page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory + +VK_AMD_device_coherent_memory is a device extension that enables access to +additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and +`VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for +allocation of buffers intended for writing "breadcrumb markers" in between passes +or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases. + +When the extension is available but has not been enabled, Vulkan physical device +still exposes those memory types, but their usage is forbidden. VMA automatically +takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt +to allocate memory of such type is made. + +If you want to use this extension in connection with VMA, follow these steps: + +\section vk_amd_device_coherent_memory_initialization Initialization + +1) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true. + +3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory" +to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to +`VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT +to VmaAllocatorCreateInfo::flags. + +\section vk_amd_device_coherent_memory_usage Usage + +After following steps described above, you can create VMA allocations and custom pools +out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible +devices. There are multiple ways to do it, for example: + +- You can request or prefer to allocate out of such memory types by adding + `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags + or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with + other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage. +- If you manually found memory type index to use for this purpose, force allocation + from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`. + +\section vk_amd_device_coherent_memory_more_information More information + +To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html) + +Example use of this extension can be found in the code of the sample and test suite +accompanying this library. + + +\page enabling_buffer_device_address Enabling buffer device address + +Device extension VK_KHR_buffer_device_address +allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code. +It is promoted to core Vulkan 1.2. + +If you want to use this feature in connection with VMA, follow these steps: + +\section enabling_buffer_device_address_initialization Initialization + +1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains +"VK_KHR_buffer_device_address". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true. + +3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add +"VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to +`VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT +to VmaAllocatorCreateInfo::flags. + +\section enabling_buffer_device_address_usage Usage + +After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA. +The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to +allocated memory blocks wherever it might be needed. + +Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`. +The second part of this functionality related to "capture and replay" is not supported, +as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage. + +\section enabling_buffer_device_address_more_information More information + +To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address) + +Example use of this extension can be found in the code of the sample and test suite +accompanying this library. + +\page general_considerations General considerations + +\section general_considerations_thread_safety Thread safety + +- The library has no global state, so separate #VmaAllocator objects can be used + independently. + There should be no need to create multiple such objects though - one per `VkDevice` is enough. +- By default, all calls to functions that take #VmaAllocator as first parameter + are safe to call from multiple threads simultaneously because they are + synchronized internally when needed. + This includes allocation and deallocation from default memory pool, as well as custom #VmaPool. +- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT + flag, calls to functions that take such #VmaAllocator object must be + synchronized externally. +- Access to a #VmaAllocation object must be externally synchronized. For example, + you must not call vmaGetAllocationInfo() and vmaMapMemory() from different + threads at the same time if you pass the same #VmaAllocation object to these + functions. +- #VmaVirtualBlock is also not safe to be used from multiple threads simultaneously. + +\section general_considerations_validation_layer_warnings Validation layer warnings + +When using this library, you can meet following types of warnings issued by +Vulkan validation layer. They don't necessarily indicate a bug, so you may need +to just ignore them. + +- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.* + - It happens when VK_KHR_dedicated_allocation extension is enabled. + `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it. +- *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.* + - It happens when you map a buffer or image, because the library maps entire + `VkDeviceMemory` block, where different types of images and buffers may end + up together, especially on GPUs with unified memory like Intel. +- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.* + - It may happen when you use [defragmentation](@ref defragmentation). + +\section general_considerations_allocation_algorithm Allocation algorithm + +The library uses following algorithm for allocation, in order: + +-# Try to find free range of memory in existing blocks. +-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size. +-# If failed, try to create such block with size/2, size/4, size/8. +-# If failed, try to allocate separate `VkDeviceMemory` for this allocation, + just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +-# If failed, choose other memory type that meets the requirements specified in + VmaAllocationCreateInfo and go to point 1. +-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + +\section general_considerations_features_not_supported Features not supported + +Features deliberately excluded from the scope of this library: + +- **Data transfer.** Uploading (streaming) and downloading data of buffers and images + between CPU and GPU memory and related synchronization is responsibility of the user. + Defining some "texture" object that would automatically stream its data from a + staging copy in CPU memory to GPU memory would rather be a feature of another, + higher-level library implemented on top of VMA. +- **Recreation of buffers and images.** Although the library has functions for + buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to + recreate these objects yourself after defragmentation. That is because the big + structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in + #VmaAllocation object. +- **Handling CPU memory allocation failures.** When dynamically creating small C++ + objects in CPU memory (not Vulkan memory), allocation failures are not checked + and handled gracefully, because that would complicate code significantly and + is usually not needed in desktop PC applications anyway. + Success of an allocation is just checked with an assert. +- **Code free of any compiler warnings.** Maintaining the library to compile and + work correctly on so many different platforms is hard enough. Being free of + any warnings, on any version of any compiler, is simply not feasible. + There are many preprocessor macros that make some variables unused, function parameters unreferenced, + or conditional expressions constant in some configurations. + The code of this library should not be bigger or more complicated just to silence these warnings. + It is recommended to disable such warnings instead. +- This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but + are not going to be included into this repository. +*/ diff --git a/third_party/vulkan/vk_platform.h b/third_party/vulkan/vk_platform.h index 728929924..18b913abc 100644 --- a/third_party/vulkan/vk_platform.h +++ b/third_party/vulkan/vk_platform.h @@ -2,19 +2,9 @@ // File: vk_platform.h // /* -** Copyright (c) 2014-2017 The Khronos Group Inc. +** Copyright 2014-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ @@ -68,7 +58,9 @@ extern "C" #define VKAPI_PTR #endif -#include +#if !defined(VK_NO_STDDEF_H) + #include +#endif // !defined(VK_NO_STDDEF_H) #if !defined(VK_NO_STDINT_H) #if defined(_MSC_VER) && (_MSC_VER < 1600) diff --git a/third_party/vulkan/vulkan.h b/third_party/vulkan/vulkan.h index d05c8490a..3f7cdba58 100644 --- a/third_party/vulkan/vulkan.h +++ b/third_party/vulkan/vulkan.h @@ -2,19 +2,9 @@ #define VULKAN_H_ 1 /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ #include "vk_platform.h" @@ -24,6 +14,10 @@ #include "vulkan_android.h" #endif +#ifdef VK_USE_PLATFORM_FUCHSIA +#include +#include "vulkan_fuchsia.h" +#endif #ifdef VK_USE_PLATFORM_IOS_MVK #include "vulkan_ios.h" @@ -34,13 +28,10 @@ #include "vulkan_macos.h" #endif - -#ifdef VK_USE_PLATFORM_MIR_KHR -#include -#include "vulkan_mir.h" +#ifdef VK_USE_PLATFORM_METAL_EXT +#include "vulkan_metal.h" #endif - #ifdef VK_USE_PLATFORM_VI_NN #include "vulkan_vi.h" #endif @@ -70,10 +61,32 @@ #endif +#ifdef VK_USE_PLATFORM_DIRECTFB_EXT +#include +#include "vulkan_directfb.h" +#endif + + #ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT #include #include #include "vulkan_xlib_xrandr.h" #endif + +#ifdef VK_USE_PLATFORM_GGP +#include +#include "vulkan_ggp.h" +#endif + + +#ifdef VK_USE_PLATFORM_SCREEN_QNX +#include +#include "vulkan_screen.h" +#endif + +#ifdef VK_ENABLE_BETA_EXTENSIONS +#include "vulkan_beta.h" +#endif + #endif // VULKAN_H_ diff --git a/third_party/vulkan/vulkan_android.h b/third_party/vulkan/vulkan_android.h index 07aaeda28..a8a830673 100644 --- a/third_party/vulkan/vulkan_android.h +++ b/third_party/vulkan/vulkan_android.h @@ -1,24 +1,10 @@ #ifndef VULKAN_ANDROID_H_ #define VULKAN_ANDROID_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,14 +13,17 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_KHR_android_surface 1 struct ANativeWindow; - #define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6 #define VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "VK_KHR_android_surface" - typedef VkFlags VkAndroidSurfaceCreateFlagsKHR; - typedef struct VkAndroidSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; @@ -42,7 +31,6 @@ typedef struct VkAndroidSurfaceCreateInfoKHR { struct ANativeWindow* window; } VkAndroidSurfaceCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateAndroidSurfaceKHR)(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES @@ -53,12 +41,11 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateAndroidSurfaceKHR( VkSurfaceKHR* pSurface); #endif + #define VK_ANDROID_external_memory_android_hardware_buffer 1 struct AHardwareBuffer; - -#define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION 3 +#define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION 4 #define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME "VK_ANDROID_external_memory_android_hardware_buffer" - typedef struct VkAndroidHardwareBufferUsageANDROID { VkStructureType sType; void* pNext; @@ -103,6 +90,18 @@ typedef struct VkExternalFormatANDROID { uint64_t externalFormat; } VkExternalFormatANDROID; +typedef struct VkAndroidHardwareBufferFormatProperties2ANDROID { + VkStructureType sType; + void* pNext; + VkFormat format; + uint64_t externalFormat; + VkFormatFeatureFlags2KHR formatFeatures; + VkComponentMapping samplerYcbcrConversionComponents; + VkSamplerYcbcrModelConversion suggestedYcbcrModel; + VkSamplerYcbcrRange suggestedYcbcrRange; + VkChromaLocation suggestedXChromaOffset; + VkChromaLocation suggestedYChromaOffset; +} VkAndroidHardwareBufferFormatProperties2ANDROID; typedef VkResult (VKAPI_PTR *PFN_vkGetAndroidHardwareBufferPropertiesANDROID)(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryAndroidHardwareBufferANDROID)(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer); diff --git a/third_party/vulkan/vulkan_beta.h b/third_party/vulkan/vulkan_beta.h new file mode 100644 index 000000000..d2f34d1cd --- /dev/null +++ b/third_party/vulkan/vulkan_beta.h @@ -0,0 +1,833 @@ +#ifndef VULKAN_BETA_H_ +#define VULKAN_BETA_H_ 1 + +/* +** Copyright 2015-2021 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_KHR_video_queue 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkVideoSessionKHR) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkVideoSessionParametersKHR) +#define VK_KHR_VIDEO_QUEUE_SPEC_VERSION 2 +#define VK_KHR_VIDEO_QUEUE_EXTENSION_NAME "VK_KHR_video_queue" + +typedef enum VkQueryResultStatusKHR { + VK_QUERY_RESULT_STATUS_ERROR_KHR = -1, + VK_QUERY_RESULT_STATUS_NOT_READY_KHR = 0, + VK_QUERY_RESULT_STATUS_COMPLETE_KHR = 1, + VK_QUERY_RESULT_STATUS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkQueryResultStatusKHR; + +typedef enum VkVideoCodecOperationFlagBitsKHR { + VK_VIDEO_CODEC_OPERATION_INVALID_BIT_KHR = 0, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_EXT = 0x00010000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_EXT = 0x00020000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_EXT = 0x00000001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_EXT = 0x00000002, +#endif + VK_VIDEO_CODEC_OPERATION_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoCodecOperationFlagBitsKHR; +typedef VkFlags VkVideoCodecOperationFlagsKHR; + +typedef enum VkVideoChromaSubsamplingFlagBitsKHR { + VK_VIDEO_CHROMA_SUBSAMPLING_INVALID_BIT_KHR = 0, + VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR = 0x00000001, + VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR = 0x00000002, + VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR = 0x00000004, + VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR = 0x00000008, + VK_VIDEO_CHROMA_SUBSAMPLING_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoChromaSubsamplingFlagBitsKHR; +typedef VkFlags VkVideoChromaSubsamplingFlagsKHR; + +typedef enum VkVideoComponentBitDepthFlagBitsKHR { + VK_VIDEO_COMPONENT_BIT_DEPTH_INVALID_KHR = 0, + VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR = 0x00000001, + VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR = 0x00000004, + VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR = 0x00000010, + VK_VIDEO_COMPONENT_BIT_DEPTH_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoComponentBitDepthFlagBitsKHR; +typedef VkFlags VkVideoComponentBitDepthFlagsKHR; + +typedef enum VkVideoCapabilityFlagBitsKHR { + VK_VIDEO_CAPABILITY_PROTECTED_CONTENT_BIT_KHR = 0x00000001, + VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR = 0x00000002, + VK_VIDEO_CAPABILITY_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoCapabilityFlagBitsKHR; +typedef VkFlags VkVideoCapabilityFlagsKHR; + +typedef enum VkVideoSessionCreateFlagBitsKHR { + VK_VIDEO_SESSION_CREATE_DEFAULT_KHR = 0, + VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR = 0x00000001, + VK_VIDEO_SESSION_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoSessionCreateFlagBitsKHR; +typedef VkFlags VkVideoSessionCreateFlagsKHR; +typedef VkFlags VkVideoBeginCodingFlagsKHR; +typedef VkFlags VkVideoEndCodingFlagsKHR; + +typedef enum VkVideoCodingControlFlagBitsKHR { + VK_VIDEO_CODING_CONTROL_DEFAULT_KHR = 0, + VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR = 0x00000001, + VK_VIDEO_CODING_CONTROL_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoCodingControlFlagBitsKHR; +typedef VkFlags VkVideoCodingControlFlagsKHR; + +typedef enum VkVideoCodingQualityPresetFlagBitsKHR { + VK_VIDEO_CODING_QUALITY_PRESET_NORMAL_BIT_KHR = 0x00000001, + VK_VIDEO_CODING_QUALITY_PRESET_POWER_BIT_KHR = 0x00000002, + VK_VIDEO_CODING_QUALITY_PRESET_QUALITY_BIT_KHR = 0x00000004, + VK_VIDEO_CODING_QUALITY_PRESET_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoCodingQualityPresetFlagBitsKHR; +typedef VkFlags VkVideoCodingQualityPresetFlagsKHR; +typedef struct VkVideoQueueFamilyProperties2KHR { + VkStructureType sType; + void* pNext; + VkVideoCodecOperationFlagsKHR videoCodecOperations; +} VkVideoQueueFamilyProperties2KHR; + +typedef struct VkVideoProfileKHR { + VkStructureType sType; + void* pNext; + VkVideoCodecOperationFlagBitsKHR videoCodecOperation; + VkVideoChromaSubsamplingFlagsKHR chromaSubsampling; + VkVideoComponentBitDepthFlagsKHR lumaBitDepth; + VkVideoComponentBitDepthFlagsKHR chromaBitDepth; +} VkVideoProfileKHR; + +typedef struct VkVideoProfilesKHR { + VkStructureType sType; + void* pNext; + uint32_t profileCount; + const VkVideoProfileKHR* pProfiles; +} VkVideoProfilesKHR; + +typedef struct VkVideoCapabilitiesKHR { + VkStructureType sType; + void* pNext; + VkVideoCapabilityFlagsKHR capabilityFlags; + VkDeviceSize minBitstreamBufferOffsetAlignment; + VkDeviceSize minBitstreamBufferSizeAlignment; + VkExtent2D videoPictureExtentGranularity; + VkExtent2D minExtent; + VkExtent2D maxExtent; + uint32_t maxReferencePicturesSlotsCount; + uint32_t maxReferencePicturesActiveCount; +} VkVideoCapabilitiesKHR; + +typedef struct VkPhysicalDeviceVideoFormatInfoKHR { + VkStructureType sType; + void* pNext; + VkImageUsageFlags imageUsage; + const VkVideoProfilesKHR* pVideoProfiles; +} VkPhysicalDeviceVideoFormatInfoKHR; + +typedef struct VkVideoFormatPropertiesKHR { + VkStructureType sType; + void* pNext; + VkFormat format; +} VkVideoFormatPropertiesKHR; + +typedef struct VkVideoPictureResourceKHR { + VkStructureType sType; + const void* pNext; + VkOffset2D codedOffset; + VkExtent2D codedExtent; + uint32_t baseArrayLayer; + VkImageView imageViewBinding; +} VkVideoPictureResourceKHR; + +typedef struct VkVideoReferenceSlotKHR { + VkStructureType sType; + const void* pNext; + int8_t slotIndex; + const VkVideoPictureResourceKHR* pPictureResource; +} VkVideoReferenceSlotKHR; + +typedef struct VkVideoGetMemoryPropertiesKHR { + VkStructureType sType; + const void* pNext; + uint32_t memoryBindIndex; + VkMemoryRequirements2* pMemoryRequirements; +} VkVideoGetMemoryPropertiesKHR; + +typedef struct VkVideoBindMemoryKHR { + VkStructureType sType; + const void* pNext; + uint32_t memoryBindIndex; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + VkDeviceSize memorySize; +} VkVideoBindMemoryKHR; + +typedef struct VkVideoSessionCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t queueFamilyIndex; + VkVideoSessionCreateFlagsKHR flags; + const VkVideoProfileKHR* pVideoProfile; + VkFormat pictureFormat; + VkExtent2D maxCodedExtent; + VkFormat referencePicturesFormat; + uint32_t maxReferencePicturesSlotsCount; + uint32_t maxReferencePicturesActiveCount; +} VkVideoSessionCreateInfoKHR; + +typedef struct VkVideoSessionParametersCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkVideoSessionParametersKHR videoSessionParametersTemplate; + VkVideoSessionKHR videoSession; +} VkVideoSessionParametersCreateInfoKHR; + +typedef struct VkVideoSessionParametersUpdateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t updateSequenceCount; +} VkVideoSessionParametersUpdateInfoKHR; + +typedef struct VkVideoBeginCodingInfoKHR { + VkStructureType sType; + const void* pNext; + VkVideoBeginCodingFlagsKHR flags; + VkVideoCodingQualityPresetFlagsKHR codecQualityPreset; + VkVideoSessionKHR videoSession; + VkVideoSessionParametersKHR videoSessionParameters; + uint32_t referenceSlotCount; + const VkVideoReferenceSlotKHR* pReferenceSlots; +} VkVideoBeginCodingInfoKHR; + +typedef struct VkVideoEndCodingInfoKHR { + VkStructureType sType; + const void* pNext; + VkVideoEndCodingFlagsKHR flags; +} VkVideoEndCodingInfoKHR; + +typedef struct VkVideoCodingControlInfoKHR { + VkStructureType sType; + const void* pNext; + VkVideoCodingControlFlagsKHR flags; +} VkVideoCodingControlInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceVideoCapabilitiesKHR)(VkPhysicalDevice physicalDevice, const VkVideoProfileKHR* pVideoProfile, VkVideoCapabilitiesKHR* pCapabilities); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceVideoFormatPropertiesKHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceVideoFormatInfoKHR* pVideoFormatInfo, uint32_t* pVideoFormatPropertyCount, VkVideoFormatPropertiesKHR* pVideoFormatProperties); +typedef VkResult (VKAPI_PTR *PFN_vkCreateVideoSessionKHR)(VkDevice device, const VkVideoSessionCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkVideoSessionKHR* pVideoSession); +typedef void (VKAPI_PTR *PFN_vkDestroyVideoSessionKHR)(VkDevice device, VkVideoSessionKHR videoSession, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetVideoSessionMemoryRequirementsKHR)(VkDevice device, VkVideoSessionKHR videoSession, uint32_t* pVideoSessionMemoryRequirementsCount, VkVideoGetMemoryPropertiesKHR* pVideoSessionMemoryRequirements); +typedef VkResult (VKAPI_PTR *PFN_vkBindVideoSessionMemoryKHR)(VkDevice device, VkVideoSessionKHR videoSession, uint32_t videoSessionBindMemoryCount, const VkVideoBindMemoryKHR* pVideoSessionBindMemories); +typedef VkResult (VKAPI_PTR *PFN_vkCreateVideoSessionParametersKHR)(VkDevice device, const VkVideoSessionParametersCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkVideoSessionParametersKHR* pVideoSessionParameters); +typedef VkResult (VKAPI_PTR *PFN_vkUpdateVideoSessionParametersKHR)(VkDevice device, VkVideoSessionParametersKHR videoSessionParameters, const VkVideoSessionParametersUpdateInfoKHR* pUpdateInfo); +typedef void (VKAPI_PTR *PFN_vkDestroyVideoSessionParametersKHR)(VkDevice device, VkVideoSessionParametersKHR videoSessionParameters, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkCmdBeginVideoCodingKHR)(VkCommandBuffer commandBuffer, const VkVideoBeginCodingInfoKHR* pBeginInfo); +typedef void (VKAPI_PTR *PFN_vkCmdEndVideoCodingKHR)(VkCommandBuffer commandBuffer, const VkVideoEndCodingInfoKHR* pEndCodingInfo); +typedef void (VKAPI_PTR *PFN_vkCmdControlVideoCodingKHR)(VkCommandBuffer commandBuffer, const VkVideoCodingControlInfoKHR* pCodingControlInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceVideoCapabilitiesKHR( + VkPhysicalDevice physicalDevice, + const VkVideoProfileKHR* pVideoProfile, + VkVideoCapabilitiesKHR* pCapabilities); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceVideoFormatPropertiesKHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceVideoFormatInfoKHR* pVideoFormatInfo, + uint32_t* pVideoFormatPropertyCount, + VkVideoFormatPropertiesKHR* pVideoFormatProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateVideoSessionKHR( + VkDevice device, + const VkVideoSessionCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkVideoSessionKHR* pVideoSession); + +VKAPI_ATTR void VKAPI_CALL vkDestroyVideoSessionKHR( + VkDevice device, + VkVideoSessionKHR videoSession, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetVideoSessionMemoryRequirementsKHR( + VkDevice device, + VkVideoSessionKHR videoSession, + uint32_t* pVideoSessionMemoryRequirementsCount, + VkVideoGetMemoryPropertiesKHR* pVideoSessionMemoryRequirements); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindVideoSessionMemoryKHR( + VkDevice device, + VkVideoSessionKHR videoSession, + uint32_t videoSessionBindMemoryCount, + const VkVideoBindMemoryKHR* pVideoSessionBindMemories); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateVideoSessionParametersKHR( + VkDevice device, + const VkVideoSessionParametersCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkVideoSessionParametersKHR* pVideoSessionParameters); + +VKAPI_ATTR VkResult VKAPI_CALL vkUpdateVideoSessionParametersKHR( + VkDevice device, + VkVideoSessionParametersKHR videoSessionParameters, + const VkVideoSessionParametersUpdateInfoKHR* pUpdateInfo); + +VKAPI_ATTR void VKAPI_CALL vkDestroyVideoSessionParametersKHR( + VkDevice device, + VkVideoSessionParametersKHR videoSessionParameters, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginVideoCodingKHR( + VkCommandBuffer commandBuffer, + const VkVideoBeginCodingInfoKHR* pBeginInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndVideoCodingKHR( + VkCommandBuffer commandBuffer, + const VkVideoEndCodingInfoKHR* pEndCodingInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdControlVideoCodingKHR( + VkCommandBuffer commandBuffer, + const VkVideoCodingControlInfoKHR* pCodingControlInfo); +#endif + + +#define VK_KHR_video_decode_queue 1 +#define VK_KHR_VIDEO_DECODE_QUEUE_SPEC_VERSION 2 +#define VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME "VK_KHR_video_decode_queue" + +typedef enum VkVideoDecodeFlagBitsKHR { + VK_VIDEO_DECODE_DEFAULT_KHR = 0, + VK_VIDEO_DECODE_RESERVED_0_BIT_KHR = 0x00000001, + VK_VIDEO_DECODE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoDecodeFlagBitsKHR; +typedef VkFlags VkVideoDecodeFlagsKHR; +typedef struct VkVideoDecodeInfoKHR { + VkStructureType sType; + const void* pNext; + VkVideoDecodeFlagsKHR flags; + VkOffset2D codedOffset; + VkExtent2D codedExtent; + VkBuffer srcBuffer; + VkDeviceSize srcBufferOffset; + VkDeviceSize srcBufferRange; + VkVideoPictureResourceKHR dstPictureResource; + const VkVideoReferenceSlotKHR* pSetupReferenceSlot; + uint32_t referenceSlotCount; + const VkVideoReferenceSlotKHR* pReferenceSlots; +} VkVideoDecodeInfoKHR; + +typedef void (VKAPI_PTR *PFN_vkCmdDecodeVideoKHR)(VkCommandBuffer commandBuffer, const VkVideoDecodeInfoKHR* pFrameInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDecodeVideoKHR( + VkCommandBuffer commandBuffer, + const VkVideoDecodeInfoKHR* pFrameInfo); +#endif + + +#define VK_KHR_portability_subset 1 +#define VK_KHR_PORTABILITY_SUBSET_SPEC_VERSION 1 +#define VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME "VK_KHR_portability_subset" +typedef struct VkPhysicalDevicePortabilitySubsetFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 constantAlphaColorBlendFactors; + VkBool32 events; + VkBool32 imageViewFormatReinterpretation; + VkBool32 imageViewFormatSwizzle; + VkBool32 imageView2DOn3DImage; + VkBool32 multisampleArrayImage; + VkBool32 mutableComparisonSamplers; + VkBool32 pointPolygons; + VkBool32 samplerMipLodBias; + VkBool32 separateStencilMaskRef; + VkBool32 shaderSampleRateInterpolationFunctions; + VkBool32 tessellationIsolines; + VkBool32 tessellationPointMode; + VkBool32 triangleFans; + VkBool32 vertexAttributeAccessBeyondStride; +} VkPhysicalDevicePortabilitySubsetFeaturesKHR; + +typedef struct VkPhysicalDevicePortabilitySubsetPropertiesKHR { + VkStructureType sType; + void* pNext; + uint32_t minVertexInputBindingStrideAlignment; +} VkPhysicalDevicePortabilitySubsetPropertiesKHR; + + + +#define VK_KHR_video_encode_queue 1 +#define VK_KHR_VIDEO_ENCODE_QUEUE_SPEC_VERSION 3 +#define VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME "VK_KHR_video_encode_queue" + +typedef enum VkVideoEncodeFlagBitsKHR { + VK_VIDEO_ENCODE_DEFAULT_KHR = 0, + VK_VIDEO_ENCODE_RESERVED_0_BIT_KHR = 0x00000001, + VK_VIDEO_ENCODE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoEncodeFlagBitsKHR; +typedef VkFlags VkVideoEncodeFlagsKHR; + +typedef enum VkVideoEncodeRateControlFlagBitsKHR { + VK_VIDEO_ENCODE_RATE_CONTROL_DEFAULT_KHR = 0, + VK_VIDEO_ENCODE_RATE_CONTROL_RESET_BIT_KHR = 0x00000001, + VK_VIDEO_ENCODE_RATE_CONTROL_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoEncodeRateControlFlagBitsKHR; +typedef VkFlags VkVideoEncodeRateControlFlagsKHR; + +typedef enum VkVideoEncodeRateControlModeFlagBitsKHR { + VK_VIDEO_ENCODE_RATE_CONTROL_MODE_NONE_BIT_KHR = 0, + VK_VIDEO_ENCODE_RATE_CONTROL_MODE_CBR_BIT_KHR = 1, + VK_VIDEO_ENCODE_RATE_CONTROL_MODE_VBR_BIT_KHR = 2, + VK_VIDEO_ENCODE_RATE_CONTROL_MODE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkVideoEncodeRateControlModeFlagBitsKHR; +typedef VkFlags VkVideoEncodeRateControlModeFlagsKHR; +typedef struct VkVideoEncodeInfoKHR { + VkStructureType sType; + const void* pNext; + VkVideoEncodeFlagsKHR flags; + uint32_t qualityLevel; + VkExtent2D codedExtent; + VkBuffer dstBitstreamBuffer; + VkDeviceSize dstBitstreamBufferOffset; + VkDeviceSize dstBitstreamBufferMaxRange; + VkVideoPictureResourceKHR srcPictureResource; + const VkVideoReferenceSlotKHR* pSetupReferenceSlot; + uint32_t referenceSlotCount; + const VkVideoReferenceSlotKHR* pReferenceSlots; +} VkVideoEncodeInfoKHR; + +typedef struct VkVideoEncodeRateControlInfoKHR { + VkStructureType sType; + const void* pNext; + VkVideoEncodeRateControlFlagsKHR flags; + VkVideoEncodeRateControlModeFlagBitsKHR rateControlMode; + uint32_t averageBitrate; + uint16_t peakToAverageBitrateRatio; + uint16_t frameRateNumerator; + uint16_t frameRateDenominator; + uint32_t virtualBufferSizeInMs; +} VkVideoEncodeRateControlInfoKHR; + +typedef void (VKAPI_PTR *PFN_vkCmdEncodeVideoKHR)(VkCommandBuffer commandBuffer, const VkVideoEncodeInfoKHR* pEncodeInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdEncodeVideoKHR( + VkCommandBuffer commandBuffer, + const VkVideoEncodeInfoKHR* pEncodeInfo); +#endif + + +#define VK_EXT_video_encode_h264 1 +#include "vk_video/vulkan_video_codec_h264std.h" +#include "vk_video/vulkan_video_codec_h264std_encode.h" +#define VK_EXT_VIDEO_ENCODE_H264_SPEC_VERSION 2 +#define VK_EXT_VIDEO_ENCODE_H264_EXTENSION_NAME "VK_EXT_video_encode_h264" + +typedef enum VkVideoEncodeH264CapabilityFlagBitsEXT { + VK_VIDEO_ENCODE_H264_CAPABILITY_CABAC_BIT_EXT = 0x00000001, + VK_VIDEO_ENCODE_H264_CAPABILITY_CAVLC_BIT_EXT = 0x00000002, + VK_VIDEO_ENCODE_H264_CAPABILITY_WEIGHTED_BI_PRED_IMPLICIT_BIT_EXT = 0x00000004, + VK_VIDEO_ENCODE_H264_CAPABILITY_TRANSFORM_8X8_BIT_EXT = 0x00000008, + VK_VIDEO_ENCODE_H264_CAPABILITY_CHROMA_QP_OFFSET_BIT_EXT = 0x00000010, + VK_VIDEO_ENCODE_H264_CAPABILITY_SECOND_CHROMA_QP_OFFSET_BIT_EXT = 0x00000020, + VK_VIDEO_ENCODE_H264_CAPABILITY_DEBLOCKING_FILTER_DISABLED_BIT_EXT = 0x00000040, + VK_VIDEO_ENCODE_H264_CAPABILITY_DEBLOCKING_FILTER_ENABLED_BIT_EXT = 0x00000080, + VK_VIDEO_ENCODE_H264_CAPABILITY_DEBLOCKING_FILTER_PARTIAL_BIT_EXT = 0x00000100, + VK_VIDEO_ENCODE_H264_CAPABILITY_MULTIPLE_SLICE_PER_FRAME_BIT_EXT = 0x00000200, + VK_VIDEO_ENCODE_H264_CAPABILITY_EVENLY_DISTRIBUTED_SLICE_SIZE_BIT_EXT = 0x00000400, + VK_VIDEO_ENCODE_H264_CAPABILITY_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkVideoEncodeH264CapabilityFlagBitsEXT; +typedef VkFlags VkVideoEncodeH264CapabilityFlagsEXT; + +typedef enum VkVideoEncodeH264InputModeFlagBitsEXT { + VK_VIDEO_ENCODE_H264_INPUT_MODE_FRAME_BIT_EXT = 0x00000001, + VK_VIDEO_ENCODE_H264_INPUT_MODE_SLICE_BIT_EXT = 0x00000002, + VK_VIDEO_ENCODE_H264_INPUT_MODE_NON_VCL_BIT_EXT = 0x00000004, + VK_VIDEO_ENCODE_H264_INPUT_MODE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkVideoEncodeH264InputModeFlagBitsEXT; +typedef VkFlags VkVideoEncodeH264InputModeFlagsEXT; + +typedef enum VkVideoEncodeH264OutputModeFlagBitsEXT { + VK_VIDEO_ENCODE_H264_OUTPUT_MODE_FRAME_BIT_EXT = 0x00000001, + VK_VIDEO_ENCODE_H264_OUTPUT_MODE_SLICE_BIT_EXT = 0x00000002, + VK_VIDEO_ENCODE_H264_OUTPUT_MODE_NON_VCL_BIT_EXT = 0x00000004, + VK_VIDEO_ENCODE_H264_OUTPUT_MODE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkVideoEncodeH264OutputModeFlagBitsEXT; +typedef VkFlags VkVideoEncodeH264OutputModeFlagsEXT; + +typedef enum VkVideoEncodeH264CreateFlagBitsEXT { + VK_VIDEO_ENCODE_H264_CREATE_DEFAULT_EXT = 0, + VK_VIDEO_ENCODE_H264_CREATE_RESERVED_0_BIT_EXT = 0x00000001, + VK_VIDEO_ENCODE_H264_CREATE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkVideoEncodeH264CreateFlagBitsEXT; +typedef VkFlags VkVideoEncodeH264CreateFlagsEXT; +typedef struct VkVideoEncodeH264CapabilitiesEXT { + VkStructureType sType; + const void* pNext; + VkVideoEncodeH264CapabilityFlagsEXT flags; + VkVideoEncodeH264InputModeFlagsEXT inputModeFlags; + VkVideoEncodeH264OutputModeFlagsEXT outputModeFlags; + VkExtent2D minPictureSizeInMbs; + VkExtent2D maxPictureSizeInMbs; + VkExtent2D inputImageDataAlignment; + uint8_t maxNumL0ReferenceForP; + uint8_t maxNumL0ReferenceForB; + uint8_t maxNumL1Reference; + uint8_t qualityLevelCount; + VkExtensionProperties stdExtensionVersion; +} VkVideoEncodeH264CapabilitiesEXT; + +typedef struct VkVideoEncodeH264SessionCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkVideoEncodeH264CreateFlagsEXT flags; + VkExtent2D maxPictureSizeInMbs; + const VkExtensionProperties* pStdExtensionVersion; +} VkVideoEncodeH264SessionCreateInfoEXT; + +typedef struct VkVideoEncodeH264SessionParametersAddInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t spsStdCount; + const StdVideoH264SequenceParameterSet* pSpsStd; + uint32_t ppsStdCount; + const StdVideoH264PictureParameterSet* pPpsStd; +} VkVideoEncodeH264SessionParametersAddInfoEXT; + +typedef struct VkVideoEncodeH264SessionParametersCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t maxSpsStdCount; + uint32_t maxPpsStdCount; + const VkVideoEncodeH264SessionParametersAddInfoEXT* pParametersAddInfo; +} VkVideoEncodeH264SessionParametersCreateInfoEXT; + +typedef struct VkVideoEncodeH264DpbSlotInfoEXT { + VkStructureType sType; + const void* pNext; + int8_t slotIndex; + const StdVideoEncodeH264PictureInfo* pStdPictureInfo; +} VkVideoEncodeH264DpbSlotInfoEXT; + +typedef struct VkVideoEncodeH264NaluSliceEXT { + VkStructureType sType; + const void* pNext; + const StdVideoEncodeH264SliceHeader* pSliceHeaderStd; + uint32_t mbCount; + uint8_t refFinalList0EntryCount; + const VkVideoEncodeH264DpbSlotInfoEXT* pRefFinalList0Entries; + uint8_t refFinalList1EntryCount; + const VkVideoEncodeH264DpbSlotInfoEXT* pRefFinalList1Entries; + uint32_t precedingNaluBytes; + uint8_t minQp; + uint8_t maxQp; +} VkVideoEncodeH264NaluSliceEXT; + +typedef struct VkVideoEncodeH264VclFrameInfoEXT { + VkStructureType sType; + const void* pNext; + uint8_t refDefaultFinalList0EntryCount; + const VkVideoEncodeH264DpbSlotInfoEXT* pRefDefaultFinalList0Entries; + uint8_t refDefaultFinalList1EntryCount; + const VkVideoEncodeH264DpbSlotInfoEXT* pRefDefaultFinalList1Entries; + uint32_t naluSliceEntryCount; + const VkVideoEncodeH264NaluSliceEXT* pNaluSliceEntries; + const VkVideoEncodeH264DpbSlotInfoEXT* pCurrentPictureInfo; +} VkVideoEncodeH264VclFrameInfoEXT; + +typedef struct VkVideoEncodeH264EmitPictureParametersEXT { + VkStructureType sType; + const void* pNext; + uint8_t spsId; + VkBool32 emitSpsEnable; + uint32_t ppsIdEntryCount; + const uint8_t* ppsIdEntries; +} VkVideoEncodeH264EmitPictureParametersEXT; + +typedef struct VkVideoEncodeH264ProfileEXT { + VkStructureType sType; + const void* pNext; + StdVideoH264ProfileIdc stdProfileIdc; +} VkVideoEncodeH264ProfileEXT; + + + +#define VK_EXT_video_encode_h265 1 +#include "vk_video/vulkan_video_codec_h265std.h" +#include "vk_video/vulkan_video_codec_h265std_encode.h" +#define VK_EXT_VIDEO_ENCODE_H265_SPEC_VERSION 2 +#define VK_EXT_VIDEO_ENCODE_H265_EXTENSION_NAME "VK_EXT_video_encode_h265" +typedef VkFlags VkVideoEncodeH265CapabilityFlagsEXT; + +typedef enum VkVideoEncodeH265InputModeFlagBitsEXT { + VK_VIDEO_ENCODE_H265_INPUT_MODE_FRAME_BIT_EXT = 0x00000001, + VK_VIDEO_ENCODE_H265_INPUT_MODE_SLICE_BIT_EXT = 0x00000002, + VK_VIDEO_ENCODE_H265_INPUT_MODE_NON_VCL_BIT_EXT = 0x00000004, + VK_VIDEO_ENCODE_H265_INPUT_MODE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkVideoEncodeH265InputModeFlagBitsEXT; +typedef VkFlags VkVideoEncodeH265InputModeFlagsEXT; + +typedef enum VkVideoEncodeH265OutputModeFlagBitsEXT { + VK_VIDEO_ENCODE_H265_OUTPUT_MODE_FRAME_BIT_EXT = 0x00000001, + VK_VIDEO_ENCODE_H265_OUTPUT_MODE_SLICE_BIT_EXT = 0x00000002, + VK_VIDEO_ENCODE_H265_OUTPUT_MODE_NON_VCL_BIT_EXT = 0x00000004, + VK_VIDEO_ENCODE_H265_OUTPUT_MODE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkVideoEncodeH265OutputModeFlagBitsEXT; +typedef VkFlags VkVideoEncodeH265OutputModeFlagsEXT; +typedef VkFlags VkVideoEncodeH265CreateFlagsEXT; + +typedef enum VkVideoEncodeH265CtbSizeFlagBitsEXT { + VK_VIDEO_ENCODE_H265_CTB_SIZE_8_BIT_EXT = 0x00000001, + VK_VIDEO_ENCODE_H265_CTB_SIZE_16_BIT_EXT = 0x00000002, + VK_VIDEO_ENCODE_H265_CTB_SIZE_32_BIT_EXT = 0x00000004, + VK_VIDEO_ENCODE_H265_CTB_SIZE_64_BIT_EXT = 0x00000008, + VK_VIDEO_ENCODE_H265_CTB_SIZE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkVideoEncodeH265CtbSizeFlagBitsEXT; +typedef VkFlags VkVideoEncodeH265CtbSizeFlagsEXT; +typedef struct VkVideoEncodeH265CapabilitiesEXT { + VkStructureType sType; + const void* pNext; + VkVideoEncodeH265CapabilityFlagsEXT flags; + VkVideoEncodeH265InputModeFlagsEXT inputModeFlags; + VkVideoEncodeH265OutputModeFlagsEXT outputModeFlags; + VkVideoEncodeH265CtbSizeFlagsEXT ctbSizes; + VkExtent2D inputImageDataAlignment; + uint8_t maxNumL0ReferenceForP; + uint8_t maxNumL0ReferenceForB; + uint8_t maxNumL1Reference; + uint8_t maxNumSubLayers; + uint8_t qualityLevelCount; + VkExtensionProperties stdExtensionVersion; +} VkVideoEncodeH265CapabilitiesEXT; + +typedef struct VkVideoEncodeH265SessionCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkVideoEncodeH265CreateFlagsEXT flags; + const VkExtensionProperties* pStdExtensionVersion; +} VkVideoEncodeH265SessionCreateInfoEXT; + +typedef struct VkVideoEncodeH265SessionParametersAddInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t vpsStdCount; + const StdVideoH265VideoParameterSet* pVpsStd; + uint32_t spsStdCount; + const StdVideoH265SequenceParameterSet* pSpsStd; + uint32_t ppsStdCount; + const StdVideoH265PictureParameterSet* pPpsStd; +} VkVideoEncodeH265SessionParametersAddInfoEXT; + +typedef struct VkVideoEncodeH265SessionParametersCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t maxVpsStdCount; + uint32_t maxSpsStdCount; + uint32_t maxPpsStdCount; + const VkVideoEncodeH265SessionParametersAddInfoEXT* pParametersAddInfo; +} VkVideoEncodeH265SessionParametersCreateInfoEXT; + +typedef struct VkVideoEncodeH265DpbSlotInfoEXT { + VkStructureType sType; + const void* pNext; + int8_t slotIndex; + const StdVideoEncodeH265ReferenceInfo* pStdReferenceInfo; +} VkVideoEncodeH265DpbSlotInfoEXT; + +typedef struct VkVideoEncodeH265ReferenceListsEXT { + VkStructureType sType; + const void* pNext; + uint8_t referenceList0EntryCount; + const VkVideoEncodeH265DpbSlotInfoEXT* pReferenceList0Entries; + uint8_t referenceList1EntryCount; + const VkVideoEncodeH265DpbSlotInfoEXT* pReferenceList1Entries; + const StdVideoEncodeH265ReferenceModifications* pReferenceModifications; +} VkVideoEncodeH265ReferenceListsEXT; + +typedef struct VkVideoEncodeH265NaluSliceEXT { + VkStructureType sType; + const void* pNext; + uint32_t ctbCount; + const VkVideoEncodeH265ReferenceListsEXT* pReferenceFinalLists; + const StdVideoEncodeH265SliceHeader* pSliceHeaderStd; +} VkVideoEncodeH265NaluSliceEXT; + +typedef struct VkVideoEncodeH265VclFrameInfoEXT { + VkStructureType sType; + const void* pNext; + const VkVideoEncodeH265ReferenceListsEXT* pReferenceFinalLists; + uint32_t naluSliceEntryCount; + const VkVideoEncodeH265NaluSliceEXT* pNaluSliceEntries; + const StdVideoEncodeH265PictureInfo* pCurrentPictureInfo; +} VkVideoEncodeH265VclFrameInfoEXT; + +typedef struct VkVideoEncodeH265EmitPictureParametersEXT { + VkStructureType sType; + const void* pNext; + uint8_t vpsId; + uint8_t spsId; + VkBool32 emitVpsEnable; + VkBool32 emitSpsEnable; + uint32_t ppsIdEntryCount; + const uint8_t* ppsIdEntries; +} VkVideoEncodeH265EmitPictureParametersEXT; + +typedef struct VkVideoEncodeH265ProfileEXT { + VkStructureType sType; + const void* pNext; + StdVideoH265ProfileIdc stdProfileIdc; +} VkVideoEncodeH265ProfileEXT; + + + +#define VK_EXT_video_decode_h264 1 +#include "vk_video/vulkan_video_codec_h264std_decode.h" +#define VK_EXT_VIDEO_DECODE_H264_SPEC_VERSION 3 +#define VK_EXT_VIDEO_DECODE_H264_EXTENSION_NAME "VK_EXT_video_decode_h264" + +typedef enum VkVideoDecodeH264PictureLayoutFlagBitsEXT { + VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_EXT = 0, + VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_EXT = 0x00000001, + VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_SEPARATE_PLANES_BIT_EXT = 0x00000002, + VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkVideoDecodeH264PictureLayoutFlagBitsEXT; +typedef VkFlags VkVideoDecodeH264PictureLayoutFlagsEXT; +typedef VkFlags VkVideoDecodeH264CreateFlagsEXT; +typedef struct VkVideoDecodeH264ProfileEXT { + VkStructureType sType; + const void* pNext; + StdVideoH264ProfileIdc stdProfileIdc; + VkVideoDecodeH264PictureLayoutFlagsEXT pictureLayout; +} VkVideoDecodeH264ProfileEXT; + +typedef struct VkVideoDecodeH264CapabilitiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxLevel; + VkOffset2D fieldOffsetGranularity; + VkExtensionProperties stdExtensionVersion; +} VkVideoDecodeH264CapabilitiesEXT; + +typedef struct VkVideoDecodeH264SessionCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkVideoDecodeH264CreateFlagsEXT flags; + const VkExtensionProperties* pStdExtensionVersion; +} VkVideoDecodeH264SessionCreateInfoEXT; + +typedef struct VkVideoDecodeH264SessionParametersAddInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t spsStdCount; + const StdVideoH264SequenceParameterSet* pSpsStd; + uint32_t ppsStdCount; + const StdVideoH264PictureParameterSet* pPpsStd; +} VkVideoDecodeH264SessionParametersAddInfoEXT; + +typedef struct VkVideoDecodeH264SessionParametersCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t maxSpsStdCount; + uint32_t maxPpsStdCount; + const VkVideoDecodeH264SessionParametersAddInfoEXT* pParametersAddInfo; +} VkVideoDecodeH264SessionParametersCreateInfoEXT; + +typedef struct VkVideoDecodeH264PictureInfoEXT { + VkStructureType sType; + const void* pNext; + const StdVideoDecodeH264PictureInfo* pStdPictureInfo; + uint32_t slicesCount; + const uint32_t* pSlicesDataOffsets; +} VkVideoDecodeH264PictureInfoEXT; + +typedef struct VkVideoDecodeH264MvcEXT { + VkStructureType sType; + const void* pNext; + const StdVideoDecodeH264Mvc* pStdMvc; +} VkVideoDecodeH264MvcEXT; + +typedef struct VkVideoDecodeH264DpbSlotInfoEXT { + VkStructureType sType; + const void* pNext; + const StdVideoDecodeH264ReferenceInfo* pStdReferenceInfo; +} VkVideoDecodeH264DpbSlotInfoEXT; + + + +#define VK_EXT_video_decode_h265 1 +#include "vk_video/vulkan_video_codec_h265std_decode.h" +#define VK_EXT_VIDEO_DECODE_H265_SPEC_VERSION 1 +#define VK_EXT_VIDEO_DECODE_H265_EXTENSION_NAME "VK_EXT_video_decode_h265" +typedef VkFlags VkVideoDecodeH265CreateFlagsEXT; +typedef struct VkVideoDecodeH265ProfileEXT { + VkStructureType sType; + const void* pNext; + StdVideoH265ProfileIdc stdProfileIdc; +} VkVideoDecodeH265ProfileEXT; + +typedef struct VkVideoDecodeH265CapabilitiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxLevel; + VkExtensionProperties stdExtensionVersion; +} VkVideoDecodeH265CapabilitiesEXT; + +typedef struct VkVideoDecodeH265SessionCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkVideoDecodeH265CreateFlagsEXT flags; + const VkExtensionProperties* pStdExtensionVersion; +} VkVideoDecodeH265SessionCreateInfoEXT; + +typedef struct VkVideoDecodeH265SessionParametersAddInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t spsStdCount; + const StdVideoH265SequenceParameterSet* pSpsStd; + uint32_t ppsStdCount; + const StdVideoH265PictureParameterSet* pPpsStd; +} VkVideoDecodeH265SessionParametersAddInfoEXT; + +typedef struct VkVideoDecodeH265SessionParametersCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t maxSpsStdCount; + uint32_t maxPpsStdCount; + const VkVideoDecodeH265SessionParametersAddInfoEXT* pParametersAddInfo; +} VkVideoDecodeH265SessionParametersCreateInfoEXT; + +typedef struct VkVideoDecodeH265PictureInfoEXT { + VkStructureType sType; + const void* pNext; + StdVideoDecodeH265PictureInfo* pStdPictureInfo; + uint32_t slicesCount; + const uint32_t* pSlicesDataOffsets; +} VkVideoDecodeH265PictureInfoEXT; + +typedef struct VkVideoDecodeH265DpbSlotInfoEXT { + VkStructureType sType; + const void* pNext; + const StdVideoDecodeH265ReferenceInfo* pStdReferenceInfo; +} VkVideoDecodeH265DpbSlotInfoEXT; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/vulkan/vulkan_core.h b/third_party/vulkan/vulkan_core.h index 9fefb4382..2bd3f779e 100644 --- a/third_party/vulkan/vulkan_core.h +++ b/third_party/vulkan/vulkan_core.h @@ -1,24 +1,10 @@ #ifndef VULKAN_CORE_H_ #define VULKAN_CORE_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,47 +13,90 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_VERSION_1_0 1 #include "vk_platform.h" -#define VK_MAKE_VERSION(major, minor, patch) \ - (((major) << 22) | ((minor) << 12) | (patch)) - -// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. -//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0 - -// Vulkan 1.0 version number -#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)// Patch version should always be set to 0 - -#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) -#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) -#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) -// Version of this file -#define VK_HEADER_VERSION 74 - - -#define VK_NULL_HANDLE 0 - - - #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; -#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE) -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#ifndef VK_USE_64_BIT_PTR_DEFINES + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VK_USE_64_BIT_PTR_DEFINES 1 + #else + #define VK_USE_64_BIT_PTR_DEFINES 0 + #endif +#endif + + +#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE + #if (VK_USE_64_BIT_PTR_DEFINES==1) + #if (defined(__cplusplus) && (__cplusplus >= 201103L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L)) + #define VK_NULL_HANDLE nullptr + #else + #define VK_NULL_HANDLE ((void*)0) + #endif + #else + #define VK_NULL_HANDLE 0ULL + #endif +#endif +#ifndef VK_NULL_HANDLE + #define VK_NULL_HANDLE 0 +#endif + + +#ifndef VK_DEFINE_NON_DISPATCHABLE_HANDLE + #if (VK_USE_64_BIT_PTR_DEFINES==1) #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; -#else + #else #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; + #endif #endif -#endif - +// DEPRECATED: This define is deprecated. VK_MAKE_API_VERSION should be used instead. +#define VK_MAKE_VERSION(major, minor, patch) \ + ((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) -typedef uint32_t VkFlags; +// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. +//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0 + +#define VK_MAKE_API_VERSION(variant, major, minor, patch) \ + ((((uint32_t)(variant)) << 29) | (((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) + +// Vulkan 1.0 version number +#define VK_API_VERSION_1_0 VK_MAKE_API_VERSION(0, 1, 0, 0)// Patch version should always be set to 0 + +// Version of this file +#define VK_HEADER_VERSION 198 + +// Complete version of this file +#define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 2, VK_HEADER_VERSION) + +// DEPRECATED: This define is deprecated. VK_API_VERSION_MAJOR should be used instead. +#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) + +// DEPRECATED: This define is deprecated. VK_API_VERSION_MINOR should be used instead. +#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) + +// DEPRECATED: This define is deprecated. VK_API_VERSION_PATCH should be used instead. +#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) + +#define VK_API_VERSION_VARIANT(version) ((uint32_t)(version) >> 29) +#define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU) +#define VK_API_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) +#define VK_API_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) typedef uint32_t VkBool32; +typedef uint64_t VkDeviceAddress; typedef uint64_t VkDeviceSize; +typedef uint32_t VkFlags; typedef uint32_t VkSampleMask; - +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage) VK_DEFINE_HANDLE(VkInstance) VK_DEFINE_HANDLE(VkPhysicalDevice) VK_DEFINE_HANDLE(VkDevice) @@ -76,8 +105,6 @@ VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSemaphore) VK_DEFINE_HANDLE(VkCommandBuffer) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory) -VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer) -VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkEvent) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkQueryPool) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferView) @@ -85,39 +112,29 @@ VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderModule) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineCache) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineLayout) -VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipeline) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSetLayout) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSampler) -VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorPool) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSet) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorPool) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCommandPool) - -#define VK_LOD_CLAMP_NONE 1000.0f -#define VK_REMAINING_MIP_LEVELS (~0U) -#define VK_REMAINING_ARRAY_LAYERS (~0U) -#define VK_WHOLE_SIZE (~0ULL) +#define VK_UUID_SIZE 16U #define VK_ATTACHMENT_UNUSED (~0U) -#define VK_TRUE 1 -#define VK_FALSE 0 +#define VK_FALSE 0U +#define VK_LOD_CLAMP_NONE 1000.0F #define VK_QUEUE_FAMILY_IGNORED (~0U) +#define VK_REMAINING_ARRAY_LAYERS (~0U) +#define VK_REMAINING_MIP_LEVELS (~0U) #define VK_SUBPASS_EXTERNAL (~0U) -#define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 -#define VK_UUID_SIZE 16 -#define VK_MAX_MEMORY_TYPES 32 -#define VK_MAX_MEMORY_HEAPS 16 -#define VK_MAX_EXTENSION_NAME_SIZE 256 -#define VK_MAX_DESCRIPTION_SIZE 256 - - -typedef enum VkPipelineCacheHeaderVersion { - VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1, - VK_PIPELINE_CACHE_HEADER_VERSION_BEGIN_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, - VK_PIPELINE_CACHE_HEADER_VERSION_END_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, - VK_PIPELINE_CACHE_HEADER_VERSION_RANGE_SIZE = (VK_PIPELINE_CACHE_HEADER_VERSION_ONE - VK_PIPELINE_CACHE_HEADER_VERSION_ONE + 1), - VK_PIPELINE_CACHE_HEADER_VERSION_MAX_ENUM = 0x7FFFFFFF -} VkPipelineCacheHeaderVersion; +#define VK_TRUE 1U +#define VK_WHOLE_SIZE (~0ULL) +#define VK_MAX_MEMORY_TYPES 32U +#define VK_MAX_MEMORY_HEAPS 16U +#define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256U +#define VK_MAX_EXTENSION_NAME_SIZE 256U +#define VK_MAX_DESCRIPTION_SIZE 256U typedef enum VkResult { VK_SUCCESS = 0, @@ -138,8 +155,11 @@ typedef enum VkResult { VK_ERROR_TOO_MANY_OBJECTS = -10, VK_ERROR_FORMAT_NOT_SUPPORTED = -11, VK_ERROR_FRAGMENTED_POOL = -12, + VK_ERROR_UNKNOWN = -13, VK_ERROR_OUT_OF_POOL_MEMORY = -1000069000, VK_ERROR_INVALID_EXTERNAL_HANDLE = -1000072003, + VK_ERROR_FRAGMENTATION = -1000161000, + VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS = -1000257000, VK_ERROR_SURFACE_LOST_KHR = -1000000000, VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, VK_SUBOPTIMAL_KHR = 1000001003, @@ -147,13 +167,20 @@ typedef enum VkResult { VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, VK_ERROR_INVALID_SHADER_NV = -1000012000, - VK_ERROR_FRAGMENTATION_EXT = -1000161000, + VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT = -1000158000, VK_ERROR_NOT_PERMITTED_EXT = -1000174001, + VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT = -1000255000, + VK_THREAD_IDLE_KHR = 1000268000, + VK_THREAD_DONE_KHR = 1000268001, + VK_OPERATION_DEFERRED_KHR = 1000268002, + VK_OPERATION_NOT_DEFERRED_KHR = 1000268003, + VK_PIPELINE_COMPILE_REQUIRED_EXT = 1000297000, VK_ERROR_OUT_OF_POOL_MEMORY_KHR = VK_ERROR_OUT_OF_POOL_MEMORY, VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR = VK_ERROR_INVALID_EXTERNAL_HANDLE, - VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL, - VK_RESULT_END_RANGE = VK_INCOMPLETE, - VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1), + VK_ERROR_FRAGMENTATION_EXT = VK_ERROR_FRAGMENTATION, + VK_ERROR_INVALID_DEVICE_ADDRESS_EXT = VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS, + VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR = VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS, + VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT = VK_PIPELINE_COMPILE_REQUIRED_EXT, VK_RESULT_MAX_ENUM = 0x7FFFFFFF } VkResult; @@ -243,7 +270,7 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO = 1000053000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES = 1000053001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES = 1000053002, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = 1000120000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES = 1000120000, VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO = 1000145000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES = 1000145001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES = 1000145002, @@ -271,7 +298,57 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES = 1000076001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES = 1000168000, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT = 1000168001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = 1000063000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES = 1000063000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES = 49, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES = 50, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES = 51, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES = 52, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO = 1000147000, + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2 = 1000109000, + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2 = 1000109001, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2 = 1000109002, + VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2 = 1000109003, + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2 = 1000109004, + VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO = 1000109005, + VK_STRUCTURE_TYPE_SUBPASS_END_INFO = 1000109006, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES = 1000177000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES = 1000196000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES = 1000180000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES = 1000082000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES = 1000197000, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO = 1000161000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES = 1000161001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES = 1000161002, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO = 1000161003, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT = 1000161004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES = 1000199000, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE = 1000199001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES = 1000221000, + VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO = 1000246000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES = 1000130000, + VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO = 1000130001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES = 1000211000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES = 1000108000, + VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO = 1000108001, + VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO = 1000108002, + VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO = 1000108003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES = 1000253000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES = 1000175000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES = 1000241000, + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_STENCIL_LAYOUT = 1000241001, + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT = 1000241002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES = 1000261000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES = 1000207000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES = 1000207001, + VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO = 1000207002, + VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO = 1000207003, + VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO = 1000207004, + VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO = 1000207005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES = 1000257000, + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO = 1000244001, + VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO = 1000257002, + VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO = 1000257003, + VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO = 1000257004, VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000, VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001, VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, @@ -286,7 +363,6 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, - VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, @@ -294,10 +370,161 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_PROFILE_KHR = 1000023000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR = 1000023001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_KHR = 1000023002, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_GET_MEMORY_PROPERTIES_KHR = 1000023003, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_BIND_MEMORY_KHR = 1000023004, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR = 1000023005, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR = 1000023006, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_UPDATE_INFO_KHR = 1000023007, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR = 1000023008, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR = 1000023009, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_CODING_CONTROL_INFO_KHR = 1000023010, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_KHR = 1000023011, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000023012, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_PROFILES_KHR = 1000023013, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR = 1000023014, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR = 1000023015, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR = 1000024000, +#endif VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT = 1000028000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT = 1000028001, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT = 1000028002, + VK_STRUCTURE_TYPE_CU_MODULE_CREATE_INFO_NVX = 1000029000, + VK_STRUCTURE_TYPE_CU_FUNCTION_CREATE_INFO_NVX = 1000029001, + VK_STRUCTURE_TYPE_CU_LAUNCH_INFO_NVX = 1000029002, + VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX = 1000030000, + VK_STRUCTURE_TYPE_IMAGE_VIEW_ADDRESS_PROPERTIES_NVX = 1000030001, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_EXT = 1000038000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_CREATE_INFO_EXT = 1000038001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000038002, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000038003, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT = 1000038004, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_EXT = 1000038005, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_NALU_SLICE_EXT = 1000038006, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT = 1000038007, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_EXT = 1000038008, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_CAPABILITIES_EXT = 1000039000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_CREATE_INFO_EXT = 1000039001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000039002, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT = 1000039003, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_VCL_FRAME_INFO_EXT = 1000039004, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_DPB_SLOT_INFO_EXT = 1000039005, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_NALU_SLICE_EXT = 1000039006, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_EMIT_PICTURE_PARAMETERS_EXT = 1000039007, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_PROFILE_EXT = 1000039008, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_REFERENCE_LISTS_EXT = 1000039009, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_CAPABILITIES_EXT = 1000040000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_CREATE_INFO_EXT = 1000040001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PICTURE_INFO_EXT = 1000040002, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_MVC_EXT = 1000040003, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_EXT = 1000040004, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000040005, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT = 1000040006, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT = 1000040007, +#endif VK_STRUCTURE_TYPE_TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD = 1000041000, + VK_STRUCTURE_TYPE_RENDERING_INFO_KHR = 1000044000, + VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR = 1000044001, + VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR = 1000044002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR = 1000044003, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO_KHR = 1000044004, + VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR = 1000044006, + VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_INFO_EXT = 1000044007, + VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_AMD = 1000044008, + VK_STRUCTURE_TYPE_MULTIVIEW_PER_VIEW_ATTRIBUTES_INFO_NVX = 1000044009, + VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP = 1000049000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV = 1000050000, VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001, VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000, @@ -305,6 +532,9 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT = 1000066000, + VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT = 1000067000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT = 1000067001, VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073000, VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073001, VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR = 1000073002, @@ -320,13 +550,10 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR = 1000079000, VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR = 1000079001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT = 1000081000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT = 1000081001, + VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT = 1000081002, VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000, - VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000, - VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001, - VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002, - VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003, - VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004, - VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005, VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000, VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT = 1000090000, VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000, @@ -340,6 +567,8 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT = 1000101000, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT = 1000101001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT = 1000102000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT = 1000102001, VK_STRUCTURE_TYPE_HDR_METADATA_EXT = 1000105000, VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR = 1000111000, VK_STRUCTURE_TYPE_IMPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114000, @@ -347,9 +576,21 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_FENCE_GET_WIN32_HANDLE_INFO_KHR = 1000114002, VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR = 1000115000, VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR = 1000115001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR = 1000116000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_PROPERTIES_KHR = 1000116001, + VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_CREATE_INFO_KHR = 1000116002, + VK_STRUCTURE_TYPE_PERFORMANCE_QUERY_SUBMIT_INFO_KHR = 1000116003, + VK_STRUCTURE_TYPE_ACQUIRE_PROFILING_LOCK_INFO_KHR = 1000116004, + VK_STRUCTURE_TYPE_PERFORMANCE_COUNTER_KHR = 1000116005, + VK_STRUCTURE_TYPE_PERFORMANCE_COUNTER_DESCRIPTION_KHR = 1000116006, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000, VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001, VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR = 1000119002, + VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR = 1000121000, + VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR = 1000121001, + VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR = 1000121002, + VK_STRUCTURE_TYPE_DISPLAY_PLANE_INFO_2_KHR = 1000121003, + VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR = 1000121004, VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK = 1000122000, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000128000, @@ -363,33 +604,312 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129003, VK_STRUCTURE_TYPE_MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129004, VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID = 1000129005, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000, - VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001, + VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_2_ANDROID = 1000129006, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT = 1000138000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT = 1000138001, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT = 1000138002, + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT = 1000138003, VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000, VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001, VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003, VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT = 1000143004, - VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = 1000147000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001, VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002, VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR = 1000150007, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR = 1000150000, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR = 1000150002, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR = 1000150003, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR = 1000150004, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR = 1000150005, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR = 1000150006, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_VERSION_INFO_KHR = 1000150009, + VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_INFO_KHR = 1000150010, + VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_TO_MEMORY_INFO_KHR = 1000150011, + VK_STRUCTURE_TYPE_COPY_MEMORY_TO_ACCELERATION_STRUCTURE_INFO_KHR = 1000150012, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR = 1000150013, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR = 1000150014, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR = 1000150017, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR = 1000150020, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR = 1000347000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR = 1000347001, + VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR = 1000150015, + VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR = 1000150016, + VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_INTERFACE_CREATE_INFO_KHR = 1000150018, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR = 1000348013, VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV = 1000154000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_PROPERTIES_NV = 1000154001, + VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT = 1000158000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT = 1000158002, + VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT = 1000158003, + VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT = 1000158004, + VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT = 1000158005, + VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_2_EXT = 1000158006, VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000, VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT = 1000161000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT = 1000161001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT = 1000161002, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT = 1000161003, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT_EXT = 1000161004, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR = 1000163000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR = 1000163001, +#endif + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV = 1000164000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV = 1000164001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV = 1000164002, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_COARSE_SAMPLE_ORDER_STATE_CREATE_INFO_NV = 1000164005, + VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV = 1000165000, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV = 1000165001, + VK_STRUCTURE_TYPE_GEOMETRY_NV = 1000165003, + VK_STRUCTURE_TYPE_GEOMETRY_TRIANGLES_NV = 1000165004, + VK_STRUCTURE_TYPE_GEOMETRY_AABB_NV = 1000165005, + VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV = 1000165006, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV = 1000165007, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV = 1000165008, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV = 1000165009, + VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV = 1000165011, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV = 1000165012, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV = 1000166000, + VK_STRUCTURE_TYPE_PIPELINE_REPRESENTATIVE_FRAGMENT_TEST_STATE_CREATE_INFO_NV = 1000166001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_IMAGE_FORMAT_INFO_EXT = 1000170000, + VK_STRUCTURE_TYPE_FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT = 1000170001, VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT = 1000174000, VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT = 1000178000, VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT = 1000178001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT = 1000178002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CLOCK_FEATURES_KHR = 1000181000, + VK_STRUCTURE_TYPE_PIPELINE_COMPILER_CONTROL_CREATE_INFO_AMD = 1000183000, + VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT = 1000184000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD = 1000185000, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_CAPABILITIES_EXT = 1000187000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_CREATE_INFO_EXT = 1000187001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT = 1000187002, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT = 1000187003, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_EXT = 1000187004, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PICTURE_INFO_EXT = 1000187005, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT = 1000187006, +#endif + VK_STRUCTURE_TYPE_DEVICE_MEMORY_OVERALLOCATION_CREATE_INFO_AMD = 1000189000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT = 1000190000, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT = 1000190001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT = 1000190002, + VK_STRUCTURE_TYPE_PRESENT_FRAME_TOKEN_GGP = 1000191000, + VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT = 1000192000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_NV = 1000201000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV = 1000202000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV = 1000202001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_NV = 1000203000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_FOOTPRINT_FEATURES_NV = 1000204000, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_EXCLUSIVE_SCISSOR_STATE_CREATE_INFO_NV = 1000205000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV = 1000205002, + VK_STRUCTURE_TYPE_CHECKPOINT_DATA_NV = 1000206000, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_NV = 1000206001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL = 1000209000, + VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL = 1000210000, + VK_STRUCTURE_TYPE_INITIALIZE_PERFORMANCE_API_INFO_INTEL = 1000210001, + VK_STRUCTURE_TYPE_PERFORMANCE_MARKER_INFO_INTEL = 1000210002, + VK_STRUCTURE_TYPE_PERFORMANCE_STREAM_MARKER_INFO_INTEL = 1000210003, + VK_STRUCTURE_TYPE_PERFORMANCE_OVERRIDE_INFO_INTEL = 1000210004, + VK_STRUCTURE_TYPE_PERFORMANCE_CONFIGURATION_ACQUIRE_INFO_INTEL = 1000210005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT = 1000212000, + VK_STRUCTURE_TYPE_DISPLAY_NATIVE_HDR_SURFACE_CAPABILITIES_AMD = 1000213000, + VK_STRUCTURE_TYPE_SWAPCHAIN_DISPLAY_NATIVE_HDR_CREATE_INFO_AMD = 1000213001, + VK_STRUCTURE_TYPE_IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA = 1000214000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES_KHR = 1000215000, + VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT = 1000218000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT = 1000218001, + VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT = 1000218002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT = 1000225000, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT = 1000225001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT = 1000225002, + VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR = 1000226000, + VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR = 1000226001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR = 1000226002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR = 1000226003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR = 1000226004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD = 1000227000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD = 1000229000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT = 1000234000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT = 1000237000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT = 1000238000, + VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT = 1000238001, + VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR = 1000239000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEDICATED_ALLOCATION_IMAGE_ALIASING_FEATURES_NV = 1000240000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT = 1000244000, + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT = 1000244002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT = 1000245000, + VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT = 1000247000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR = 1000248000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_NV = 1000249000, + VK_STRUCTURE_TYPE_COOPERATIVE_MATRIX_PROPERTIES_NV = 1000249001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_PROPERTIES_NV = 1000249002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COVERAGE_REDUCTION_MODE_FEATURES_NV = 1000250000, + VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_REDUCTION_STATE_CREATE_INFO_NV = 1000250001, + VK_STRUCTURE_TYPE_FRAMEBUFFER_MIXED_SAMPLES_COMBINATION_NV = 1000250002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT = 1000251000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_IMAGE_ARRAYS_FEATURES_EXT = 1000252000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT = 1000254000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT = 1000254001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_PROPERTIES_EXT = 1000254002, + VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT = 1000255000, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT = 1000255002, + VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT = 1000255001, + VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT = 1000256000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT = 1000259000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT = 1000259001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_PROPERTIES_EXT = 1000259002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT = 1000260000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT = 1000265000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT = 1000267000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR = 1000269000, + VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR = 1000269001, + VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR = 1000269002, + VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR = 1000269003, + VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR = 1000269004, + VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INTERNAL_REPRESENTATION_KHR = 1000269005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT = 1000273000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT = 1000276000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV = 1000277000, + VK_STRUCTURE_TYPE_GRAPHICS_SHADER_GROUP_CREATE_INFO_NV = 1000277001, + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_SHADER_GROUPS_CREATE_INFO_NV = 1000277002, + VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_TOKEN_NV = 1000277003, + VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NV = 1000277004, + VK_STRUCTURE_TYPE_GENERATED_COMMANDS_INFO_NV = 1000277005, + VK_STRUCTURE_TYPE_GENERATED_COMMANDS_MEMORY_REQUIREMENTS_INFO_NV = 1000277006, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_FEATURES_NV = 1000277007, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INHERITED_VIEWPORT_SCISSOR_FEATURES_NV = 1000278000, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_VIEWPORT_SCISSOR_INFO_NV = 1000278001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES_KHR = 1000280000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES_KHR = 1000280001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT = 1000281000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT = 1000281001, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDER_PASS_TRANSFORM_INFO_QCOM = 1000282000, + VK_STRUCTURE_TYPE_RENDER_PASS_TRANSFORM_BEGIN_INFO_QCOM = 1000282001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEVICE_MEMORY_REPORT_FEATURES_EXT = 1000284000, + VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT = 1000284001, + VK_STRUCTURE_TYPE_DEVICE_MEMORY_REPORT_CALLBACK_DATA_EXT = 1000284002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT = 1000286000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT = 1000286001, + VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT = 1000287000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT = 1000287001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT = 1000287002, + VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR = 1000290000, + VK_STRUCTURE_TYPE_PRESENT_ID_KHR = 1000294000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR = 1000294001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT = 1000295000, + VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO_EXT = 1000295001, + VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO_EXT = 1000295002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT = 1000297000, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR = 1000299000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_INFO_KHR = 1000299001, +#endif + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DIAGNOSTICS_CONFIG_FEATURES_NV = 1000300000, + VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV = 1000300001, + VK_STRUCTURE_TYPE_MEMORY_BARRIER_2_KHR = 1000314000, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2_KHR = 1000314001, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2_KHR = 1000314002, + VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR = 1000314003, + VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR = 1000314004, + VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR = 1000314005, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR = 1000314006, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR = 1000314007, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_2_NV = 1000314008, + VK_STRUCTURE_TYPE_CHECKPOINT_DATA_2_NV = 1000314009, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR = 1000323000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES_KHR = 1000325000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_PROPERTIES_NV = 1000326000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_ENUMS_FEATURES_NV = 1000326001, + VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_ENUM_STATE_CREATE_INFO_NV = 1000326002, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_MOTION_TRIANGLES_DATA_NV = 1000327000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MOTION_BLUR_FEATURES_NV = 1000327001, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MOTION_INFO_NV = 1000327002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT = 1000330000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT = 1000332000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_PROPERTIES_EXT = 1000332001, + VK_STRUCTURE_TYPE_COPY_COMMAND_TRANSFORM_INFO_QCOM = 1000333000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT = 1000335000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR = 1000336000, + VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2_KHR = 1000337000, + VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2_KHR = 1000337001, + VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2_KHR = 1000337002, + VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2_KHR = 1000337003, + VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2_KHR = 1000337004, + VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2_KHR = 1000337005, + VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR = 1000337006, + VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR = 1000337007, + VK_STRUCTURE_TYPE_IMAGE_BLIT_2_KHR = 1000337008, + VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR = 1000337009, + VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2_KHR = 1000337010, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT = 1000340000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT = 1000344000, + VK_STRUCTURE_TYPE_DIRECTFB_SURFACE_CREATE_INFO_EXT = 1000346000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_VALVE = 1000351000, + VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_VALVE = 1000351002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT = 1000352000, + VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT = 1000352001, + VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT = 1000352002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT = 1000353000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT = 1000356000, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3_KHR = 1000360000, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA = 1000364000, + VK_STRUCTURE_TYPE_MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA = 1000364001, + VK_STRUCTURE_TYPE_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA = 1000364002, + VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA = 1000365000, + VK_STRUCTURE_TYPE_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA = 1000365001, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA = 1000366000, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA = 1000366001, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA = 1000366002, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA = 1000366003, + VK_STRUCTURE_TYPE_BUFFER_CONSTRAINTS_INFO_FUCHSIA = 1000366004, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_BUFFER_CREATE_INFO_FUCHSIA = 1000366005, + VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA = 1000366006, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA = 1000366007, + VK_STRUCTURE_TYPE_SYSMEM_COLOR_SPACE_FUCHSIA = 1000366008, + VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA = 1000366009, + VK_STRUCTURE_TYPE_SUBPASS_SHADING_PIPELINE_CREATE_INFO_HUAWEI = 1000369000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_FEATURES_HUAWEI = 1000369001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_SHADING_PROPERTIES_HUAWEI = 1000369002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INVOCATION_MASK_FEATURES_HUAWEI = 1000370000, + VK_STRUCTURE_TYPE_MEMORY_GET_REMOTE_ADDRESS_INFO_NV = 1000371000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_RDMA_FEATURES_NV = 1000371001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT = 1000377000, + VK_STRUCTURE_TYPE_SCREEN_SURFACE_CREATE_INFO_QNX = 1000378000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT = 1000381000, + VK_STRUCTURE_TYPE_PIPELINE_COLOR_WRITE_CREATE_INFO_EXT = 1000381001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_EXT = 1000388000, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT = 1000388001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT = 1000392000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT = 1000392001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT = 1000411000, + VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT = 1000411001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT = 1000412000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES_KHR = 1000413000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES_KHR = 1000413001, + VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS_KHR = 1000413002, + VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR = 1000413003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_NV = VK_STRUCTURE_TYPE_ATTACHMENT_SAMPLE_COUNT_INFO_AMD, VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, @@ -422,8 +942,22 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES, + VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO, + VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO_KHR = VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO, + VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO, + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, + VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, + VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO_KHR = VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO, + VK_STRUCTURE_TYPE_SUBPASS_END_INFO_KHR = VK_STRUCTURE_TYPE_SUBPASS_END_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES, VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO, @@ -431,14 +965,18 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO, VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES, + VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO, VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO, VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO, @@ -447,31 +985,177 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES, VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO, VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT_EXT = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT, - VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO, - VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, - VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1), + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES, + VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, + VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, + VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO_KHR = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO, + VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO_INTEL = VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_QUERY_CREATE_INFO_INTEL, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES, + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_STENCIL_LAYOUT_KHR = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_STENCIL_LAYOUT, + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT_KHR = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT, + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_KHR = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO, + VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO, + VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; +typedef enum VkImageLayout { + VK_IMAGE_LAYOUT_UNDEFINED = 0, + VK_IMAGE_LAYOUT_GENERAL = 1, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7, + VK_IMAGE_LAYOUT_PREINITIALIZED = 8, + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL = 1000117000, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL = 1000117001, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL = 1000241000, + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL = 1000241001, + VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL = 1000241002, + VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL = 1000241003, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR = 1000024000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_LAYOUT_VIDEO_DECODE_SRC_KHR = 1000024001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR = 1000024002, +#endif + VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000, + VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT = 1000218000, + VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR = 1000164003, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_LAYOUT_VIDEO_ENCODE_DST_KHR = 1000299000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR = 1000299001, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_LAYOUT_VIDEO_ENCODE_DPB_KHR = 1000299002, +#endif + VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR = 1000314000, + VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR = 1000314001, + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF +} VkImageLayout; + +typedef enum VkObjectType { + VK_OBJECT_TYPE_UNKNOWN = 0, + VK_OBJECT_TYPE_INSTANCE = 1, + VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2, + VK_OBJECT_TYPE_DEVICE = 3, + VK_OBJECT_TYPE_QUEUE = 4, + VK_OBJECT_TYPE_SEMAPHORE = 5, + VK_OBJECT_TYPE_COMMAND_BUFFER = 6, + VK_OBJECT_TYPE_FENCE = 7, + VK_OBJECT_TYPE_DEVICE_MEMORY = 8, + VK_OBJECT_TYPE_BUFFER = 9, + VK_OBJECT_TYPE_IMAGE = 10, + VK_OBJECT_TYPE_EVENT = 11, + VK_OBJECT_TYPE_QUERY_POOL = 12, + VK_OBJECT_TYPE_BUFFER_VIEW = 13, + VK_OBJECT_TYPE_IMAGE_VIEW = 14, + VK_OBJECT_TYPE_SHADER_MODULE = 15, + VK_OBJECT_TYPE_PIPELINE_CACHE = 16, + VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17, + VK_OBJECT_TYPE_RENDER_PASS = 18, + VK_OBJECT_TYPE_PIPELINE = 19, + VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20, + VK_OBJECT_TYPE_SAMPLER = 21, + VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22, + VK_OBJECT_TYPE_DESCRIPTOR_SET = 23, + VK_OBJECT_TYPE_FRAMEBUFFER = 24, + VK_OBJECT_TYPE_COMMAND_POOL = 25, + VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000, + VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, + VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, + VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, + VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000, + VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001, + VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_OBJECT_TYPE_VIDEO_SESSION_KHR = 1000023000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR = 1000023001, +#endif + VK_OBJECT_TYPE_CU_MODULE_NVX = 1000029000, + VK_OBJECT_TYPE_CU_FUNCTION_NVX = 1000029001, + VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000, + VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR = 1000150000, + VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000, + VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, + VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL = 1000210000, + VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR = 1000268000, + VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV = 1000277000, + VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT = 1000295000, + VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA = 1000366000, + VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE, + VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION, + VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkObjectType; + +typedef enum VkPipelineCacheHeaderVersion { + VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1, + VK_PIPELINE_CACHE_HEADER_VERSION_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCacheHeaderVersion; + +typedef enum VkVendorId { + VK_VENDOR_ID_VIV = 0x10001, + VK_VENDOR_ID_VSI = 0x10002, + VK_VENDOR_ID_KAZAN = 0x10003, + VK_VENDOR_ID_CODEPLAY = 0x10004, + VK_VENDOR_ID_MESA = 0x10005, + VK_VENDOR_ID_POCL = 0x10006, + VK_VENDOR_ID_MAX_ENUM = 0x7FFFFFFF +} VkVendorId; + typedef enum VkSystemAllocationScope { VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 1, VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 2, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 3, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4, - VK_SYSTEM_ALLOCATION_SCOPE_BEGIN_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_COMMAND, - VK_SYSTEM_ALLOCATION_SCOPE_END_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE, - VK_SYSTEM_ALLOCATION_SCOPE_RANGE_SIZE = (VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND + 1), VK_SYSTEM_ALLOCATION_SCOPE_MAX_ENUM = 0x7FFFFFFF } VkSystemAllocationScope; typedef enum VkInternalAllocationType { VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0, - VK_INTERNAL_ALLOCATION_TYPE_BEGIN_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE, - VK_INTERNAL_ALLOCATION_TYPE_END_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE, - VK_INTERNAL_ALLOCATION_TYPE_RANGE_SIZE = (VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE - VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE + 1), VK_INTERNAL_ALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF } VkInternalAllocationType; @@ -703,6 +1387,26 @@ typedef enum VkFormat { VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = 1000066000, + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = 1000066001, + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = 1000066002, + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = 1000066003, + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = 1000066004, + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = 1000066005, + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = 1000066006, + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = 1000066007, + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = 1000066008, + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = 1000066009, + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = 1000066010, + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = 1000066011, + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = 1000066012, + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = 1000066013, + VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT = 1000330000, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT = 1000330001, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT = 1000330002, + VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT = 1000330003, + VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT = 1000340000, + VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT = 1000340001, VK_FORMAT_G8B8G8R8_422_UNORM_KHR = VK_FORMAT_G8B8G8R8_422_UNORM, VK_FORMAT_B8G8R8G8_422_UNORM_KHR = VK_FORMAT_B8G8R8G8_422_UNORM, VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, @@ -737,40 +1441,29 @@ typedef enum VkFormat { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, - VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED, - VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK, - VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1), VK_FORMAT_MAX_ENUM = 0x7FFFFFFF } VkFormat; +typedef enum VkImageTiling { + VK_IMAGE_TILING_OPTIMAL = 0, + VK_IMAGE_TILING_LINEAR = 1, + VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT = 1000158000, + VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF +} VkImageTiling; + typedef enum VkImageType { VK_IMAGE_TYPE_1D = 0, VK_IMAGE_TYPE_2D = 1, VK_IMAGE_TYPE_3D = 2, - VK_IMAGE_TYPE_BEGIN_RANGE = VK_IMAGE_TYPE_1D, - VK_IMAGE_TYPE_END_RANGE = VK_IMAGE_TYPE_3D, - VK_IMAGE_TYPE_RANGE_SIZE = (VK_IMAGE_TYPE_3D - VK_IMAGE_TYPE_1D + 1), VK_IMAGE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkImageType; -typedef enum VkImageTiling { - VK_IMAGE_TILING_OPTIMAL = 0, - VK_IMAGE_TILING_LINEAR = 1, - VK_IMAGE_TILING_BEGIN_RANGE = VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_TILING_END_RANGE = VK_IMAGE_TILING_LINEAR, - VK_IMAGE_TILING_RANGE_SIZE = (VK_IMAGE_TILING_LINEAR - VK_IMAGE_TILING_OPTIMAL + 1), - VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF -} VkImageTiling; - typedef enum VkPhysicalDeviceType { VK_PHYSICAL_DEVICE_TYPE_OTHER = 0, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3, VK_PHYSICAL_DEVICE_TYPE_CPU = 4, - VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE = VK_PHYSICAL_DEVICE_TYPE_OTHER, - VK_PHYSICAL_DEVICE_TYPE_END_RANGE = VK_PHYSICAL_DEVICE_TYPE_CPU, - VK_PHYSICAL_DEVICE_TYPE_RANGE_SIZE = (VK_PHYSICAL_DEVICE_TYPE_CPU - VK_PHYSICAL_DEVICE_TYPE_OTHER + 1), VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkPhysicalDeviceType; @@ -778,57 +1471,27 @@ typedef enum VkQueryType { VK_QUERY_TYPE_OCCLUSION = 0, VK_QUERY_TYPE_PIPELINE_STATISTICS = 1, VK_QUERY_TYPE_TIMESTAMP = 2, - VK_QUERY_TYPE_BEGIN_RANGE = VK_QUERY_TYPE_OCCLUSION, - VK_QUERY_TYPE_END_RANGE = VK_QUERY_TYPE_TIMESTAMP, - VK_QUERY_TYPE_RANGE_SIZE = (VK_QUERY_TYPE_TIMESTAMP - VK_QUERY_TYPE_OCCLUSION + 1), +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR = 1000023000, +#endif + VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT = 1000028004, + VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR = 1000116000, + VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR = 1000150000, + VK_QUERY_TYPE_ACCELERATION_STRUCTURE_SERIALIZATION_SIZE_KHR = 1000150001, + VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV = 1000165000, + VK_QUERY_TYPE_PERFORMANCE_QUERY_INTEL = 1000210000, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_QUERY_TYPE_VIDEO_ENCODE_BITSTREAM_BUFFER_RANGE_KHR = 1000299000, +#endif VK_QUERY_TYPE_MAX_ENUM = 0x7FFFFFFF } VkQueryType; typedef enum VkSharingMode { VK_SHARING_MODE_EXCLUSIVE = 0, VK_SHARING_MODE_CONCURRENT = 1, - VK_SHARING_MODE_BEGIN_RANGE = VK_SHARING_MODE_EXCLUSIVE, - VK_SHARING_MODE_END_RANGE = VK_SHARING_MODE_CONCURRENT, - VK_SHARING_MODE_RANGE_SIZE = (VK_SHARING_MODE_CONCURRENT - VK_SHARING_MODE_EXCLUSIVE + 1), VK_SHARING_MODE_MAX_ENUM = 0x7FFFFFFF } VkSharingMode; -typedef enum VkImageLayout { - VK_IMAGE_LAYOUT_UNDEFINED = 0, - VK_IMAGE_LAYOUT_GENERAL = 1, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7, - VK_IMAGE_LAYOUT_PREINITIALIZED = 8, - VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL = 1000117000, - VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL = 1000117001, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, - VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000, - VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, - VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED, - VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1), - VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF -} VkImageLayout; - -typedef enum VkImageViewType { - VK_IMAGE_VIEW_TYPE_1D = 0, - VK_IMAGE_VIEW_TYPE_2D = 1, - VK_IMAGE_VIEW_TYPE_3D = 2, - VK_IMAGE_VIEW_TYPE_CUBE = 3, - VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4, - VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5, - VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6, - VK_IMAGE_VIEW_TYPE_BEGIN_RANGE = VK_IMAGE_VIEW_TYPE_1D, - VK_IMAGE_VIEW_TYPE_END_RANGE = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, - VK_IMAGE_VIEW_TYPE_RANGE_SIZE = (VK_IMAGE_VIEW_TYPE_CUBE_ARRAY - VK_IMAGE_VIEW_TYPE_1D + 1), - VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF -} VkImageViewType; - typedef enum VkComponentSwizzle { VK_COMPONENT_SWIZZLE_IDENTITY = 0, VK_COMPONENT_SWIZZLE_ZERO = 1, @@ -837,111 +1500,19 @@ typedef enum VkComponentSwizzle { VK_COMPONENT_SWIZZLE_G = 4, VK_COMPONENT_SWIZZLE_B = 5, VK_COMPONENT_SWIZZLE_A = 6, - VK_COMPONENT_SWIZZLE_BEGIN_RANGE = VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_END_RANGE = VK_COMPONENT_SWIZZLE_A, - VK_COMPONENT_SWIZZLE_RANGE_SIZE = (VK_COMPONENT_SWIZZLE_A - VK_COMPONENT_SWIZZLE_IDENTITY + 1), VK_COMPONENT_SWIZZLE_MAX_ENUM = 0x7FFFFFFF } VkComponentSwizzle; -typedef enum VkVertexInputRate { - VK_VERTEX_INPUT_RATE_VERTEX = 0, - VK_VERTEX_INPUT_RATE_INSTANCE = 1, - VK_VERTEX_INPUT_RATE_BEGIN_RANGE = VK_VERTEX_INPUT_RATE_VERTEX, - VK_VERTEX_INPUT_RATE_END_RANGE = VK_VERTEX_INPUT_RATE_INSTANCE, - VK_VERTEX_INPUT_RATE_RANGE_SIZE = (VK_VERTEX_INPUT_RATE_INSTANCE - VK_VERTEX_INPUT_RATE_VERTEX + 1), - VK_VERTEX_INPUT_RATE_MAX_ENUM = 0x7FFFFFFF -} VkVertexInputRate; - -typedef enum VkPrimitiveTopology { - VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0, - VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1, - VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5, - VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6, - VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9, - VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10, - VK_PRIMITIVE_TOPOLOGY_BEGIN_RANGE = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, - VK_PRIMITIVE_TOPOLOGY_END_RANGE = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, - VK_PRIMITIVE_TOPOLOGY_RANGE_SIZE = (VK_PRIMITIVE_TOPOLOGY_PATCH_LIST - VK_PRIMITIVE_TOPOLOGY_POINT_LIST + 1), - VK_PRIMITIVE_TOPOLOGY_MAX_ENUM = 0x7FFFFFFF -} VkPrimitiveTopology; - -typedef enum VkPolygonMode { - VK_POLYGON_MODE_FILL = 0, - VK_POLYGON_MODE_LINE = 1, - VK_POLYGON_MODE_POINT = 2, - VK_POLYGON_MODE_FILL_RECTANGLE_NV = 1000153000, - VK_POLYGON_MODE_BEGIN_RANGE = VK_POLYGON_MODE_FILL, - VK_POLYGON_MODE_END_RANGE = VK_POLYGON_MODE_POINT, - VK_POLYGON_MODE_RANGE_SIZE = (VK_POLYGON_MODE_POINT - VK_POLYGON_MODE_FILL + 1), - VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF -} VkPolygonMode; - -typedef enum VkFrontFace { - VK_FRONT_FACE_COUNTER_CLOCKWISE = 0, - VK_FRONT_FACE_CLOCKWISE = 1, - VK_FRONT_FACE_BEGIN_RANGE = VK_FRONT_FACE_COUNTER_CLOCKWISE, - VK_FRONT_FACE_END_RANGE = VK_FRONT_FACE_CLOCKWISE, - VK_FRONT_FACE_RANGE_SIZE = (VK_FRONT_FACE_CLOCKWISE - VK_FRONT_FACE_COUNTER_CLOCKWISE + 1), - VK_FRONT_FACE_MAX_ENUM = 0x7FFFFFFF -} VkFrontFace; - -typedef enum VkCompareOp { - VK_COMPARE_OP_NEVER = 0, - VK_COMPARE_OP_LESS = 1, - VK_COMPARE_OP_EQUAL = 2, - VK_COMPARE_OP_LESS_OR_EQUAL = 3, - VK_COMPARE_OP_GREATER = 4, - VK_COMPARE_OP_NOT_EQUAL = 5, - VK_COMPARE_OP_GREATER_OR_EQUAL = 6, - VK_COMPARE_OP_ALWAYS = 7, - VK_COMPARE_OP_BEGIN_RANGE = VK_COMPARE_OP_NEVER, - VK_COMPARE_OP_END_RANGE = VK_COMPARE_OP_ALWAYS, - VK_COMPARE_OP_RANGE_SIZE = (VK_COMPARE_OP_ALWAYS - VK_COMPARE_OP_NEVER + 1), - VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF -} VkCompareOp; - -typedef enum VkStencilOp { - VK_STENCIL_OP_KEEP = 0, - VK_STENCIL_OP_ZERO = 1, - VK_STENCIL_OP_REPLACE = 2, - VK_STENCIL_OP_INCREMENT_AND_CLAMP = 3, - VK_STENCIL_OP_DECREMENT_AND_CLAMP = 4, - VK_STENCIL_OP_INVERT = 5, - VK_STENCIL_OP_INCREMENT_AND_WRAP = 6, - VK_STENCIL_OP_DECREMENT_AND_WRAP = 7, - VK_STENCIL_OP_BEGIN_RANGE = VK_STENCIL_OP_KEEP, - VK_STENCIL_OP_END_RANGE = VK_STENCIL_OP_DECREMENT_AND_WRAP, - VK_STENCIL_OP_RANGE_SIZE = (VK_STENCIL_OP_DECREMENT_AND_WRAP - VK_STENCIL_OP_KEEP + 1), - VK_STENCIL_OP_MAX_ENUM = 0x7FFFFFFF -} VkStencilOp; - -typedef enum VkLogicOp { - VK_LOGIC_OP_CLEAR = 0, - VK_LOGIC_OP_AND = 1, - VK_LOGIC_OP_AND_REVERSE = 2, - VK_LOGIC_OP_COPY = 3, - VK_LOGIC_OP_AND_INVERTED = 4, - VK_LOGIC_OP_NO_OP = 5, - VK_LOGIC_OP_XOR = 6, - VK_LOGIC_OP_OR = 7, - VK_LOGIC_OP_NOR = 8, - VK_LOGIC_OP_EQUIVALENT = 9, - VK_LOGIC_OP_INVERT = 10, - VK_LOGIC_OP_OR_REVERSE = 11, - VK_LOGIC_OP_COPY_INVERTED = 12, - VK_LOGIC_OP_OR_INVERTED = 13, - VK_LOGIC_OP_NAND = 14, - VK_LOGIC_OP_SET = 15, - VK_LOGIC_OP_BEGIN_RANGE = VK_LOGIC_OP_CLEAR, - VK_LOGIC_OP_END_RANGE = VK_LOGIC_OP_SET, - VK_LOGIC_OP_RANGE_SIZE = (VK_LOGIC_OP_SET - VK_LOGIC_OP_CLEAR + 1), - VK_LOGIC_OP_MAX_ENUM = 0x7FFFFFFF -} VkLogicOp; +typedef enum VkImageViewType { + VK_IMAGE_VIEW_TYPE_1D = 0, + VK_IMAGE_VIEW_TYPE_2D = 1, + VK_IMAGE_VIEW_TYPE_3D = 2, + VK_IMAGE_VIEW_TYPE_CUBE = 3, + VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4, + VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6, + VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkImageViewType; typedef enum VkBlendFactor { VK_BLEND_FACTOR_ZERO = 0, @@ -963,9 +1534,6 @@ typedef enum VkBlendFactor { VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 16, VK_BLEND_FACTOR_SRC1_ALPHA = 17, VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18, - VK_BLEND_FACTOR_BEGIN_RANGE = VK_BLEND_FACTOR_ZERO, - VK_BLEND_FACTOR_END_RANGE = VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, - VK_BLEND_FACTOR_RANGE_SIZE = (VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA - VK_BLEND_FACTOR_ZERO + 1), VK_BLEND_FACTOR_MAX_ENUM = 0x7FFFFFFF } VkBlendFactor; @@ -1021,12 +1589,21 @@ typedef enum VkBlendOp { VK_BLEND_OP_RED_EXT = 1000148043, VK_BLEND_OP_GREEN_EXT = 1000148044, VK_BLEND_OP_BLUE_EXT = 1000148045, - VK_BLEND_OP_BEGIN_RANGE = VK_BLEND_OP_ADD, - VK_BLEND_OP_END_RANGE = VK_BLEND_OP_MAX, - VK_BLEND_OP_RANGE_SIZE = (VK_BLEND_OP_MAX - VK_BLEND_OP_ADD + 1), VK_BLEND_OP_MAX_ENUM = 0x7FFFFFFF } VkBlendOp; +typedef enum VkCompareOp { + VK_COMPARE_OP_NEVER = 0, + VK_COMPARE_OP_LESS = 1, + VK_COMPARE_OP_EQUAL = 2, + VK_COMPARE_OP_LESS_OR_EQUAL = 3, + VK_COMPARE_OP_GREATER = 4, + VK_COMPARE_OP_NOT_EQUAL = 5, + VK_COMPARE_OP_GREATER_OR_EQUAL = 6, + VK_COMPARE_OP_ALWAYS = 7, + VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF +} VkCompareOp; + typedef enum VkDynamicState { VK_DYNAMIC_STATE_VIEWPORT = 0, VK_DYNAMIC_STATE_SCISSOR = 1, @@ -1040,42 +1617,100 @@ typedef enum VkDynamicState { VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000, VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000, VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000, - VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE, - VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1), + VK_DYNAMIC_STATE_RAY_TRACING_PIPELINE_STACK_SIZE_KHR = 1000347000, + VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV = 1000164004, + VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV = 1000164006, + VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV = 1000205001, + VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR = 1000226000, + VK_DYNAMIC_STATE_LINE_STIPPLE_EXT = 1000259000, + VK_DYNAMIC_STATE_CULL_MODE_EXT = 1000267000, + VK_DYNAMIC_STATE_FRONT_FACE_EXT = 1000267001, + VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT = 1000267002, + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT = 1000267003, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT = 1000267004, + VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT = 1000267005, + VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT = 1000267006, + VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT = 1000267007, + VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT = 1000267008, + VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT = 1000267009, + VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT = 1000267010, + VK_DYNAMIC_STATE_STENCIL_OP_EXT = 1000267011, + VK_DYNAMIC_STATE_VERTEX_INPUT_EXT = 1000352000, + VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT = 1000377000, + VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT = 1000377001, + VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT = 1000377002, + VK_DYNAMIC_STATE_LOGIC_OP_EXT = 1000377003, + VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT = 1000377004, + VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT = 1000381000, VK_DYNAMIC_STATE_MAX_ENUM = 0x7FFFFFFF } VkDynamicState; -typedef enum VkFilter { - VK_FILTER_NEAREST = 0, - VK_FILTER_LINEAR = 1, - VK_FILTER_CUBIC_IMG = 1000015000, - VK_FILTER_BEGIN_RANGE = VK_FILTER_NEAREST, - VK_FILTER_END_RANGE = VK_FILTER_LINEAR, - VK_FILTER_RANGE_SIZE = (VK_FILTER_LINEAR - VK_FILTER_NEAREST + 1), - VK_FILTER_MAX_ENUM = 0x7FFFFFFF -} VkFilter; +typedef enum VkFrontFace { + VK_FRONT_FACE_COUNTER_CLOCKWISE = 0, + VK_FRONT_FACE_CLOCKWISE = 1, + VK_FRONT_FACE_MAX_ENUM = 0x7FFFFFFF +} VkFrontFace; -typedef enum VkSamplerMipmapMode { - VK_SAMPLER_MIPMAP_MODE_NEAREST = 0, - VK_SAMPLER_MIPMAP_MODE_LINEAR = 1, - VK_SAMPLER_MIPMAP_MODE_BEGIN_RANGE = VK_SAMPLER_MIPMAP_MODE_NEAREST, - VK_SAMPLER_MIPMAP_MODE_END_RANGE = VK_SAMPLER_MIPMAP_MODE_LINEAR, - VK_SAMPLER_MIPMAP_MODE_RANGE_SIZE = (VK_SAMPLER_MIPMAP_MODE_LINEAR - VK_SAMPLER_MIPMAP_MODE_NEAREST + 1), - VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF -} VkSamplerMipmapMode; +typedef enum VkVertexInputRate { + VK_VERTEX_INPUT_RATE_VERTEX = 0, + VK_VERTEX_INPUT_RATE_INSTANCE = 1, + VK_VERTEX_INPUT_RATE_MAX_ENUM = 0x7FFFFFFF +} VkVertexInputRate; -typedef enum VkSamplerAddressMode { - VK_SAMPLER_ADDRESS_MODE_REPEAT = 0, - VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1, - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2, - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3, - VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4, - VK_SAMPLER_ADDRESS_MODE_BEGIN_RANGE = VK_SAMPLER_ADDRESS_MODE_REPEAT, - VK_SAMPLER_ADDRESS_MODE_END_RANGE = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - VK_SAMPLER_ADDRESS_MODE_RANGE_SIZE = (VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER - VK_SAMPLER_ADDRESS_MODE_REPEAT + 1), - VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF -} VkSamplerAddressMode; +typedef enum VkPrimitiveTopology { + VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9, + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10, + VK_PRIMITIVE_TOPOLOGY_MAX_ENUM = 0x7FFFFFFF +} VkPrimitiveTopology; + +typedef enum VkPolygonMode { + VK_POLYGON_MODE_FILL = 0, + VK_POLYGON_MODE_LINE = 1, + VK_POLYGON_MODE_POINT = 2, + VK_POLYGON_MODE_FILL_RECTANGLE_NV = 1000153000, + VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF +} VkPolygonMode; + +typedef enum VkStencilOp { + VK_STENCIL_OP_KEEP = 0, + VK_STENCIL_OP_ZERO = 1, + VK_STENCIL_OP_REPLACE = 2, + VK_STENCIL_OP_INCREMENT_AND_CLAMP = 3, + VK_STENCIL_OP_DECREMENT_AND_CLAMP = 4, + VK_STENCIL_OP_INVERT = 5, + VK_STENCIL_OP_INCREMENT_AND_WRAP = 6, + VK_STENCIL_OP_DECREMENT_AND_WRAP = 7, + VK_STENCIL_OP_MAX_ENUM = 0x7FFFFFFF +} VkStencilOp; + +typedef enum VkLogicOp { + VK_LOGIC_OP_CLEAR = 0, + VK_LOGIC_OP_AND = 1, + VK_LOGIC_OP_AND_REVERSE = 2, + VK_LOGIC_OP_COPY = 3, + VK_LOGIC_OP_AND_INVERTED = 4, + VK_LOGIC_OP_NO_OP = 5, + VK_LOGIC_OP_XOR = 6, + VK_LOGIC_OP_OR = 7, + VK_LOGIC_OP_NOR = 8, + VK_LOGIC_OP_EQUIVALENT = 9, + VK_LOGIC_OP_INVERT = 10, + VK_LOGIC_OP_OR_REVERSE = 11, + VK_LOGIC_OP_COPY_INVERTED = 12, + VK_LOGIC_OP_OR_INVERTED = 13, + VK_LOGIC_OP_NAND = 14, + VK_LOGIC_OP_SET = 15, + VK_LOGIC_OP_MAX_ENUM = 0x7FFFFFFF +} VkLogicOp; typedef enum VkBorderColor { VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0, @@ -1084,12 +1719,35 @@ typedef enum VkBorderColor { VK_BORDER_COLOR_INT_OPAQUE_BLACK = 3, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4, VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5, - VK_BORDER_COLOR_BEGIN_RANGE = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, - VK_BORDER_COLOR_END_RANGE = VK_BORDER_COLOR_INT_OPAQUE_WHITE, - VK_BORDER_COLOR_RANGE_SIZE = (VK_BORDER_COLOR_INT_OPAQUE_WHITE - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK + 1), + VK_BORDER_COLOR_FLOAT_CUSTOM_EXT = 1000287003, + VK_BORDER_COLOR_INT_CUSTOM_EXT = 1000287004, VK_BORDER_COLOR_MAX_ENUM = 0x7FFFFFFF } VkBorderColor; +typedef enum VkFilter { + VK_FILTER_NEAREST = 0, + VK_FILTER_LINEAR = 1, + VK_FILTER_CUBIC_IMG = 1000015000, + VK_FILTER_CUBIC_EXT = VK_FILTER_CUBIC_IMG, + VK_FILTER_MAX_ENUM = 0x7FFFFFFF +} VkFilter; + +typedef enum VkSamplerAddressMode { + VK_SAMPLER_ADDRESS_MODE_REPEAT = 0, + VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3, + VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4, + VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE_KHR = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE, + VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerAddressMode; + +typedef enum VkSamplerMipmapMode { + VK_SAMPLER_MIPMAP_MODE_NEAREST = 0, + VK_SAMPLER_MIPMAP_MODE_LINEAR = 1, + VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerMipmapMode; + typedef enum VkDescriptorType { VK_DESCRIPTOR_TYPE_SAMPLER = 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 1, @@ -1102,9 +1760,10 @@ typedef enum VkDescriptorType { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, - VK_DESCRIPTOR_TYPE_BEGIN_RANGE = VK_DESCRIPTOR_TYPE_SAMPLER, - VK_DESCRIPTOR_TYPE_END_RANGE = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, - VK_DESCRIPTOR_TYPE_RANGE_SIZE = (VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT - VK_DESCRIPTOR_TYPE_SAMPLER + 1), + VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT = 1000138000, + VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR = 1000150000, + VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, + VK_DESCRIPTOR_TYPE_MUTABLE_VALVE = 1000351000, VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF } VkDescriptorType; @@ -1112,104 +1771,104 @@ typedef enum VkAttachmentLoadOp { VK_ATTACHMENT_LOAD_OP_LOAD = 0, VK_ATTACHMENT_LOAD_OP_CLEAR = 1, VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2, - VK_ATTACHMENT_LOAD_OP_BEGIN_RANGE = VK_ATTACHMENT_LOAD_OP_LOAD, - VK_ATTACHMENT_LOAD_OP_END_RANGE = VK_ATTACHMENT_LOAD_OP_DONT_CARE, - VK_ATTACHMENT_LOAD_OP_RANGE_SIZE = (VK_ATTACHMENT_LOAD_OP_DONT_CARE - VK_ATTACHMENT_LOAD_OP_LOAD + 1), + VK_ATTACHMENT_LOAD_OP_NONE_EXT = 1000400000, VK_ATTACHMENT_LOAD_OP_MAX_ENUM = 0x7FFFFFFF } VkAttachmentLoadOp; typedef enum VkAttachmentStoreOp { VK_ATTACHMENT_STORE_OP_STORE = 0, VK_ATTACHMENT_STORE_OP_DONT_CARE = 1, - VK_ATTACHMENT_STORE_OP_BEGIN_RANGE = VK_ATTACHMENT_STORE_OP_STORE, - VK_ATTACHMENT_STORE_OP_END_RANGE = VK_ATTACHMENT_STORE_OP_DONT_CARE, - VK_ATTACHMENT_STORE_OP_RANGE_SIZE = (VK_ATTACHMENT_STORE_OP_DONT_CARE - VK_ATTACHMENT_STORE_OP_STORE + 1), + VK_ATTACHMENT_STORE_OP_NONE_KHR = 1000301000, + VK_ATTACHMENT_STORE_OP_NONE_QCOM = VK_ATTACHMENT_STORE_OP_NONE_KHR, + VK_ATTACHMENT_STORE_OP_NONE_EXT = VK_ATTACHMENT_STORE_OP_NONE_KHR, VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF } VkAttachmentStoreOp; typedef enum VkPipelineBindPoint { VK_PIPELINE_BIND_POINT_GRAPHICS = 0, VK_PIPELINE_BIND_POINT_COMPUTE = 1, - VK_PIPELINE_BIND_POINT_BEGIN_RANGE = VK_PIPELINE_BIND_POINT_GRAPHICS, - VK_PIPELINE_BIND_POINT_END_RANGE = VK_PIPELINE_BIND_POINT_COMPUTE, - VK_PIPELINE_BIND_POINT_RANGE_SIZE = (VK_PIPELINE_BIND_POINT_COMPUTE - VK_PIPELINE_BIND_POINT_GRAPHICS + 1), + VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR = 1000165000, + VK_PIPELINE_BIND_POINT_SUBPASS_SHADING_HUAWEI = 1000369003, + VK_PIPELINE_BIND_POINT_RAY_TRACING_NV = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, VK_PIPELINE_BIND_POINT_MAX_ENUM = 0x7FFFFFFF } VkPipelineBindPoint; typedef enum VkCommandBufferLevel { VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0, VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1, - VK_COMMAND_BUFFER_LEVEL_BEGIN_RANGE = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - VK_COMMAND_BUFFER_LEVEL_END_RANGE = VK_COMMAND_BUFFER_LEVEL_SECONDARY, - VK_COMMAND_BUFFER_LEVEL_RANGE_SIZE = (VK_COMMAND_BUFFER_LEVEL_SECONDARY - VK_COMMAND_BUFFER_LEVEL_PRIMARY + 1), VK_COMMAND_BUFFER_LEVEL_MAX_ENUM = 0x7FFFFFFF } VkCommandBufferLevel; typedef enum VkIndexType { VK_INDEX_TYPE_UINT16 = 0, VK_INDEX_TYPE_UINT32 = 1, - VK_INDEX_TYPE_BEGIN_RANGE = VK_INDEX_TYPE_UINT16, - VK_INDEX_TYPE_END_RANGE = VK_INDEX_TYPE_UINT32, - VK_INDEX_TYPE_RANGE_SIZE = (VK_INDEX_TYPE_UINT32 - VK_INDEX_TYPE_UINT16 + 1), + VK_INDEX_TYPE_NONE_KHR = 1000165000, + VK_INDEX_TYPE_UINT8_EXT = 1000265000, + VK_INDEX_TYPE_NONE_NV = VK_INDEX_TYPE_NONE_KHR, VK_INDEX_TYPE_MAX_ENUM = 0x7FFFFFFF } VkIndexType; typedef enum VkSubpassContents { VK_SUBPASS_CONTENTS_INLINE = 0, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1, - VK_SUBPASS_CONTENTS_BEGIN_RANGE = VK_SUBPASS_CONTENTS_INLINE, - VK_SUBPASS_CONTENTS_END_RANGE = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, - VK_SUBPASS_CONTENTS_RANGE_SIZE = (VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS - VK_SUBPASS_CONTENTS_INLINE + 1), VK_SUBPASS_CONTENTS_MAX_ENUM = 0x7FFFFFFF } VkSubpassContents; -typedef enum VkObjectType { - VK_OBJECT_TYPE_UNKNOWN = 0, - VK_OBJECT_TYPE_INSTANCE = 1, - VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2, - VK_OBJECT_TYPE_DEVICE = 3, - VK_OBJECT_TYPE_QUEUE = 4, - VK_OBJECT_TYPE_SEMAPHORE = 5, - VK_OBJECT_TYPE_COMMAND_BUFFER = 6, - VK_OBJECT_TYPE_FENCE = 7, - VK_OBJECT_TYPE_DEVICE_MEMORY = 8, - VK_OBJECT_TYPE_BUFFER = 9, - VK_OBJECT_TYPE_IMAGE = 10, - VK_OBJECT_TYPE_EVENT = 11, - VK_OBJECT_TYPE_QUERY_POOL = 12, - VK_OBJECT_TYPE_BUFFER_VIEW = 13, - VK_OBJECT_TYPE_IMAGE_VIEW = 14, - VK_OBJECT_TYPE_SHADER_MODULE = 15, - VK_OBJECT_TYPE_PIPELINE_CACHE = 16, - VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17, - VK_OBJECT_TYPE_RENDER_PASS = 18, - VK_OBJECT_TYPE_PIPELINE = 19, - VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20, - VK_OBJECT_TYPE_SAMPLER = 21, - VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22, - VK_OBJECT_TYPE_DESCRIPTOR_SET = 23, - VK_OBJECT_TYPE_FRAMEBUFFER = 24, - VK_OBJECT_TYPE_COMMAND_POOL = 25, - VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000, - VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, - VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, - VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, - VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000, - VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001, - VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, - VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000, - VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001, - VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000, - VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000, - VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE, - VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION, - VK_OBJECT_TYPE_BEGIN_RANGE = VK_OBJECT_TYPE_UNKNOWN, - VK_OBJECT_TYPE_END_RANGE = VK_OBJECT_TYPE_COMMAND_POOL, - VK_OBJECT_TYPE_RANGE_SIZE = (VK_OBJECT_TYPE_COMMAND_POOL - VK_OBJECT_TYPE_UNKNOWN + 1), - VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF -} VkObjectType; +typedef enum VkAccessFlagBits { + VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 0x00000001, + VK_ACCESS_INDEX_READ_BIT = 0x00000002, + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004, + VK_ACCESS_UNIFORM_READ_BIT = 0x00000008, + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 0x00000010, + VK_ACCESS_SHADER_READ_BIT = 0x00000020, + VK_ACCESS_SHADER_WRITE_BIT = 0x00000040, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 0x00000080, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400, + VK_ACCESS_TRANSFER_READ_BIT = 0x00000800, + VK_ACCESS_TRANSFER_WRITE_BIT = 0x00001000, + VK_ACCESS_HOST_READ_BIT = 0x00002000, + VK_ACCESS_HOST_WRITE_BIT = 0x00004000, + VK_ACCESS_MEMORY_READ_BIT = 0x00008000, + VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, + VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT = 0x02000000, + VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT = 0x04000000, + VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT = 0x08000000, + VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT = 0x00100000, + VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT = 0x00080000, + VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR = 0x00200000, + VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR = 0x00400000, + VK_ACCESS_FRAGMENT_DENSITY_MAP_READ_BIT_EXT = 0x01000000, + VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR = 0x00800000, + VK_ACCESS_COMMAND_PREPROCESS_READ_BIT_NV = 0x00020000, + VK_ACCESS_COMMAND_PREPROCESS_WRITE_BIT_NV = 0x00040000, + VK_ACCESS_NONE_KHR = 0, + VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV = VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR, + VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR, + VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, + VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkAccessFlagBits; +typedef VkFlags VkAccessFlags; -typedef VkFlags VkInstanceCreateFlags; +typedef enum VkImageAspectFlagBits { + VK_IMAGE_ASPECT_COLOR_BIT = 0x00000001, + VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, + VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, + VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, + VK_IMAGE_ASPECT_PLANE_0_BIT = 0x00000010, + VK_IMAGE_ASPECT_PLANE_1_BIT = 0x00000020, + VK_IMAGE_ASPECT_PLANE_2_BIT = 0x00000040, + VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT = 0x00000080, + VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT = 0x00000100, + VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT = 0x00000200, + VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT = 0x00000400, + VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = VK_IMAGE_ASPECT_PLANE_0_BIT, + VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = VK_IMAGE_ASPECT_PLANE_1_BIT, + VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = VK_IMAGE_ASPECT_PLANE_2_BIT, + VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageAspectFlagBits; +typedef VkFlags VkImageAspectFlags; typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT = 0x00000001, @@ -1234,10 +1893,26 @@ typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT = 0x00200000, VK_FORMAT_FEATURE_DISJOINT_BIT = 0x00400000, VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT = 0x00800000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT = 0x00010000, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = 0x00010000, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_FORMAT_FEATURE_VIDEO_DECODE_OUTPUT_BIT_KHR = 0x02000000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_FORMAT_FEATURE_VIDEO_DECODE_DPB_BIT_KHR = 0x04000000, +#endif + VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR = 0x20000000, + VK_FORMAT_FEATURE_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x01000000, + VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x40000000, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_FORMAT_FEATURE_VIDEO_ENCODE_INPUT_BIT_KHR = 0x08000000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_FORMAT_FEATURE_VIDEO_ENCODE_DPB_BIT_KHR = 0x10000000, +#endif VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT, VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = VK_FORMAT_FEATURE_TRANSFER_DST_BIT, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT, VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT, VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT, VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT, @@ -1245,23 +1920,11 @@ typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT, VK_FORMAT_FEATURE_DISJOINT_BIT_KHR = VK_FORMAT_FEATURE_DISJOINT_BIT, VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR = VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG, VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFormatFeatureFlagBits; typedef VkFlags VkFormatFeatureFlags; -typedef enum VkImageUsageFlagBits { - VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001, - VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002, - VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004, - VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008, - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020, - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040, - VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080, - VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkImageUsageFlagBits; -typedef VkFlags VkImageUsageFlags; - typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 0x00000001, VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, @@ -1275,7 +1938,9 @@ typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_EXTENDED_USAGE_BIT = 0x00000100, VK_IMAGE_CREATE_PROTECTED_BIT = 0x00000800, VK_IMAGE_CREATE_DISJOINT_BIT = 0x00000200, + VK_IMAGE_CREATE_CORNER_SAMPLED_BIT_NV = 0x00002000, VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000, + VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT = 0x00004000, VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT, VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT, VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT, @@ -1298,26 +1963,41 @@ typedef enum VkSampleCountFlagBits { } VkSampleCountFlagBits; typedef VkFlags VkSampleCountFlags; -typedef enum VkQueueFlagBits { - VK_QUEUE_GRAPHICS_BIT = 0x00000001, - VK_QUEUE_COMPUTE_BIT = 0x00000002, - VK_QUEUE_TRANSFER_BIT = 0x00000004, - VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, - VK_QUEUE_PROTECTED_BIT = 0x00000010, - VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkQueueFlagBits; -typedef VkFlags VkQueueFlags; - -typedef enum VkMemoryPropertyFlagBits { - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002, - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004, - VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008, - VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010, - VK_MEMORY_PROPERTY_PROTECTED_BIT = 0x00000020, - VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkMemoryPropertyFlagBits; -typedef VkFlags VkMemoryPropertyFlags; +typedef enum VkImageUsageFlagBits { + VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001, + VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002, + VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004, + VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020, + VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040, + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR = 0x00000400, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR = 0x00000800, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR = 0x00001000, +#endif + VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x00000200, + VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00000100, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_USAGE_VIDEO_ENCODE_DST_BIT_KHR = 0x00002000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR = 0x00004000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR = 0x00008000, +#endif + VK_IMAGE_USAGE_INVOCATION_MASK_BIT_HUAWEI = 0x00040000, + VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV = VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, + VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageUsageFlagBits; +typedef VkFlags VkImageUsageFlags; +typedef VkFlags VkInstanceCreateFlags; typedef enum VkMemoryHeapFlagBits { VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001, @@ -1326,6 +2006,36 @@ typedef enum VkMemoryHeapFlagBits { VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryHeapFlagBits; typedef VkFlags VkMemoryHeapFlags; + +typedef enum VkMemoryPropertyFlagBits { + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002, + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004, + VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008, + VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010, + VK_MEMORY_PROPERTY_PROTECTED_BIT = 0x00000020, + VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD = 0x00000040, + VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD = 0x00000080, + VK_MEMORY_PROPERTY_RDMA_CAPABLE_BIT_NV = 0x00000100, + VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkMemoryPropertyFlagBits; +typedef VkFlags VkMemoryPropertyFlags; + +typedef enum VkQueueFlagBits { + VK_QUEUE_GRAPHICS_BIT = 0x00000001, + VK_QUEUE_COMPUTE_BIT = 0x00000002, + VK_QUEUE_TRANSFER_BIT = 0x00000004, + VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, + VK_QUEUE_PROTECTED_BIT = 0x00000010, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_QUEUE_VIDEO_DECODE_BIT_KHR = 0x00000020, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_QUEUE_VIDEO_ENCODE_BIT_KHR = 0x00000040, +#endif + VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueueFlagBits; +typedef VkFlags VkQueueFlags; typedef VkFlags VkDeviceCreateFlags; typedef enum VkDeviceQueueCreateFlagBits { @@ -1352,26 +2062,29 @@ typedef enum VkPipelineStageFlagBits { VK_PIPELINE_STAGE_HOST_BIT = 0x00004000, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, - VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000, + VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT = 0x01000000, + VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT = 0x00040000, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR = 0x02000000, + VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR = 0x00200000, + VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV = 0x00080000, + VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV = 0x00100000, + VK_PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT_EXT = 0x00800000, + VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00400000, + VK_PIPELINE_STAGE_COMMAND_PREPROCESS_BIT_NV = 0x00020000, + VK_PIPELINE_STAGE_NONE_KHR = 0, + VK_PIPELINE_STAGE_SHADING_RATE_IMAGE_BIT_NV = VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, + VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_NV = VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV = VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineStageFlagBits; typedef VkFlags VkPipelineStageFlags; typedef VkFlags VkMemoryMapFlags; -typedef enum VkImageAspectFlagBits { - VK_IMAGE_ASPECT_COLOR_BIT = 0x00000001, - VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, - VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, - VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, - VK_IMAGE_ASPECT_PLANE_0_BIT = 0x00000010, - VK_IMAGE_ASPECT_PLANE_1_BIT = 0x00000020, - VK_IMAGE_ASPECT_PLANE_2_BIT = 0x00000040, - VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = VK_IMAGE_ASPECT_PLANE_0_BIT, - VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = VK_IMAGE_ASPECT_PLANE_1_BIT, - VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = VK_IMAGE_ASPECT_PLANE_2_BIT, - VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkImageAspectFlagBits; -typedef VkFlags VkImageAspectFlags; +typedef enum VkSparseMemoryBindFlagBits { + VK_SPARSE_MEMORY_BIND_METADATA_BIT = 0x00000001, + VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSparseMemoryBindFlagBits; +typedef VkFlags VkSparseMemoryBindFlags; typedef enum VkSparseImageFormatFlagBits { VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 0x00000001, @@ -1381,20 +2094,18 @@ typedef enum VkSparseImageFormatFlagBits { } VkSparseImageFormatFlagBits; typedef VkFlags VkSparseImageFormatFlags; -typedef enum VkSparseMemoryBindFlagBits { - VK_SPARSE_MEMORY_BIND_METADATA_BIT = 0x00000001, - VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkSparseMemoryBindFlagBits; -typedef VkFlags VkSparseMemoryBindFlags; - typedef enum VkFenceCreateFlagBits { VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001, VK_FENCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFenceCreateFlagBits; typedef VkFlags VkFenceCreateFlags; typedef VkFlags VkSemaphoreCreateFlags; + +typedef enum VkEventCreateFlagBits { + VK_EVENT_CREATE_DEVICE_ONLY_BIT_KHR = 0x00000001, + VK_EVENT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkEventCreateFlagBits; typedef VkFlags VkEventCreateFlags; -typedef VkFlags VkQueryPoolCreateFlags; typedef enum VkQueryPipelineStatisticFlagBits { VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT = 0x00000001, @@ -1411,12 +2122,16 @@ typedef enum VkQueryPipelineStatisticFlagBits { VK_QUERY_PIPELINE_STATISTIC_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryPipelineStatisticFlagBits; typedef VkFlags VkQueryPipelineStatisticFlags; +typedef VkFlags VkQueryPoolCreateFlags; typedef enum VkQueryResultFlagBits { VK_QUERY_RESULT_64_BIT = 0x00000001, VK_QUERY_RESULT_WAIT_BIT = 0x00000002, VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 0x00000004, VK_QUERY_RESULT_PARTIAL_BIT = 0x00000008, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_QUERY_RESULT_WITH_STATUS_BIT_KHR = 0x00000010, +#endif VK_QUERY_RESULT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkQueryResultFlagBits; typedef VkFlags VkQueryResultFlags; @@ -1426,6 +2141,9 @@ typedef enum VkBufferCreateFlagBits { VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 0x00000004, VK_BUFFER_CREATE_PROTECTED_BIT = 0x00000008, + VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT = 0x00000010, + VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_EXT = VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT, + VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_KHR = VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT, VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkBufferCreateFlagBits; typedef VkFlags VkBufferCreateFlags; @@ -1440,25 +2158,91 @@ typedef enum VkBufferUsageFlagBits { VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 0x00000040, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 0x00000080, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 0x00000100, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT = 0x00020000, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR = 0x00002000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_BUFFER_USAGE_VIDEO_DECODE_DST_BIT_KHR = 0x00004000, +#endif + VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT = 0x00000800, + VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT = 0x00001000, + VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT = 0x00000200, + VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR = 0x00080000, + VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR = 0x00100000, + VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR = 0x00000400, +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_BUFFER_USAGE_VIDEO_ENCODE_DST_BIT_KHR = 0x00008000, +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS + VK_BUFFER_USAGE_VIDEO_ENCODE_SRC_BIT_KHR = 0x00010000, +#endif + VK_BUFFER_USAGE_RAY_TRACING_BIT_NV = VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkBufferUsageFlagBits; typedef VkFlags VkBufferUsageFlags; typedef VkFlags VkBufferViewCreateFlags; + +typedef enum VkImageViewCreateFlagBits { + VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT = 0x00000001, + VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DEFERRED_BIT_EXT = 0x00000002, + VK_IMAGE_VIEW_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageViewCreateFlagBits; typedef VkFlags VkImageViewCreateFlags; typedef VkFlags VkShaderModuleCreateFlags; + +typedef enum VkPipelineCacheCreateFlagBits { + VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT_EXT = 0x00000001, + VK_PIPELINE_CACHE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCacheCreateFlagBits; typedef VkFlags VkPipelineCacheCreateFlags; +typedef enum VkColorComponentFlagBits { + VK_COLOR_COMPONENT_R_BIT = 0x00000001, + VK_COLOR_COMPONENT_G_BIT = 0x00000002, + VK_COLOR_COMPONENT_B_BIT = 0x00000004, + VK_COLOR_COMPONENT_A_BIT = 0x00000008, + VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkColorComponentFlagBits; +typedef VkFlags VkColorComponentFlags; + typedef enum VkPipelineCreateFlagBits { VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001, VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002, VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004, VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT = 0x00000008, - VK_PIPELINE_CREATE_DISPATCH_BASE = 0x00000010, + VK_PIPELINE_CREATE_DISPATCH_BASE_BIT = 0x00000010, + VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00200000, + VK_PIPELINE_RASTERIZATION_STATE_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT = 0x00400000, + VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_BIT_KHR = 0x00004000, + VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_BIT_KHR = 0x00008000, + VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_MISS_SHADERS_BIT_KHR = 0x00010000, + VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR = 0x00020000, + VK_PIPELINE_CREATE_RAY_TRACING_SKIP_TRIANGLES_BIT_KHR = 0x00001000, + VK_PIPELINE_CREATE_RAY_TRACING_SKIP_AABBS_BIT_KHR = 0x00002000, + VK_PIPELINE_CREATE_RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR = 0x00080000, + VK_PIPELINE_CREATE_DEFER_COMPILE_BIT_NV = 0x00000020, + VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR = 0x00000040, + VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR = 0x00000080, + VK_PIPELINE_CREATE_INDIRECT_BINDABLE_BIT_NV = 0x00040000, + VK_PIPELINE_CREATE_LIBRARY_BIT_KHR = 0x00000800, + VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT = 0x00000100, + VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT = 0x00000200, + VK_PIPELINE_CREATE_RAY_TRACING_ALLOW_MOTION_BIT_NV = 0x00100000, + VK_PIPELINE_CREATE_DISPATCH_BASE = VK_PIPELINE_CREATE_DISPATCH_BASE_BIT, VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHR = VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT, VK_PIPELINE_CREATE_DISPATCH_BASE_KHR = VK_PIPELINE_CREATE_DISPATCH_BASE, VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineCreateFlagBits; typedef VkFlags VkPipelineCreateFlags; + +typedef enum VkPipelineShaderStageCreateFlagBits { + VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT = 0x00000001, + VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT = 0x00000002, + VK_PIPELINE_SHADER_STAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineShaderStageCreateFlagBits; typedef VkFlags VkPipelineShaderStageCreateFlags; typedef enum VkShaderStageFlagBits { @@ -1470,13 +2254,23 @@ typedef enum VkShaderStageFlagBits { VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020, VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, VK_SHADER_STAGE_ALL = 0x7FFFFFFF, + VK_SHADER_STAGE_RAYGEN_BIT_KHR = 0x00000100, + VK_SHADER_STAGE_ANY_HIT_BIT_KHR = 0x00000200, + VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR = 0x00000400, + VK_SHADER_STAGE_MISS_BIT_KHR = 0x00000800, + VK_SHADER_STAGE_INTERSECTION_BIT_KHR = 0x00001000, + VK_SHADER_STAGE_CALLABLE_BIT_KHR = 0x00002000, + VK_SHADER_STAGE_TASK_BIT_NV = 0x00000040, + VK_SHADER_STAGE_MESH_BIT_NV = 0x00000080, + VK_SHADER_STAGE_SUBPASS_SHADING_BIT_HUAWEI = 0x00004000, + VK_SHADER_STAGE_RAYGEN_BIT_NV = VK_SHADER_STAGE_RAYGEN_BIT_KHR, + VK_SHADER_STAGE_ANY_HIT_BIT_NV = VK_SHADER_STAGE_ANY_HIT_BIT_KHR, + VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, + VK_SHADER_STAGE_MISS_BIT_NV = VK_SHADER_STAGE_MISS_BIT_KHR, + VK_SHADER_STAGE_INTERSECTION_BIT_NV = VK_SHADER_STAGE_INTERSECTION_BIT_KHR, + VK_SHADER_STAGE_CALLABLE_BIT_NV = VK_SHADER_STAGE_CALLABLE_BIT_KHR, VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkShaderStageFlagBits; -typedef VkFlags VkPipelineVertexInputStateCreateFlags; -typedef VkFlags VkPipelineInputAssemblyStateCreateFlags; -typedef VkFlags VkPipelineTessellationStateCreateFlags; -typedef VkFlags VkPipelineViewportStateCreateFlags; -typedef VkFlags VkPipelineRasterizationStateCreateFlags; typedef enum VkCullModeFlagBits { VK_CULL_MODE_NONE = 0, @@ -1486,39 +2280,43 @@ typedef enum VkCullModeFlagBits { VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkCullModeFlagBits; typedef VkFlags VkCullModeFlags; +typedef VkFlags VkPipelineVertexInputStateCreateFlags; +typedef VkFlags VkPipelineInputAssemblyStateCreateFlags; +typedef VkFlags VkPipelineTessellationStateCreateFlags; +typedef VkFlags VkPipelineViewportStateCreateFlags; +typedef VkFlags VkPipelineRasterizationStateCreateFlags; typedef VkFlags VkPipelineMultisampleStateCreateFlags; typedef VkFlags VkPipelineDepthStencilStateCreateFlags; typedef VkFlags VkPipelineColorBlendStateCreateFlags; - -typedef enum VkColorComponentFlagBits { - VK_COLOR_COMPONENT_R_BIT = 0x00000001, - VK_COLOR_COMPONENT_G_BIT = 0x00000002, - VK_COLOR_COMPONENT_B_BIT = 0x00000004, - VK_COLOR_COMPONENT_A_BIT = 0x00000008, - VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkColorComponentFlagBits; -typedef VkFlags VkColorComponentFlags; typedef VkFlags VkPipelineDynamicStateCreateFlags; typedef VkFlags VkPipelineLayoutCreateFlags; typedef VkFlags VkShaderStageFlags; -typedef VkFlags VkSamplerCreateFlags; -typedef enum VkDescriptorSetLayoutCreateFlagBits { - VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR = 0x00000001, - VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT = 0x00000002, - VK_DESCRIPTOR_SET_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkDescriptorSetLayoutCreateFlagBits; -typedef VkFlags VkDescriptorSetLayoutCreateFlags; +typedef enum VkSamplerCreateFlagBits { + VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT = 0x00000001, + VK_SAMPLER_CREATE_SUBSAMPLED_COARSE_RECONSTRUCTION_BIT_EXT = 0x00000002, + VK_SAMPLER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSamplerCreateFlagBits; +typedef VkFlags VkSamplerCreateFlags; typedef enum VkDescriptorPoolCreateFlagBits { VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001, - VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT = 0x00000002, + VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT = 0x00000002, + VK_DESCRIPTOR_POOL_CREATE_HOST_ONLY_BIT_VALVE = 0x00000004, + VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDescriptorPoolCreateFlagBits; typedef VkFlags VkDescriptorPoolCreateFlags; typedef VkFlags VkDescriptorPoolResetFlags; -typedef VkFlags VkFramebufferCreateFlags; -typedef VkFlags VkRenderPassCreateFlags; + +typedef enum VkDescriptorSetLayoutCreateFlagBits { + VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT = 0x00000002, + VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR = 0x00000001, + VK_DESCRIPTOR_SET_LAYOUT_CREATE_HOST_ONLY_POOL_BIT_VALVE = 0x00000004, + VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, + VK_DESCRIPTOR_SET_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorSetLayoutCreateFlagBits; +typedef VkFlags VkDescriptorSetLayoutCreateFlags; typedef enum VkAttachmentDescriptionFlagBits { VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 0x00000001, @@ -1526,38 +2324,6 @@ typedef enum VkAttachmentDescriptionFlagBits { } VkAttachmentDescriptionFlagBits; typedef VkFlags VkAttachmentDescriptionFlags; -typedef enum VkSubpassDescriptionFlagBits { - VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX = 0x00000001, - VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002, - VK_SUBPASS_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkSubpassDescriptionFlagBits; -typedef VkFlags VkSubpassDescriptionFlags; - -typedef enum VkAccessFlagBits { - VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 0x00000001, - VK_ACCESS_INDEX_READ_BIT = 0x00000002, - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004, - VK_ACCESS_UNIFORM_READ_BIT = 0x00000008, - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 0x00000010, - VK_ACCESS_SHADER_READ_BIT = 0x00000020, - VK_ACCESS_SHADER_WRITE_BIT = 0x00000040, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 0x00000080, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400, - VK_ACCESS_TRANSFER_READ_BIT = 0x00000800, - VK_ACCESS_TRANSFER_WRITE_BIT = 0x00001000, - VK_ACCESS_HOST_READ_BIT = 0x00002000, - VK_ACCESS_HOST_WRITE_BIT = 0x00004000, - VK_ACCESS_MEMORY_READ_BIT = 0x00008000, - VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, - VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000, - VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000, - VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT = 0x00080000, - VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF -} VkAccessFlagBits; -typedef VkFlags VkAccessFlags; - typedef enum VkDependencyFlagBits { VK_DEPENDENCY_BY_REGION_BIT = 0x00000001, VK_DEPENDENCY_DEVICE_GROUP_BIT = 0x00000004, @@ -1568,6 +2334,28 @@ typedef enum VkDependencyFlagBits { } VkDependencyFlagBits; typedef VkFlags VkDependencyFlags; +typedef enum VkFramebufferCreateFlagBits { + VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT = 0x00000001, + VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT_KHR = VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, + VK_FRAMEBUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFramebufferCreateFlagBits; +typedef VkFlags VkFramebufferCreateFlags; + +typedef enum VkRenderPassCreateFlagBits { + VK_RENDER_PASS_CREATE_TRANSFORM_BIT_QCOM = 0x00000002, + VK_RENDER_PASS_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkRenderPassCreateFlagBits; +typedef VkFlags VkRenderPassCreateFlags; + +typedef enum VkSubpassDescriptionFlagBits { + VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX = 0x00000001, + VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002, + VK_SUBPASS_DESCRIPTION_FRAGMENT_REGION_BIT_QCOM = 0x00000004, + VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM = 0x00000008, + VK_SUBPASS_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSubpassDescriptionFlagBits; +typedef VkFlags VkSubpassDescriptionFlags; + typedef enum VkCommandPoolCreateFlagBits { VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 0x00000001, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 0x00000002, @@ -1605,31 +2393,116 @@ typedef VkFlags VkCommandBufferResetFlags; typedef enum VkStencilFaceFlagBits { VK_STENCIL_FACE_FRONT_BIT = 0x00000001, VK_STENCIL_FACE_BACK_BIT = 0x00000002, - VK_STENCIL_FRONT_AND_BACK = 0x00000003, + VK_STENCIL_FACE_FRONT_AND_BACK = 0x00000003, + VK_STENCIL_FRONT_AND_BACK = VK_STENCIL_FACE_FRONT_AND_BACK, VK_STENCIL_FACE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkStencilFaceFlagBits; typedef VkFlags VkStencilFaceFlags; +typedef struct VkExtent2D { + uint32_t width; + uint32_t height; +} VkExtent2D; -typedef struct VkApplicationInfo { +typedef struct VkExtent3D { + uint32_t width; + uint32_t height; + uint32_t depth; +} VkExtent3D; + +typedef struct VkOffset2D { + int32_t x; + int32_t y; +} VkOffset2D; + +typedef struct VkOffset3D { + int32_t x; + int32_t y; + int32_t z; +} VkOffset3D; + +typedef struct VkRect2D { + VkOffset2D offset; + VkExtent2D extent; +} VkRect2D; + +typedef struct VkBaseInStructure { + VkStructureType sType; + const struct VkBaseInStructure* pNext; +} VkBaseInStructure; + +typedef struct VkBaseOutStructure { + VkStructureType sType; + struct VkBaseOutStructure* pNext; +} VkBaseOutStructure; + +typedef struct VkBufferMemoryBarrier { VkStructureType sType; const void* pNext; - const char* pApplicationName; - uint32_t applicationVersion; - const char* pEngineName; - uint32_t engineVersion; - uint32_t apiVersion; -} VkApplicationInfo; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize size; +} VkBufferMemoryBarrier; -typedef struct VkInstanceCreateInfo { - VkStructureType sType; - const void* pNext; - VkInstanceCreateFlags flags; - const VkApplicationInfo* pApplicationInfo; - uint32_t enabledLayerCount; - const char* const* ppEnabledLayerNames; - uint32_t enabledExtensionCount; - const char* const* ppEnabledExtensionNames; -} VkInstanceCreateInfo; +typedef struct VkDispatchIndirectCommand { + uint32_t x; + uint32_t y; + uint32_t z; +} VkDispatchIndirectCommand; + +typedef struct VkDrawIndexedIndirectCommand { + uint32_t indexCount; + uint32_t instanceCount; + uint32_t firstIndex; + int32_t vertexOffset; + uint32_t firstInstance; +} VkDrawIndexedIndirectCommand; + +typedef struct VkDrawIndirectCommand { + uint32_t vertexCount; + uint32_t instanceCount; + uint32_t firstVertex; + uint32_t firstInstance; +} VkDrawIndirectCommand; + +typedef struct VkImageSubresourceRange { + VkImageAspectFlags aspectMask; + uint32_t baseMipLevel; + uint32_t levelCount; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkImageSubresourceRange; + +typedef struct VkImageMemoryBarrier { + VkStructureType sType; + const void* pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkImageLayout oldLayout; + VkImageLayout newLayout; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkImage image; + VkImageSubresourceRange subresourceRange; +} VkImageMemoryBarrier; + +typedef struct VkMemoryBarrier { + VkStructureType sType; + const void* pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; +} VkMemoryBarrier; + +typedef struct VkPipelineCacheHeaderVersionOne { + uint32_t headerSize; + VkPipelineCacheHeaderVersion headerVersion; + uint32_t vendorID; + uint32_t deviceID; + uint8_t pipelineCacheUUID[VK_UUID_SIZE]; +} VkPipelineCacheHeaderVersionOne; typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)( void* pUserData, @@ -1637,13 +2510,6 @@ typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)( size_t alignment, VkSystemAllocationScope allocationScope); -typedef void* (VKAPI_PTR *PFN_vkReallocationFunction)( - void* pUserData, - void* pOriginal, - size_t size, - size_t alignment, - VkSystemAllocationScope allocationScope); - typedef void (VKAPI_PTR *PFN_vkFreeFunction)( void* pUserData, void* pMemory); @@ -1660,6 +2526,14 @@ typedef void (VKAPI_PTR *PFN_vkInternalFreeNotification)( VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); +typedef void* (VKAPI_PTR *PFN_vkReallocationFunction)( + void* pUserData, + void* pOriginal, + size_t size, + size_t alignment, + VkSystemAllocationScope allocationScope); + +typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); typedef struct VkAllocationCallbacks { void* pUserData; PFN_vkAllocationFunction pfnAllocation; @@ -1669,6 +2543,51 @@ typedef struct VkAllocationCallbacks { PFN_vkInternalFreeNotification pfnInternalFree; } VkAllocationCallbacks; +typedef struct VkApplicationInfo { + VkStructureType sType; + const void* pNext; + const char* pApplicationName; + uint32_t applicationVersion; + const char* pEngineName; + uint32_t engineVersion; + uint32_t apiVersion; +} VkApplicationInfo; + +typedef struct VkFormatProperties { + VkFormatFeatureFlags linearTilingFeatures; + VkFormatFeatureFlags optimalTilingFeatures; + VkFormatFeatureFlags bufferFeatures; +} VkFormatProperties; + +typedef struct VkImageFormatProperties { + VkExtent3D maxExtent; + uint32_t maxMipLevels; + uint32_t maxArrayLayers; + VkSampleCountFlags sampleCounts; + VkDeviceSize maxResourceSize; +} VkImageFormatProperties; + +typedef struct VkInstanceCreateInfo { + VkStructureType sType; + const void* pNext; + VkInstanceCreateFlags flags; + const VkApplicationInfo* pApplicationInfo; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; +} VkInstanceCreateInfo; + +typedef struct VkMemoryHeap { + VkDeviceSize size; + VkMemoryHeapFlags flags; +} VkMemoryHeap; + +typedef struct VkMemoryType { + VkMemoryPropertyFlags propertyFlags; + uint32_t heapIndex; +} VkMemoryType; + typedef struct VkPhysicalDeviceFeatures { VkBool32 robustBufferAccess; VkBool32 fullDrawIndexUint32; @@ -1727,26 +2646,6 @@ typedef struct VkPhysicalDeviceFeatures { VkBool32 inheritedQueries; } VkPhysicalDeviceFeatures; -typedef struct VkFormatProperties { - VkFormatFeatureFlags linearTilingFeatures; - VkFormatFeatureFlags optimalTilingFeatures; - VkFormatFeatureFlags bufferFeatures; -} VkFormatProperties; - -typedef struct VkExtent3D { - uint32_t width; - uint32_t height; - uint32_t depth; -} VkExtent3D; - -typedef struct VkImageFormatProperties { - VkExtent3D maxExtent; - uint32_t maxMipLevels; - uint32_t maxArrayLayers; - VkSampleCountFlags sampleCounts; - VkDeviceSize maxResourceSize; -} VkImageFormatProperties; - typedef struct VkPhysicalDeviceLimits { uint32_t maxImageDimension1D; uint32_t maxImageDimension2D; @@ -1856,6 +2755,13 @@ typedef struct VkPhysicalDeviceLimits { VkDeviceSize nonCoherentAtomSize; } VkPhysicalDeviceLimits; +typedef struct VkPhysicalDeviceMemoryProperties { + uint32_t memoryTypeCount; + VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES]; + uint32_t memoryHeapCount; + VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS]; +} VkPhysicalDeviceMemoryProperties; + typedef struct VkPhysicalDeviceSparseProperties { VkBool32 residencyStandard2DBlockShape; VkBool32 residencyStandard2DMultisampleBlockShape; @@ -1883,24 +2789,6 @@ typedef struct VkQueueFamilyProperties { VkExtent3D minImageTransferGranularity; } VkQueueFamilyProperties; -typedef struct VkMemoryType { - VkMemoryPropertyFlags propertyFlags; - uint32_t heapIndex; -} VkMemoryType; - -typedef struct VkMemoryHeap { - VkDeviceSize size; - VkMemoryHeapFlags flags; -} VkMemoryHeap; - -typedef struct VkPhysicalDeviceMemoryProperties { - uint32_t memoryTypeCount; - VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES]; - uint32_t memoryHeapCount; - VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS]; -} VkPhysicalDeviceMemoryProperties; - -typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); typedef struct VkDeviceQueueCreateInfo { VkStructureType sType; const void* pNext; @@ -1947,13 +2835,6 @@ typedef struct VkSubmitInfo { const VkSemaphore* pSignalSemaphores; } VkSubmitInfo; -typedef struct VkMemoryAllocateInfo { - VkStructureType sType; - const void* pNext; - VkDeviceSize allocationSize; - uint32_t memoryTypeIndex; -} VkMemoryAllocateInfo; - typedef struct VkMappedMemoryRange { VkStructureType sType; const void* pNext; @@ -1962,26 +2843,19 @@ typedef struct VkMappedMemoryRange { VkDeviceSize size; } VkMappedMemoryRange; +typedef struct VkMemoryAllocateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceSize allocationSize; + uint32_t memoryTypeIndex; +} VkMemoryAllocateInfo; + typedef struct VkMemoryRequirements { VkDeviceSize size; VkDeviceSize alignment; uint32_t memoryTypeBits; } VkMemoryRequirements; -typedef struct VkSparseImageFormatProperties { - VkImageAspectFlags aspectMask; - VkExtent3D imageGranularity; - VkSparseImageFormatFlags flags; -} VkSparseImageFormatProperties; - -typedef struct VkSparseImageMemoryRequirements { - VkSparseImageFormatProperties formatProperties; - uint32_t imageMipTailFirstLod; - VkDeviceSize imageMipTailSize; - VkDeviceSize imageMipTailOffset; - VkDeviceSize imageMipTailStride; -} VkSparseImageMemoryRequirements; - typedef struct VkSparseMemoryBind { VkDeviceSize resourceOffset; VkDeviceSize size; @@ -2008,12 +2882,6 @@ typedef struct VkImageSubresource { uint32_t arrayLayer; } VkImageSubresource; -typedef struct VkOffset3D { - int32_t x; - int32_t y; - int32_t z; -} VkOffset3D; - typedef struct VkSparseImageMemoryBind { VkImageSubresource subresource; VkOffset3D offset; @@ -2044,6 +2912,20 @@ typedef struct VkBindSparseInfo { const VkSemaphore* pSignalSemaphores; } VkBindSparseInfo; +typedef struct VkSparseImageFormatProperties { + VkImageAspectFlags aspectMask; + VkExtent3D imageGranularity; + VkSparseImageFormatFlags flags; +} VkSparseImageFormatProperties; + +typedef struct VkSparseImageMemoryRequirements { + VkSparseImageFormatProperties formatProperties; + uint32_t imageMipTailFirstLod; + VkDeviceSize imageMipTailSize; + VkDeviceSize imageMipTailOffset; + VkDeviceSize imageMipTailStride; +} VkSparseImageMemoryRequirements; + typedef struct VkFenceCreateInfo { VkStructureType sType; const void* pNext; @@ -2125,14 +3007,6 @@ typedef struct VkComponentMapping { VkComponentSwizzle a; } VkComponentMapping; -typedef struct VkImageSubresourceRange { - VkImageAspectFlags aspectMask; - uint32_t baseMipLevel; - uint32_t levelCount; - uint32_t baseArrayLayer; - uint32_t layerCount; -} VkImageSubresourceRange; - typedef struct VkImageViewCreateInfo { VkStructureType sType; const void* pNext; @@ -2183,6 +3057,16 @@ typedef struct VkPipelineShaderStageCreateInfo { const VkSpecializationInfo* pSpecializationInfo; } VkPipelineShaderStageCreateInfo; +typedef struct VkComputePipelineCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineCreateFlags flags; + VkPipelineShaderStageCreateInfo stage; + VkPipelineLayout layout; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkComputePipelineCreateInfo; + typedef struct VkVertexInputBindingDescription { uint32_t binding; uint32_t stride; @@ -2230,21 +3114,6 @@ typedef struct VkViewport { float maxDepth; } VkViewport; -typedef struct VkOffset2D { - int32_t x; - int32_t y; -} VkOffset2D; - -typedef struct VkExtent2D { - uint32_t width; - uint32_t height; -} VkExtent2D; - -typedef struct VkRect2D { - VkOffset2D offset; - VkExtent2D extent; -} VkRect2D; - typedef struct VkPipelineViewportStateCreateInfo { VkStructureType sType; const void* pNext; @@ -2360,16 +3229,6 @@ typedef struct VkGraphicsPipelineCreateInfo { int32_t basePipelineIndex; } VkGraphicsPipelineCreateInfo; -typedef struct VkComputePipelineCreateInfo { - VkStructureType sType; - const void* pNext; - VkPipelineCreateFlags flags; - VkPipelineShaderStageCreateInfo stage; - VkPipelineLayout layout; - VkPipeline basePipelineHandle; - int32_t basePipelineIndex; -} VkComputePipelineCreateInfo; - typedef struct VkPushConstantRange { VkShaderStageFlags stageFlags; uint32_t offset; @@ -2407,21 +3266,29 @@ typedef struct VkSamplerCreateInfo { VkBool32 unnormalizedCoordinates; } VkSamplerCreateInfo; -typedef struct VkDescriptorSetLayoutBinding { - uint32_t binding; - VkDescriptorType descriptorType; - uint32_t descriptorCount; - VkShaderStageFlags stageFlags; - const VkSampler* pImmutableSamplers; -} VkDescriptorSetLayoutBinding; +typedef struct VkCopyDescriptorSet { + VkStructureType sType; + const void* pNext; + VkDescriptorSet srcSet; + uint32_t srcBinding; + uint32_t srcArrayElement; + VkDescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; +} VkCopyDescriptorSet; -typedef struct VkDescriptorSetLayoutCreateInfo { - VkStructureType sType; - const void* pNext; - VkDescriptorSetLayoutCreateFlags flags; - uint32_t bindingCount; - const VkDescriptorSetLayoutBinding* pBindings; -} VkDescriptorSetLayoutCreateInfo; +typedef struct VkDescriptorBufferInfo { + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize range; +} VkDescriptorBufferInfo; + +typedef struct VkDescriptorImageInfo { + VkSampler sampler; + VkImageView imageView; + VkImageLayout imageLayout; +} VkDescriptorImageInfo; typedef struct VkDescriptorPoolSize { VkDescriptorType type; @@ -2445,17 +3312,21 @@ typedef struct VkDescriptorSetAllocateInfo { const VkDescriptorSetLayout* pSetLayouts; } VkDescriptorSetAllocateInfo; -typedef struct VkDescriptorImageInfo { - VkSampler sampler; - VkImageView imageView; - VkImageLayout imageLayout; -} VkDescriptorImageInfo; +typedef struct VkDescriptorSetLayoutBinding { + uint32_t binding; + VkDescriptorType descriptorType; + uint32_t descriptorCount; + VkShaderStageFlags stageFlags; + const VkSampler* pImmutableSamplers; +} VkDescriptorSetLayoutBinding; -typedef struct VkDescriptorBufferInfo { - VkBuffer buffer; - VkDeviceSize offset; - VkDeviceSize range; -} VkDescriptorBufferInfo; +typedef struct VkDescriptorSetLayoutCreateInfo { + VkStructureType sType; + const void* pNext; + VkDescriptorSetLayoutCreateFlags flags; + uint32_t bindingCount; + const VkDescriptorSetLayoutBinding* pBindings; +} VkDescriptorSetLayoutCreateInfo; typedef struct VkWriteDescriptorSet { VkStructureType sType; @@ -2470,30 +3341,6 @@ typedef struct VkWriteDescriptorSet { const VkBufferView* pTexelBufferView; } VkWriteDescriptorSet; -typedef struct VkCopyDescriptorSet { - VkStructureType sType; - const void* pNext; - VkDescriptorSet srcSet; - uint32_t srcBinding; - uint32_t srcArrayElement; - VkDescriptorSet dstSet; - uint32_t dstBinding; - uint32_t dstArrayElement; - uint32_t descriptorCount; -} VkCopyDescriptorSet; - -typedef struct VkFramebufferCreateInfo { - VkStructureType sType; - const void* pNext; - VkFramebufferCreateFlags flags; - VkRenderPass renderPass; - uint32_t attachmentCount; - const VkImageView* pAttachments; - uint32_t width; - uint32_t height; - uint32_t layers; -} VkFramebufferCreateInfo; - typedef struct VkAttachmentDescription { VkAttachmentDescriptionFlags flags; VkFormat format; @@ -2511,6 +3358,18 @@ typedef struct VkAttachmentReference { VkImageLayout layout; } VkAttachmentReference; +typedef struct VkFramebufferCreateInfo { + VkStructureType sType; + const void* pNext; + VkFramebufferCreateFlags flags; + VkRenderPass renderPass; + uint32_t attachmentCount; + const VkImageView* pAttachments; + uint32_t width; + uint32_t height; + uint32_t layers; +} VkFramebufferCreateInfo; + typedef struct VkSubpassDescription { VkSubpassDescriptionFlags flags; VkPipelineBindPoint pipelineBindPoint; @@ -2592,21 +3451,6 @@ typedef struct VkImageSubresourceLayers { uint32_t layerCount; } VkImageSubresourceLayers; -typedef struct VkImageCopy { - VkImageSubresourceLayers srcSubresource; - VkOffset3D srcOffset; - VkImageSubresourceLayers dstSubresource; - VkOffset3D dstOffset; - VkExtent3D extent; -} VkImageCopy; - -typedef struct VkImageBlit { - VkImageSubresourceLayers srcSubresource; - VkOffset3D srcOffsets[2]; - VkImageSubresourceLayers dstSubresource; - VkOffset3D dstOffsets[2]; -} VkImageBlit; - typedef struct VkBufferImageCopy { VkDeviceSize bufferOffset; uint32_t bufferRowLength; @@ -2644,6 +3488,21 @@ typedef struct VkClearRect { uint32_t layerCount; } VkClearRect; +typedef struct VkImageBlit { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffsets[2]; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffsets[2]; +} VkImageBlit; + +typedef struct VkImageCopy { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageCopy; + typedef struct VkImageResolve { VkImageSubresourceLayers srcSubresource; VkOffset3D srcOffset; @@ -2652,38 +3511,6 @@ typedef struct VkImageResolve { VkExtent3D extent; } VkImageResolve; -typedef struct VkMemoryBarrier { - VkStructureType sType; - const void* pNext; - VkAccessFlags srcAccessMask; - VkAccessFlags dstAccessMask; -} VkMemoryBarrier; - -typedef struct VkBufferMemoryBarrier { - VkStructureType sType; - const void* pNext; - VkAccessFlags srcAccessMask; - VkAccessFlags dstAccessMask; - uint32_t srcQueueFamilyIndex; - uint32_t dstQueueFamilyIndex; - VkBuffer buffer; - VkDeviceSize offset; - VkDeviceSize size; -} VkBufferMemoryBarrier; - -typedef struct VkImageMemoryBarrier { - VkStructureType sType; - const void* pNext; - VkAccessFlags srcAccessMask; - VkAccessFlags dstAccessMask; - VkImageLayout oldLayout; - VkImageLayout newLayout; - uint32_t srcQueueFamilyIndex; - uint32_t dstQueueFamilyIndex; - VkImage image; - VkImageSubresourceRange subresourceRange; -} VkImageMemoryBarrier; - typedef struct VkRenderPassBeginInfo { VkStructureType sType; const void* pNext; @@ -2694,28 +3521,6 @@ typedef struct VkRenderPassBeginInfo { const VkClearValue* pClearValues; } VkRenderPassBeginInfo; -typedef struct VkDispatchIndirectCommand { - uint32_t x; - uint32_t y; - uint32_t z; -} VkDispatchIndirectCommand; - -typedef struct VkDrawIndexedIndirectCommand { - uint32_t indexCount; - uint32_t instanceCount; - uint32_t firstIndex; - int32_t vertexOffset; - uint32_t firstInstance; -} VkDrawIndexedIndirectCommand; - -typedef struct VkDrawIndirectCommand { - uint32_t vertexCount; - uint32_t instanceCount; - uint32_t firstVertex; - uint32_t firstInstance; -} VkDrawIndirectCommand; - - typedef VkResult (VKAPI_PTR *PFN_vkCreateInstance)(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); typedef void (VKAPI_PTR *PFN_vkDestroyInstance)(VkInstance instance, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices); @@ -3653,27 +4458,22 @@ VKAPI_ATTR void VKAPI_CALL vkCmdExecuteCommands( const VkCommandBuffer* pCommandBuffers); #endif + #define VK_VERSION_1_1 1 // Vulkan 1.1 version number -#define VK_API_VERSION_1_1 VK_MAKE_VERSION(1, 1, 0)// Patch version should always be set to 0 - +#define VK_API_VERSION_1_1 VK_MAKE_API_VERSION(0, 1, 1, 0)// Patch version should always be set to 0 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSamplerYcbcrConversion) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorUpdateTemplate) - -#define VK_MAX_DEVICE_GROUP_SIZE 32 -#define VK_LUID_SIZE 8 -#define VK_QUEUE_FAMILY_EXTERNAL (~0U-1) - +#define VK_MAX_DEVICE_GROUP_SIZE 32U +#define VK_LUID_SIZE 8U +#define VK_QUEUE_FAMILY_EXTERNAL (~1U) typedef enum VkPointClippingBehavior { VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES = 0, VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY = 1, VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES, VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR = VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY, - VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES, - VK_POINT_CLIPPING_BEHAVIOR_END_RANGE = VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY, - VK_POINT_CLIPPING_BEHAVIOR_RANGE_SIZE = (VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY - VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES + 1), VK_POINT_CLIPPING_BEHAVIOR_MAX_ENUM = 0x7FFFFFFF } VkPointClippingBehavior; @@ -3682,9 +4482,6 @@ typedef enum VkTessellationDomainOrigin { VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT = 1, VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT, VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT, - VK_TESSELLATION_DOMAIN_ORIGIN_BEGIN_RANGE = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT, - VK_TESSELLATION_DOMAIN_ORIGIN_END_RANGE = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT, - VK_TESSELLATION_DOMAIN_ORIGIN_RANGE_SIZE = (VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT - VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT + 1), VK_TESSELLATION_DOMAIN_ORIGIN_MAX_ENUM = 0x7FFFFFFF } VkTessellationDomainOrigin; @@ -3699,9 +4496,6 @@ typedef enum VkSamplerYcbcrModelConversion { VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709, VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601, VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_BEGIN_RANGE = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_END_RANGE = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_RANGE_SIZE = (VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 - VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY + 1), VK_SAMPLER_YCBCR_MODEL_CONVERSION_MAX_ENUM = 0x7FFFFFFF } VkSamplerYcbcrModelConversion; @@ -3710,9 +4504,6 @@ typedef enum VkSamplerYcbcrRange { VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1, VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_FULL, VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW, - VK_SAMPLER_YCBCR_RANGE_BEGIN_RANGE = VK_SAMPLER_YCBCR_RANGE_ITU_FULL, - VK_SAMPLER_YCBCR_RANGE_END_RANGE = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW, - VK_SAMPLER_YCBCR_RANGE_RANGE_SIZE = (VK_SAMPLER_YCBCR_RANGE_ITU_NARROW - VK_SAMPLER_YCBCR_RANGE_ITU_FULL + 1), VK_SAMPLER_YCBCR_RANGE_MAX_ENUM = 0x7FFFFFFF } VkSamplerYcbcrRange; @@ -3721,9 +4512,6 @@ typedef enum VkChromaLocation { VK_CHROMA_LOCATION_MIDPOINT = 1, VK_CHROMA_LOCATION_COSITED_EVEN_KHR = VK_CHROMA_LOCATION_COSITED_EVEN, VK_CHROMA_LOCATION_MIDPOINT_KHR = VK_CHROMA_LOCATION_MIDPOINT, - VK_CHROMA_LOCATION_BEGIN_RANGE = VK_CHROMA_LOCATION_COSITED_EVEN, - VK_CHROMA_LOCATION_END_RANGE = VK_CHROMA_LOCATION_MIDPOINT, - VK_CHROMA_LOCATION_RANGE_SIZE = (VK_CHROMA_LOCATION_MIDPOINT - VK_CHROMA_LOCATION_COSITED_EVEN + 1), VK_CHROMA_LOCATION_MAX_ENUM = 0x7FFFFFFF } VkChromaLocation; @@ -3731,13 +4519,9 @@ typedef enum VkDescriptorUpdateTemplateType { VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET = 0, VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1, VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_BEGIN_RANGE = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_END_RANGE = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_RANGE_SIZE = (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET + 1), VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkDescriptorUpdateTemplateType; - typedef enum VkSubgroupFeatureFlagBits { VK_SUBGROUP_FEATURE_BASIC_BIT = 0x00000001, VK_SUBGROUP_FEATURE_VOTE_BIT = 0x00000002, @@ -3767,7 +4551,11 @@ typedef VkFlags VkPeerMemoryFeatureFlags; typedef enum VkMemoryAllocateFlagBits { VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT = 0x00000001, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT = 0x00000002, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT = 0x00000004, VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHR = VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_KHR = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT, VK_MEMORY_ALLOCATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkMemoryAllocateFlagBits; typedef VkFlags VkMemoryAllocateFlags; @@ -3786,6 +4574,8 @@ typedef enum VkExternalMemoryHandleTypeFlagBits { VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID = 0x00000400, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT = 0x00000080, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT = 0x00000100, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA = 0x00000800, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_RDMA_ADDRESS_BIT_NV = 0x00001000, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, @@ -3850,6 +4640,8 @@ typedef enum VkExternalSemaphoreHandleTypeFlagBits { VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 0x00000004, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT = 0x00000008, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT = 0x00000010, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA = 0x00000080, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D11_FENCE_BIT = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, @@ -3867,7 +4659,6 @@ typedef enum VkExternalSemaphoreFeatureFlagBits { VK_EXTERNAL_SEMAPHORE_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkExternalSemaphoreFeatureFlagBits; typedef VkFlags VkExternalSemaphoreFeatureFlags; - typedef struct VkPhysicalDeviceSubgroupProperties { VkStructureType sType; void* pNext; @@ -4135,12 +4926,14 @@ typedef struct VkPhysicalDeviceMultiviewProperties { uint32_t maxMultiviewInstanceIndex; } VkPhysicalDeviceMultiviewProperties; -typedef struct VkPhysicalDeviceVariablePointerFeatures { +typedef struct VkPhysicalDeviceVariablePointersFeatures { VkStructureType sType; void* pNext; VkBool32 variablePointersStorageBuffer; VkBool32 variablePointers; -} VkPhysicalDeviceVariablePointerFeatures; +} VkPhysicalDeviceVariablePointersFeatures; + +typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointerFeatures; typedef struct VkPhysicalDeviceProtectedMemoryFeatures { VkStructureType sType; @@ -4222,7 +5015,7 @@ typedef struct VkDescriptorUpdateTemplateEntry { typedef struct VkDescriptorUpdateTemplateCreateInfo { VkStructureType sType; - void* pNext; + const void* pNext; VkDescriptorUpdateTemplateCreateFlags flags; uint32_t descriptorUpdateEntryCount; const VkDescriptorUpdateTemplateEntry* pDescriptorUpdateEntries; @@ -4346,12 +5139,13 @@ typedef struct VkDescriptorSetLayoutSupport { VkBool32 supported; } VkDescriptorSetLayoutSupport; -typedef struct VkPhysicalDeviceShaderDrawParameterFeatures { +typedef struct VkPhysicalDeviceShaderDrawParametersFeatures { VkStructureType sType; void* pNext; VkBool32 shaderDrawParameters; -} VkPhysicalDeviceShaderDrawParameterFeatures; +} VkPhysicalDeviceShaderDrawParametersFeatures; +typedef VkPhysicalDeviceShaderDrawParametersFeatures VkPhysicalDeviceShaderDrawParameterFeatures; typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceVersion)(uint32_t* pApiVersion); typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos); @@ -4529,19 +5323,776 @@ VKAPI_ATTR void VKAPI_CALL vkGetDescriptorSetLayoutSupport( VkDescriptorSetLayoutSupport* pSupport); #endif + +#define VK_VERSION_1_2 1 +// Vulkan 1.2 version number +#define VK_API_VERSION_1_2 VK_MAKE_API_VERSION(0, 1, 2, 0)// Patch version should always be set to 0 + +#define VK_MAX_DRIVER_NAME_SIZE 256U +#define VK_MAX_DRIVER_INFO_SIZE 256U + +typedef enum VkDriverId { + VK_DRIVER_ID_AMD_PROPRIETARY = 1, + VK_DRIVER_ID_AMD_OPEN_SOURCE = 2, + VK_DRIVER_ID_MESA_RADV = 3, + VK_DRIVER_ID_NVIDIA_PROPRIETARY = 4, + VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS = 5, + VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA = 6, + VK_DRIVER_ID_IMAGINATION_PROPRIETARY = 7, + VK_DRIVER_ID_QUALCOMM_PROPRIETARY = 8, + VK_DRIVER_ID_ARM_PROPRIETARY = 9, + VK_DRIVER_ID_GOOGLE_SWIFTSHADER = 10, + VK_DRIVER_ID_GGP_PROPRIETARY = 11, + VK_DRIVER_ID_BROADCOM_PROPRIETARY = 12, + VK_DRIVER_ID_MESA_LLVMPIPE = 13, + VK_DRIVER_ID_MOLTENVK = 14, + VK_DRIVER_ID_COREAVI_PROPRIETARY = 15, + VK_DRIVER_ID_JUICE_PROPRIETARY = 16, + VK_DRIVER_ID_VERISILICON_PROPRIETARY = 17, + VK_DRIVER_ID_MESA_TURNIP = 18, + VK_DRIVER_ID_MESA_V3DV = 19, + VK_DRIVER_ID_MESA_PANVK = 20, + VK_DRIVER_ID_AMD_PROPRIETARY_KHR = VK_DRIVER_ID_AMD_PROPRIETARY, + VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR = VK_DRIVER_ID_AMD_OPEN_SOURCE, + VK_DRIVER_ID_MESA_RADV_KHR = VK_DRIVER_ID_MESA_RADV, + VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR = VK_DRIVER_ID_NVIDIA_PROPRIETARY, + VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR = VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, + VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR = VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA, + VK_DRIVER_ID_IMAGINATION_PROPRIETARY_KHR = VK_DRIVER_ID_IMAGINATION_PROPRIETARY, + VK_DRIVER_ID_QUALCOMM_PROPRIETARY_KHR = VK_DRIVER_ID_QUALCOMM_PROPRIETARY, + VK_DRIVER_ID_ARM_PROPRIETARY_KHR = VK_DRIVER_ID_ARM_PROPRIETARY, + VK_DRIVER_ID_GOOGLE_SWIFTSHADER_KHR = VK_DRIVER_ID_GOOGLE_SWIFTSHADER, + VK_DRIVER_ID_GGP_PROPRIETARY_KHR = VK_DRIVER_ID_GGP_PROPRIETARY, + VK_DRIVER_ID_BROADCOM_PROPRIETARY_KHR = VK_DRIVER_ID_BROADCOM_PROPRIETARY, + VK_DRIVER_ID_MAX_ENUM = 0x7FFFFFFF +} VkDriverId; + +typedef enum VkShaderFloatControlsIndependence { + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY = 0, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL = 1, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE = 2, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_MAX_ENUM = 0x7FFFFFFF +} VkShaderFloatControlsIndependence; + +typedef enum VkSamplerReductionMode { + VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE = 0, + VK_SAMPLER_REDUCTION_MODE_MIN = 1, + VK_SAMPLER_REDUCTION_MODE_MAX = 2, + VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE, + VK_SAMPLER_REDUCTION_MODE_MIN_EXT = VK_SAMPLER_REDUCTION_MODE_MIN, + VK_SAMPLER_REDUCTION_MODE_MAX_EXT = VK_SAMPLER_REDUCTION_MODE_MAX, + VK_SAMPLER_REDUCTION_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerReductionMode; + +typedef enum VkSemaphoreType { + VK_SEMAPHORE_TYPE_BINARY = 0, + VK_SEMAPHORE_TYPE_TIMELINE = 1, + VK_SEMAPHORE_TYPE_BINARY_KHR = VK_SEMAPHORE_TYPE_BINARY, + VK_SEMAPHORE_TYPE_TIMELINE_KHR = VK_SEMAPHORE_TYPE_TIMELINE, + VK_SEMAPHORE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkSemaphoreType; + +typedef enum VkResolveModeFlagBits { + VK_RESOLVE_MODE_NONE = 0, + VK_RESOLVE_MODE_SAMPLE_ZERO_BIT = 0x00000001, + VK_RESOLVE_MODE_AVERAGE_BIT = 0x00000002, + VK_RESOLVE_MODE_MIN_BIT = 0x00000004, + VK_RESOLVE_MODE_MAX_BIT = 0x00000008, + VK_RESOLVE_MODE_NONE_KHR = VK_RESOLVE_MODE_NONE, + VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, + VK_RESOLVE_MODE_AVERAGE_BIT_KHR = VK_RESOLVE_MODE_AVERAGE_BIT, + VK_RESOLVE_MODE_MIN_BIT_KHR = VK_RESOLVE_MODE_MIN_BIT, + VK_RESOLVE_MODE_MAX_BIT_KHR = VK_RESOLVE_MODE_MAX_BIT, + VK_RESOLVE_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkResolveModeFlagBits; +typedef VkFlags VkResolveModeFlags; + +typedef enum VkDescriptorBindingFlagBits { + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT = 0x00000001, + VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT = 0x00000002, + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT = 0x00000004, + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT = 0x00000008, + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, + VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT = VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT, + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT, + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT = VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT, + VK_DESCRIPTOR_BINDING_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorBindingFlagBits; +typedef VkFlags VkDescriptorBindingFlags; + +typedef enum VkSemaphoreWaitFlagBits { + VK_SEMAPHORE_WAIT_ANY_BIT = 0x00000001, + VK_SEMAPHORE_WAIT_ANY_BIT_KHR = VK_SEMAPHORE_WAIT_ANY_BIT, + VK_SEMAPHORE_WAIT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSemaphoreWaitFlagBits; +typedef VkFlags VkSemaphoreWaitFlags; +typedef struct VkPhysicalDeviceVulkan11Features { + VkStructureType sType; + void* pNext; + VkBool32 storageBuffer16BitAccess; + VkBool32 uniformAndStorageBuffer16BitAccess; + VkBool32 storagePushConstant16; + VkBool32 storageInputOutput16; + VkBool32 multiview; + VkBool32 multiviewGeometryShader; + VkBool32 multiviewTessellationShader; + VkBool32 variablePointersStorageBuffer; + VkBool32 variablePointers; + VkBool32 protectedMemory; + VkBool32 samplerYcbcrConversion; + VkBool32 shaderDrawParameters; +} VkPhysicalDeviceVulkan11Features; + +typedef struct VkPhysicalDeviceVulkan11Properties { + VkStructureType sType; + void* pNext; + uint8_t deviceUUID[VK_UUID_SIZE]; + uint8_t driverUUID[VK_UUID_SIZE]; + uint8_t deviceLUID[VK_LUID_SIZE]; + uint32_t deviceNodeMask; + VkBool32 deviceLUIDValid; + uint32_t subgroupSize; + VkShaderStageFlags subgroupSupportedStages; + VkSubgroupFeatureFlags subgroupSupportedOperations; + VkBool32 subgroupQuadOperationsInAllStages; + VkPointClippingBehavior pointClippingBehavior; + uint32_t maxMultiviewViewCount; + uint32_t maxMultiviewInstanceIndex; + VkBool32 protectedNoFault; + uint32_t maxPerSetDescriptors; + VkDeviceSize maxMemoryAllocationSize; +} VkPhysicalDeviceVulkan11Properties; + +typedef struct VkPhysicalDeviceVulkan12Features { + VkStructureType sType; + void* pNext; + VkBool32 samplerMirrorClampToEdge; + VkBool32 drawIndirectCount; + VkBool32 storageBuffer8BitAccess; + VkBool32 uniformAndStorageBuffer8BitAccess; + VkBool32 storagePushConstant8; + VkBool32 shaderBufferInt64Atomics; + VkBool32 shaderSharedInt64Atomics; + VkBool32 shaderFloat16; + VkBool32 shaderInt8; + VkBool32 descriptorIndexing; + VkBool32 shaderInputAttachmentArrayDynamicIndexing; + VkBool32 shaderUniformTexelBufferArrayDynamicIndexing; + VkBool32 shaderStorageTexelBufferArrayDynamicIndexing; + VkBool32 shaderUniformBufferArrayNonUniformIndexing; + VkBool32 shaderSampledImageArrayNonUniformIndexing; + VkBool32 shaderStorageBufferArrayNonUniformIndexing; + VkBool32 shaderStorageImageArrayNonUniformIndexing; + VkBool32 shaderInputAttachmentArrayNonUniformIndexing; + VkBool32 shaderUniformTexelBufferArrayNonUniformIndexing; + VkBool32 shaderStorageTexelBufferArrayNonUniformIndexing; + VkBool32 descriptorBindingUniformBufferUpdateAfterBind; + VkBool32 descriptorBindingSampledImageUpdateAfterBind; + VkBool32 descriptorBindingStorageImageUpdateAfterBind; + VkBool32 descriptorBindingStorageBufferUpdateAfterBind; + VkBool32 descriptorBindingUniformTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingStorageTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingUpdateUnusedWhilePending; + VkBool32 descriptorBindingPartiallyBound; + VkBool32 descriptorBindingVariableDescriptorCount; + VkBool32 runtimeDescriptorArray; + VkBool32 samplerFilterMinmax; + VkBool32 scalarBlockLayout; + VkBool32 imagelessFramebuffer; + VkBool32 uniformBufferStandardLayout; + VkBool32 shaderSubgroupExtendedTypes; + VkBool32 separateDepthStencilLayouts; + VkBool32 hostQueryReset; + VkBool32 timelineSemaphore; + VkBool32 bufferDeviceAddress; + VkBool32 bufferDeviceAddressCaptureReplay; + VkBool32 bufferDeviceAddressMultiDevice; + VkBool32 vulkanMemoryModel; + VkBool32 vulkanMemoryModelDeviceScope; + VkBool32 vulkanMemoryModelAvailabilityVisibilityChains; + VkBool32 shaderOutputViewportIndex; + VkBool32 shaderOutputLayer; + VkBool32 subgroupBroadcastDynamicId; +} VkPhysicalDeviceVulkan12Features; + +typedef struct VkConformanceVersion { + uint8_t major; + uint8_t minor; + uint8_t subminor; + uint8_t patch; +} VkConformanceVersion; + +typedef struct VkPhysicalDeviceVulkan12Properties { + VkStructureType sType; + void* pNext; + VkDriverId driverID; + char driverName[VK_MAX_DRIVER_NAME_SIZE]; + char driverInfo[VK_MAX_DRIVER_INFO_SIZE]; + VkConformanceVersion conformanceVersion; + VkShaderFloatControlsIndependence denormBehaviorIndependence; + VkShaderFloatControlsIndependence roundingModeIndependence; + VkBool32 shaderSignedZeroInfNanPreserveFloat16; + VkBool32 shaderSignedZeroInfNanPreserveFloat32; + VkBool32 shaderSignedZeroInfNanPreserveFloat64; + VkBool32 shaderDenormPreserveFloat16; + VkBool32 shaderDenormPreserveFloat32; + VkBool32 shaderDenormPreserveFloat64; + VkBool32 shaderDenormFlushToZeroFloat16; + VkBool32 shaderDenormFlushToZeroFloat32; + VkBool32 shaderDenormFlushToZeroFloat64; + VkBool32 shaderRoundingModeRTEFloat16; + VkBool32 shaderRoundingModeRTEFloat32; + VkBool32 shaderRoundingModeRTEFloat64; + VkBool32 shaderRoundingModeRTZFloat16; + VkBool32 shaderRoundingModeRTZFloat32; + VkBool32 shaderRoundingModeRTZFloat64; + uint32_t maxUpdateAfterBindDescriptorsInAllPools; + VkBool32 shaderUniformBufferArrayNonUniformIndexingNative; + VkBool32 shaderSampledImageArrayNonUniformIndexingNative; + VkBool32 shaderStorageBufferArrayNonUniformIndexingNative; + VkBool32 shaderStorageImageArrayNonUniformIndexingNative; + VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative; + VkBool32 robustBufferAccessUpdateAfterBind; + VkBool32 quadDivergentImplicitLod; + uint32_t maxPerStageDescriptorUpdateAfterBindSamplers; + uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages; + uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments; + uint32_t maxPerStageUpdateAfterBindResources; + uint32_t maxDescriptorSetUpdateAfterBindSamplers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindSampledImages; + uint32_t maxDescriptorSetUpdateAfterBindStorageImages; + uint32_t maxDescriptorSetUpdateAfterBindInputAttachments; + VkResolveModeFlags supportedDepthResolveModes; + VkResolveModeFlags supportedStencilResolveModes; + VkBool32 independentResolveNone; + VkBool32 independentResolve; + VkBool32 filterMinmaxSingleComponentFormats; + VkBool32 filterMinmaxImageComponentMapping; + uint64_t maxTimelineSemaphoreValueDifference; + VkSampleCountFlags framebufferIntegerColorSampleCounts; +} VkPhysicalDeviceVulkan12Properties; + +typedef struct VkImageFormatListCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t viewFormatCount; + const VkFormat* pViewFormats; +} VkImageFormatListCreateInfo; + +typedef struct VkAttachmentDescription2 { + VkStructureType sType; + const void* pNext; + VkAttachmentDescriptionFlags flags; + VkFormat format; + VkSampleCountFlagBits samples; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkAttachmentLoadOp stencilLoadOp; + VkAttachmentStoreOp stencilStoreOp; + VkImageLayout initialLayout; + VkImageLayout finalLayout; +} VkAttachmentDescription2; + +typedef struct VkAttachmentReference2 { + VkStructureType sType; + const void* pNext; + uint32_t attachment; + VkImageLayout layout; + VkImageAspectFlags aspectMask; +} VkAttachmentReference2; + +typedef struct VkSubpassDescription2 { + VkStructureType sType; + const void* pNext; + VkSubpassDescriptionFlags flags; + VkPipelineBindPoint pipelineBindPoint; + uint32_t viewMask; + uint32_t inputAttachmentCount; + const VkAttachmentReference2* pInputAttachments; + uint32_t colorAttachmentCount; + const VkAttachmentReference2* pColorAttachments; + const VkAttachmentReference2* pResolveAttachments; + const VkAttachmentReference2* pDepthStencilAttachment; + uint32_t preserveAttachmentCount; + const uint32_t* pPreserveAttachments; +} VkSubpassDescription2; + +typedef struct VkSubpassDependency2 { + VkStructureType sType; + const void* pNext; + uint32_t srcSubpass; + uint32_t dstSubpass; + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkDependencyFlags dependencyFlags; + int32_t viewOffset; +} VkSubpassDependency2; + +typedef struct VkRenderPassCreateInfo2 { + VkStructureType sType; + const void* pNext; + VkRenderPassCreateFlags flags; + uint32_t attachmentCount; + const VkAttachmentDescription2* pAttachments; + uint32_t subpassCount; + const VkSubpassDescription2* pSubpasses; + uint32_t dependencyCount; + const VkSubpassDependency2* pDependencies; + uint32_t correlatedViewMaskCount; + const uint32_t* pCorrelatedViewMasks; +} VkRenderPassCreateInfo2; + +typedef struct VkSubpassBeginInfo { + VkStructureType sType; + const void* pNext; + VkSubpassContents contents; +} VkSubpassBeginInfo; + +typedef struct VkSubpassEndInfo { + VkStructureType sType; + const void* pNext; +} VkSubpassEndInfo; + +typedef struct VkPhysicalDevice8BitStorageFeatures { + VkStructureType sType; + void* pNext; + VkBool32 storageBuffer8BitAccess; + VkBool32 uniformAndStorageBuffer8BitAccess; + VkBool32 storagePushConstant8; +} VkPhysicalDevice8BitStorageFeatures; + +typedef struct VkPhysicalDeviceDriverProperties { + VkStructureType sType; + void* pNext; + VkDriverId driverID; + char driverName[VK_MAX_DRIVER_NAME_SIZE]; + char driverInfo[VK_MAX_DRIVER_INFO_SIZE]; + VkConformanceVersion conformanceVersion; +} VkPhysicalDeviceDriverProperties; + +typedef struct VkPhysicalDeviceShaderAtomicInt64Features { + VkStructureType sType; + void* pNext; + VkBool32 shaderBufferInt64Atomics; + VkBool32 shaderSharedInt64Atomics; +} VkPhysicalDeviceShaderAtomicInt64Features; + +typedef struct VkPhysicalDeviceShaderFloat16Int8Features { + VkStructureType sType; + void* pNext; + VkBool32 shaderFloat16; + VkBool32 shaderInt8; +} VkPhysicalDeviceShaderFloat16Int8Features; + +typedef struct VkPhysicalDeviceFloatControlsProperties { + VkStructureType sType; + void* pNext; + VkShaderFloatControlsIndependence denormBehaviorIndependence; + VkShaderFloatControlsIndependence roundingModeIndependence; + VkBool32 shaderSignedZeroInfNanPreserveFloat16; + VkBool32 shaderSignedZeroInfNanPreserveFloat32; + VkBool32 shaderSignedZeroInfNanPreserveFloat64; + VkBool32 shaderDenormPreserveFloat16; + VkBool32 shaderDenormPreserveFloat32; + VkBool32 shaderDenormPreserveFloat64; + VkBool32 shaderDenormFlushToZeroFloat16; + VkBool32 shaderDenormFlushToZeroFloat32; + VkBool32 shaderDenormFlushToZeroFloat64; + VkBool32 shaderRoundingModeRTEFloat16; + VkBool32 shaderRoundingModeRTEFloat32; + VkBool32 shaderRoundingModeRTEFloat64; + VkBool32 shaderRoundingModeRTZFloat16; + VkBool32 shaderRoundingModeRTZFloat32; + VkBool32 shaderRoundingModeRTZFloat64; +} VkPhysicalDeviceFloatControlsProperties; + +typedef struct VkDescriptorSetLayoutBindingFlagsCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t bindingCount; + const VkDescriptorBindingFlags* pBindingFlags; +} VkDescriptorSetLayoutBindingFlagsCreateInfo; + +typedef struct VkPhysicalDeviceDescriptorIndexingFeatures { + VkStructureType sType; + void* pNext; + VkBool32 shaderInputAttachmentArrayDynamicIndexing; + VkBool32 shaderUniformTexelBufferArrayDynamicIndexing; + VkBool32 shaderStorageTexelBufferArrayDynamicIndexing; + VkBool32 shaderUniformBufferArrayNonUniformIndexing; + VkBool32 shaderSampledImageArrayNonUniformIndexing; + VkBool32 shaderStorageBufferArrayNonUniformIndexing; + VkBool32 shaderStorageImageArrayNonUniformIndexing; + VkBool32 shaderInputAttachmentArrayNonUniformIndexing; + VkBool32 shaderUniformTexelBufferArrayNonUniformIndexing; + VkBool32 shaderStorageTexelBufferArrayNonUniformIndexing; + VkBool32 descriptorBindingUniformBufferUpdateAfterBind; + VkBool32 descriptorBindingSampledImageUpdateAfterBind; + VkBool32 descriptorBindingStorageImageUpdateAfterBind; + VkBool32 descriptorBindingStorageBufferUpdateAfterBind; + VkBool32 descriptorBindingUniformTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingStorageTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingUpdateUnusedWhilePending; + VkBool32 descriptorBindingPartiallyBound; + VkBool32 descriptorBindingVariableDescriptorCount; + VkBool32 runtimeDescriptorArray; +} VkPhysicalDeviceDescriptorIndexingFeatures; + +typedef struct VkPhysicalDeviceDescriptorIndexingProperties { + VkStructureType sType; + void* pNext; + uint32_t maxUpdateAfterBindDescriptorsInAllPools; + VkBool32 shaderUniformBufferArrayNonUniformIndexingNative; + VkBool32 shaderSampledImageArrayNonUniformIndexingNative; + VkBool32 shaderStorageBufferArrayNonUniformIndexingNative; + VkBool32 shaderStorageImageArrayNonUniformIndexingNative; + VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative; + VkBool32 robustBufferAccessUpdateAfterBind; + VkBool32 quadDivergentImplicitLod; + uint32_t maxPerStageDescriptorUpdateAfterBindSamplers; + uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages; + uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments; + uint32_t maxPerStageUpdateAfterBindResources; + uint32_t maxDescriptorSetUpdateAfterBindSamplers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindSampledImages; + uint32_t maxDescriptorSetUpdateAfterBindStorageImages; + uint32_t maxDescriptorSetUpdateAfterBindInputAttachments; +} VkPhysicalDeviceDescriptorIndexingProperties; + +typedef struct VkDescriptorSetVariableDescriptorCountAllocateInfo { + VkStructureType sType; + const void* pNext; + uint32_t descriptorSetCount; + const uint32_t* pDescriptorCounts; +} VkDescriptorSetVariableDescriptorCountAllocateInfo; + +typedef struct VkDescriptorSetVariableDescriptorCountLayoutSupport { + VkStructureType sType; + void* pNext; + uint32_t maxVariableDescriptorCount; +} VkDescriptorSetVariableDescriptorCountLayoutSupport; + +typedef struct VkSubpassDescriptionDepthStencilResolve { + VkStructureType sType; + const void* pNext; + VkResolveModeFlagBits depthResolveMode; + VkResolveModeFlagBits stencilResolveMode; + const VkAttachmentReference2* pDepthStencilResolveAttachment; +} VkSubpassDescriptionDepthStencilResolve; + +typedef struct VkPhysicalDeviceDepthStencilResolveProperties { + VkStructureType sType; + void* pNext; + VkResolveModeFlags supportedDepthResolveModes; + VkResolveModeFlags supportedStencilResolveModes; + VkBool32 independentResolveNone; + VkBool32 independentResolve; +} VkPhysicalDeviceDepthStencilResolveProperties; + +typedef struct VkPhysicalDeviceScalarBlockLayoutFeatures { + VkStructureType sType; + void* pNext; + VkBool32 scalarBlockLayout; +} VkPhysicalDeviceScalarBlockLayoutFeatures; + +typedef struct VkImageStencilUsageCreateInfo { + VkStructureType sType; + const void* pNext; + VkImageUsageFlags stencilUsage; +} VkImageStencilUsageCreateInfo; + +typedef struct VkSamplerReductionModeCreateInfo { + VkStructureType sType; + const void* pNext; + VkSamplerReductionMode reductionMode; +} VkSamplerReductionModeCreateInfo; + +typedef struct VkPhysicalDeviceSamplerFilterMinmaxProperties { + VkStructureType sType; + void* pNext; + VkBool32 filterMinmaxSingleComponentFormats; + VkBool32 filterMinmaxImageComponentMapping; +} VkPhysicalDeviceSamplerFilterMinmaxProperties; + +typedef struct VkPhysicalDeviceVulkanMemoryModelFeatures { + VkStructureType sType; + void* pNext; + VkBool32 vulkanMemoryModel; + VkBool32 vulkanMemoryModelDeviceScope; + VkBool32 vulkanMemoryModelAvailabilityVisibilityChains; +} VkPhysicalDeviceVulkanMemoryModelFeatures; + +typedef struct VkPhysicalDeviceImagelessFramebufferFeatures { + VkStructureType sType; + void* pNext; + VkBool32 imagelessFramebuffer; +} VkPhysicalDeviceImagelessFramebufferFeatures; + +typedef struct VkFramebufferAttachmentImageInfo { + VkStructureType sType; + const void* pNext; + VkImageCreateFlags flags; + VkImageUsageFlags usage; + uint32_t width; + uint32_t height; + uint32_t layerCount; + uint32_t viewFormatCount; + const VkFormat* pViewFormats; +} VkFramebufferAttachmentImageInfo; + +typedef struct VkFramebufferAttachmentsCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t attachmentImageInfoCount; + const VkFramebufferAttachmentImageInfo* pAttachmentImageInfos; +} VkFramebufferAttachmentsCreateInfo; + +typedef struct VkRenderPassAttachmentBeginInfo { + VkStructureType sType; + const void* pNext; + uint32_t attachmentCount; + const VkImageView* pAttachments; +} VkRenderPassAttachmentBeginInfo; + +typedef struct VkPhysicalDeviceUniformBufferStandardLayoutFeatures { + VkStructureType sType; + void* pNext; + VkBool32 uniformBufferStandardLayout; +} VkPhysicalDeviceUniformBufferStandardLayoutFeatures; + +typedef struct VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures { + VkStructureType sType; + void* pNext; + VkBool32 shaderSubgroupExtendedTypes; +} VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures; + +typedef struct VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures { + VkStructureType sType; + void* pNext; + VkBool32 separateDepthStencilLayouts; +} VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures; + +typedef struct VkAttachmentReferenceStencilLayout { + VkStructureType sType; + void* pNext; + VkImageLayout stencilLayout; +} VkAttachmentReferenceStencilLayout; + +typedef struct VkAttachmentDescriptionStencilLayout { + VkStructureType sType; + void* pNext; + VkImageLayout stencilInitialLayout; + VkImageLayout stencilFinalLayout; +} VkAttachmentDescriptionStencilLayout; + +typedef struct VkPhysicalDeviceHostQueryResetFeatures { + VkStructureType sType; + void* pNext; + VkBool32 hostQueryReset; +} VkPhysicalDeviceHostQueryResetFeatures; + +typedef struct VkPhysicalDeviceTimelineSemaphoreFeatures { + VkStructureType sType; + void* pNext; + VkBool32 timelineSemaphore; +} VkPhysicalDeviceTimelineSemaphoreFeatures; + +typedef struct VkPhysicalDeviceTimelineSemaphoreProperties { + VkStructureType sType; + void* pNext; + uint64_t maxTimelineSemaphoreValueDifference; +} VkPhysicalDeviceTimelineSemaphoreProperties; + +typedef struct VkSemaphoreTypeCreateInfo { + VkStructureType sType; + const void* pNext; + VkSemaphoreType semaphoreType; + uint64_t initialValue; +} VkSemaphoreTypeCreateInfo; + +typedef struct VkTimelineSemaphoreSubmitInfo { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreValueCount; + const uint64_t* pWaitSemaphoreValues; + uint32_t signalSemaphoreValueCount; + const uint64_t* pSignalSemaphoreValues; +} VkTimelineSemaphoreSubmitInfo; + +typedef struct VkSemaphoreWaitInfo { + VkStructureType sType; + const void* pNext; + VkSemaphoreWaitFlags flags; + uint32_t semaphoreCount; + const VkSemaphore* pSemaphores; + const uint64_t* pValues; +} VkSemaphoreWaitInfo; + +typedef struct VkSemaphoreSignalInfo { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + uint64_t value; +} VkSemaphoreSignalInfo; + +typedef struct VkPhysicalDeviceBufferDeviceAddressFeatures { + VkStructureType sType; + void* pNext; + VkBool32 bufferDeviceAddress; + VkBool32 bufferDeviceAddressCaptureReplay; + VkBool32 bufferDeviceAddressMultiDevice; +} VkPhysicalDeviceBufferDeviceAddressFeatures; + +typedef struct VkBufferDeviceAddressInfo { + VkStructureType sType; + const void* pNext; + VkBuffer buffer; +} VkBufferDeviceAddressInfo; + +typedef struct VkBufferOpaqueCaptureAddressCreateInfo { + VkStructureType sType; + const void* pNext; + uint64_t opaqueCaptureAddress; +} VkBufferOpaqueCaptureAddressCreateInfo; + +typedef struct VkMemoryOpaqueCaptureAddressAllocateInfo { + VkStructureType sType; + const void* pNext; + uint64_t opaqueCaptureAddress; +} VkMemoryOpaqueCaptureAddressAllocateInfo; + +typedef struct VkDeviceMemoryOpaqueCaptureAddressInfo { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; +} VkDeviceMemoryOpaqueCaptureAddressInfo; + +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCount)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCount)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef VkResult (VKAPI_PTR *PFN_vkCreateRenderPass2)(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); +typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderPass2)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo); +typedef void (VKAPI_PTR *PFN_vkCmdNextSubpass2)(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo); +typedef void (VKAPI_PTR *PFN_vkCmdEndRenderPass2)(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo); +typedef void (VKAPI_PTR *PFN_vkResetQueryPool)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); +typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreCounterValue)(VkDevice device, VkSemaphore semaphore, uint64_t* pValue); +typedef VkResult (VKAPI_PTR *PFN_vkWaitSemaphores)(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout); +typedef VkResult (VKAPI_PTR *PFN_vkSignalSemaphore)(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo); +typedef VkDeviceAddress (VKAPI_PTR *PFN_vkGetBufferDeviceAddress)(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); +typedef uint64_t (VKAPI_PTR *PFN_vkGetBufferOpaqueCaptureAddress)(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); +typedef uint64_t (VKAPI_PTR *PFN_vkGetDeviceMemoryOpaqueCaptureAddress)(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCount( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCount( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass2( + VkDevice device, + const VkRenderPassCreateInfo2* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkRenderPass* pRenderPass); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass2( + VkCommandBuffer commandBuffer, + const VkRenderPassBeginInfo* pRenderPassBegin, + const VkSubpassBeginInfo* pSubpassBeginInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass2( + VkCommandBuffer commandBuffer, + const VkSubpassBeginInfo* pSubpassBeginInfo, + const VkSubpassEndInfo* pSubpassEndInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderPass2( + VkCommandBuffer commandBuffer, + const VkSubpassEndInfo* pSubpassEndInfo); + +VKAPI_ATTR void VKAPI_CALL vkResetQueryPool( + VkDevice device, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreCounterValue( + VkDevice device, + VkSemaphore semaphore, + uint64_t* pValue); + +VKAPI_ATTR VkResult VKAPI_CALL vkWaitSemaphores( + VkDevice device, + const VkSemaphoreWaitInfo* pWaitInfo, + uint64_t timeout); + +VKAPI_ATTR VkResult VKAPI_CALL vkSignalSemaphore( + VkDevice device, + const VkSemaphoreSignalInfo* pSignalInfo); + +VKAPI_ATTR VkDeviceAddress VKAPI_CALL vkGetBufferDeviceAddress( + VkDevice device, + const VkBufferDeviceAddressInfo* pInfo); + +VKAPI_ATTR uint64_t VKAPI_CALL vkGetBufferOpaqueCaptureAddress( + VkDevice device, + const VkBufferDeviceAddressInfo* pInfo); + +VKAPI_ATTR uint64_t VKAPI_CALL vkGetDeviceMemoryOpaqueCaptureAddress( + VkDevice device, + const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo); +#endif + + #define VK_KHR_surface 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) - #define VK_KHR_SURFACE_SPEC_VERSION 25 #define VK_KHR_SURFACE_EXTENSION_NAME "VK_KHR_surface" -#define VK_COLORSPACE_SRGB_NONLINEAR_KHR VK_COLOR_SPACE_SRGB_NONLINEAR_KHR +typedef enum VkPresentModeKHR { + VK_PRESENT_MODE_IMMEDIATE_KHR = 0, + VK_PRESENT_MODE_MAILBOX_KHR = 1, + VK_PRESENT_MODE_FIFO_KHR = 2, + VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3, + VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR = 1000111000, + VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR = 1000111001, + VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPresentModeKHR; typedef enum VkColorSpaceKHR { VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104001, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT = 1000104002, - VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = 1000104003, + VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT = 1000104003, VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104004, VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104005, VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104006, @@ -4553,26 +6104,12 @@ typedef enum VkColorSpaceKHR { VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012, VK_COLOR_SPACE_PASS_THROUGH_EXT = 1000104013, VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT = 1000104014, - VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, - VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, - VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1), + VK_COLOR_SPACE_DISPLAY_NATIVE_AMD = 1000213000, + VK_COLORSPACE_SRGB_NONLINEAR_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT, VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF } VkColorSpaceKHR; -typedef enum VkPresentModeKHR { - VK_PRESENT_MODE_IMMEDIATE_KHR = 0, - VK_PRESENT_MODE_MAILBOX_KHR = 1, - VK_PRESENT_MODE_FIFO_KHR = 2, - VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3, - VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR = 1000111000, - VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR = 1000111001, - VK_PRESENT_MODE_BEGIN_RANGE_KHR = VK_PRESENT_MODE_IMMEDIATE_KHR, - VK_PRESENT_MODE_END_RANGE_KHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR, - VK_PRESENT_MODE_RANGE_SIZE_KHR = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1), - VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF -} VkPresentModeKHR; - - typedef enum VkSurfaceTransformFlagBitsKHR { VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 0x00000001, VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR = 0x00000002, @@ -4585,7 +6122,6 @@ typedef enum VkSurfaceTransformFlagBitsKHR { VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 0x00000100, VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkSurfaceTransformFlagBitsKHR; -typedef VkFlags VkSurfaceTransformFlagsKHR; typedef enum VkCompositeAlphaFlagBitsKHR { VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, @@ -4595,7 +6131,7 @@ typedef enum VkCompositeAlphaFlagBitsKHR { VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkCompositeAlphaFlagBitsKHR; typedef VkFlags VkCompositeAlphaFlagsKHR; - +typedef VkFlags VkSurfaceTransformFlagsKHR; typedef struct VkSurfaceCapabilitiesKHR { uint32_t minImageCount; uint32_t maxImageCount; @@ -4614,7 +6150,6 @@ typedef struct VkSurfaceFormatKHR { VkColorSpaceKHR colorSpace; } VkSurfaceFormatKHR; - typedef void (VKAPI_PTR *PFN_vkDestroySurfaceKHR)(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); @@ -4651,16 +6186,16 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModesKHR( VkPresentModeKHR* pPresentModes); #endif + #define VK_KHR_swapchain 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) - #define VK_KHR_SWAPCHAIN_SPEC_VERSION 70 #define VK_KHR_SWAPCHAIN_EXTENSION_NAME "VK_KHR_swapchain" - typedef enum VkSwapchainCreateFlagBitsKHR { VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = 0x00000001, VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR = 0x00000002, + VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR = 0x00000004, VK_SWAPCHAIN_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkSwapchainCreateFlagBitsKHR; typedef VkFlags VkSwapchainCreateFlagsKHR; @@ -4673,7 +6208,6 @@ typedef enum VkDeviceGroupPresentModeFlagBitsKHR { VK_DEVICE_GROUP_PRESENT_MODE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkDeviceGroupPresentModeFlagBitsKHR; typedef VkFlags VkDeviceGroupPresentModeFlagsKHR; - typedef struct VkSwapchainCreateInfoKHR { VkStructureType sType; const void* pNext; @@ -4731,7 +6265,7 @@ typedef struct VkAcquireNextImageInfoKHR { typedef struct VkDeviceGroupPresentCapabilitiesKHR { VkStructureType sType; - const void* pNext; + void* pNext; uint32_t presentMask[VK_MAX_DEVICE_GROUP_SIZE]; VkDeviceGroupPresentModeFlagsKHR modes; } VkDeviceGroupPresentCapabilitiesKHR; @@ -4750,7 +6284,6 @@ typedef struct VkDeviceGroupSwapchainCreateInfoKHR { VkDeviceGroupPresentModeFlagsKHR modes; } VkDeviceGroupSwapchainCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateSwapchainKHR)(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain); typedef void (VKAPI_PTR *PFN_vkDestroySwapchainKHR)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainImagesKHR)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages); @@ -4812,13 +6345,13 @@ VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHR( uint32_t* pImageIndex); #endif + #define VK_KHR_display 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayModeKHR) - -#define VK_KHR_DISPLAY_SPEC_VERSION 21 +#define VK_KHR_DISPLAY_SPEC_VERSION 23 #define VK_KHR_DISPLAY_EXTENSION_NAME "VK_KHR_display" - +typedef VkFlags VkDisplayModeCreateFlagsKHR; typedef enum VkDisplayPlaneAlphaFlagBitsKHR { VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, @@ -4828,29 +6361,12 @@ typedef enum VkDisplayPlaneAlphaFlagBitsKHR { VK_DISPLAY_PLANE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF } VkDisplayPlaneAlphaFlagBitsKHR; typedef VkFlags VkDisplayPlaneAlphaFlagsKHR; -typedef VkFlags VkDisplayModeCreateFlagsKHR; typedef VkFlags VkDisplaySurfaceCreateFlagsKHR; - -typedef struct VkDisplayPropertiesKHR { - VkDisplayKHR display; - const char* displayName; - VkExtent2D physicalDimensions; - VkExtent2D physicalResolution; - VkSurfaceTransformFlagsKHR supportedTransforms; - VkBool32 planeReorderPossible; - VkBool32 persistentContent; -} VkDisplayPropertiesKHR; - typedef struct VkDisplayModeParametersKHR { VkExtent2D visibleRegion; uint32_t refreshRate; } VkDisplayModeParametersKHR; -typedef struct VkDisplayModePropertiesKHR { - VkDisplayModeKHR displayMode; - VkDisplayModeParametersKHR parameters; -} VkDisplayModePropertiesKHR; - typedef struct VkDisplayModeCreateInfoKHR { VkStructureType sType; const void* pNext; @@ -4858,6 +6374,11 @@ typedef struct VkDisplayModeCreateInfoKHR { VkDisplayModeParametersKHR parameters; } VkDisplayModeCreateInfoKHR; +typedef struct VkDisplayModePropertiesKHR { + VkDisplayModeKHR displayMode; + VkDisplayModeParametersKHR parameters; +} VkDisplayModePropertiesKHR; + typedef struct VkDisplayPlaneCapabilitiesKHR { VkDisplayPlaneAlphaFlagsKHR supportedAlpha; VkOffset2D minSrcPosition; @@ -4875,6 +6396,16 @@ typedef struct VkDisplayPlanePropertiesKHR { uint32_t currentStackIndex; } VkDisplayPlanePropertiesKHR; +typedef struct VkDisplayPropertiesKHR { + VkDisplayKHR display; + const char* displayName; + VkExtent2D physicalDimensions; + VkExtent2D physicalResolution; + VkSurfaceTransformFlagsKHR supportedTransforms; + VkBool32 planeReorderPossible; + VkBool32 persistentContent; +} VkDisplayPropertiesKHR; + typedef struct VkDisplaySurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; @@ -4888,7 +6419,6 @@ typedef struct VkDisplaySurfaceCreateInfoKHR { VkExtent2D imageExtent; } VkDisplaySurfaceCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPropertiesKHR* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneSupportedDisplaysKHR)(VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t* pDisplayCount, VkDisplayKHR* pDisplays); @@ -4940,10 +6470,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayPlaneSurfaceKHR( VkSurfaceKHR* pSurface); #endif -#define VK_KHR_display_swapchain 1 -#define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION 9 -#define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME "VK_KHR_display_swapchain" +#define VK_KHR_display_swapchain 1 +#define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION 10 +#define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME "VK_KHR_display_swapchain" typedef struct VkDisplayPresentInfoKHR { VkStructureType sType; const void* pNext; @@ -4952,7 +6482,6 @@ typedef struct VkDisplayPresentInfoKHR { VkBool32 persistent; } VkDisplayPresentInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateSharedSwapchainsKHR)(VkDevice device, uint32_t swapchainCount, const VkSwapchainCreateInfoKHR* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchains); #ifndef VK_NO_PROTOTYPES @@ -4964,15 +6493,125 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateSharedSwapchainsKHR( VkSwapchainKHR* pSwapchains); #endif + #define VK_KHR_sampler_mirror_clamp_to_edge 1 -#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 1 +#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 3 #define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge" +#define VK_KHR_dynamic_rendering 1 +#define VK_KHR_DYNAMIC_RENDERING_SPEC_VERSION 1 +#define VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME "VK_KHR_dynamic_rendering" + +typedef enum VkRenderingFlagBitsKHR { + VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR = 0x00000001, + VK_RENDERING_SUSPENDING_BIT_KHR = 0x00000002, + VK_RENDERING_RESUMING_BIT_KHR = 0x00000004, + VK_RENDERING_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkRenderingFlagBitsKHR; +typedef VkFlags VkRenderingFlagsKHR; +typedef struct VkRenderingAttachmentInfoKHR { + VkStructureType sType; + const void* pNext; + VkImageView imageView; + VkImageLayout imageLayout; + VkResolveModeFlagBits resolveMode; + VkImageView resolveImageView; + VkImageLayout resolveImageLayout; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkClearValue clearValue; +} VkRenderingAttachmentInfoKHR; + +typedef struct VkRenderingInfoKHR { + VkStructureType sType; + const void* pNext; + VkRenderingFlagsKHR flags; + VkRect2D renderArea; + uint32_t layerCount; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkRenderingAttachmentInfoKHR* pColorAttachments; + const VkRenderingAttachmentInfoKHR* pDepthAttachment; + const VkRenderingAttachmentInfoKHR* pStencilAttachment; +} VkRenderingInfoKHR; + +typedef struct VkPipelineRenderingCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkFormat* pColorAttachmentFormats; + VkFormat depthAttachmentFormat; + VkFormat stencilAttachmentFormat; +} VkPipelineRenderingCreateInfoKHR; + +typedef struct VkPhysicalDeviceDynamicRenderingFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 dynamicRendering; +} VkPhysicalDeviceDynamicRenderingFeaturesKHR; + +typedef struct VkCommandBufferInheritanceRenderingInfoKHR { + VkStructureType sType; + const void* pNext; + VkRenderingFlagsKHR flags; + uint32_t viewMask; + uint32_t colorAttachmentCount; + const VkFormat* pColorAttachmentFormats; + VkFormat depthAttachmentFormat; + VkFormat stencilAttachmentFormat; + VkSampleCountFlagBits rasterizationSamples; +} VkCommandBufferInheritanceRenderingInfoKHR; + +typedef struct VkRenderingFragmentShadingRateAttachmentInfoKHR { + VkStructureType sType; + const void* pNext; + VkImageView imageView; + VkImageLayout imageLayout; + VkExtent2D shadingRateAttachmentTexelSize; +} VkRenderingFragmentShadingRateAttachmentInfoKHR; + +typedef struct VkRenderingFragmentDensityMapAttachmentInfoEXT { + VkStructureType sType; + const void* pNext; + VkImageView imageView; + VkImageLayout imageLayout; +} VkRenderingFragmentDensityMapAttachmentInfoEXT; + +typedef struct VkAttachmentSampleCountInfoAMD { + VkStructureType sType; + const void* pNext; + uint32_t colorAttachmentCount; + const VkSampleCountFlagBits* pColorAttachmentSamples; + VkSampleCountFlagBits depthStencilAttachmentSamples; +} VkAttachmentSampleCountInfoAMD; + +typedef VkAttachmentSampleCountInfoAMD VkAttachmentSampleCountInfoNV; + +typedef struct VkMultiviewPerViewAttributesInfoNVX { + VkStructureType sType; + const void* pNext; + VkBool32 perViewAttributes; + VkBool32 perViewAttributesPositionXOnly; +} VkMultiviewPerViewAttributesInfoNVX; + +typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderingKHR)(VkCommandBuffer commandBuffer, const VkRenderingInfoKHR* pRenderingInfo); +typedef void (VKAPI_PTR *PFN_vkCmdEndRenderingKHR)(VkCommandBuffer commandBuffer); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderingKHR( + VkCommandBuffer commandBuffer, + const VkRenderingInfoKHR* pRenderingInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderingKHR( + VkCommandBuffer commandBuffer); +#endif + + #define VK_KHR_multiview 1 #define VK_KHR_MULTIVIEW_SPEC_VERSION 1 #define VK_KHR_MULTIVIEW_EXTENSION_NAME "VK_KHR_multiview" - typedef VkRenderPassMultiviewCreateInfo VkRenderPassMultiviewCreateInfoKHR; typedef VkPhysicalDeviceMultiviewFeatures VkPhysicalDeviceMultiviewFeaturesKHR; @@ -4982,9 +6621,8 @@ typedef VkPhysicalDeviceMultiviewProperties VkPhysicalDeviceMultiviewPropertiesK #define VK_KHR_get_physical_device_properties2 1 -#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1 +#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 2 #define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2" - typedef VkPhysicalDeviceFeatures2 VkPhysicalDeviceFeatures2KHR; typedef VkPhysicalDeviceProperties2 VkPhysicalDeviceProperties2KHR; @@ -5003,7 +6641,6 @@ typedef VkSparseImageFormatProperties2 VkSparseImageFormatProperties2KHR; typedef VkPhysicalDeviceSparseImageFormatInfo2 VkPhysicalDeviceSparseImageFormatInfo2KHR; - typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties); @@ -5047,10 +6684,10 @@ VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR( VkSparseImageFormatProperties2* pProperties); #endif -#define VK_KHR_device_group 1 -#define VK_KHR_DEVICE_GROUP_SPEC_VERSION 3 -#define VK_KHR_DEVICE_GROUP_EXTENSION_NAME "VK_KHR_device_group" +#define VK_KHR_device_group 1 +#define VK_KHR_DEVICE_GROUP_SPEC_VERSION 4 +#define VK_KHR_DEVICE_GROUP_EXTENSION_NAME "VK_KHR_device_group" typedef VkPeerMemoryFeatureFlags VkPeerMemoryFeatureFlagsKHR; typedef VkPeerMemoryFeatureFlagBits VkPeerMemoryFeatureFlagBitsKHR; @@ -5059,7 +6696,6 @@ typedef VkMemoryAllocateFlags VkMemoryAllocateFlagsKHR; typedef VkMemoryAllocateFlagBits VkMemoryAllocateFlagBitsKHR; - typedef VkMemoryAllocateFlagsInfo VkMemoryAllocateFlagsInfoKHR; typedef VkDeviceGroupRenderPassBeginInfo VkDeviceGroupRenderPassBeginInfoKHR; @@ -5074,7 +6710,6 @@ typedef VkBindBufferMemoryDeviceGroupInfo VkBindBufferMemoryDeviceGroupInfoKHR; typedef VkBindImageMemoryDeviceGroupInfo VkBindImageMemoryDeviceGroupInfoKHR; - typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHR)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHR)(VkCommandBuffer commandBuffer, uint32_t deviceMask); typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHR)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); @@ -5101,18 +6736,19 @@ VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHR( uint32_t groupCountZ); #endif + #define VK_KHR_shader_draw_parameters 1 #define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1 #define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters" #define VK_KHR_maintenance1 1 -#define VK_KHR_MAINTENANCE1_SPEC_VERSION 2 -#define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1" - +#define VK_KHR_MAINTENANCE_1_SPEC_VERSION 2 +#define VK_KHR_MAINTENANCE_1_EXTENSION_NAME "VK_KHR_maintenance1" +#define VK_KHR_MAINTENANCE1_SPEC_VERSION VK_KHR_MAINTENANCE_1_SPEC_VERSION +#define VK_KHR_MAINTENANCE1_EXTENSION_NAME VK_KHR_MAINTENANCE_1_EXTENSION_NAME typedef VkCommandPoolTrimFlags VkCommandPoolTrimFlagsKHR; - typedef void (VKAPI_PTR *PFN_vkTrimCommandPoolKHR)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); #ifndef VK_NO_PROTOTYPES @@ -5122,16 +6758,15 @@ VKAPI_ATTR void VKAPI_CALL vkTrimCommandPoolKHR( VkCommandPoolTrimFlags flags); #endif + #define VK_KHR_device_group_creation 1 #define VK_KHR_DEVICE_GROUP_CREATION_SPEC_VERSION 1 #define VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHR_device_group_creation" #define VK_MAX_DEVICE_GROUP_SIZE_KHR VK_MAX_DEVICE_GROUP_SIZE - typedef VkPhysicalDeviceGroupProperties VkPhysicalDeviceGroupPropertiesKHR; typedef VkDeviceGroupDeviceCreateInfo VkDeviceGroupDeviceCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDeviceGroupsKHR)(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); #ifndef VK_NO_PROTOTYPES @@ -5141,11 +6776,11 @@ VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroupsKHR( VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); #endif + #define VK_KHR_external_memory_capabilities 1 #define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_memory_capabilities" #define VK_LUID_SIZE_KHR VK_LUID_SIZE - typedef VkExternalMemoryHandleTypeFlags VkExternalMemoryHandleTypeFlagsKHR; typedef VkExternalMemoryHandleTypeFlagBits VkExternalMemoryHandleTypeFlagBitsKHR; @@ -5154,7 +6789,6 @@ typedef VkExternalMemoryFeatureFlags VkExternalMemoryFeatureFlagsKHR; typedef VkExternalMemoryFeatureFlagBits VkExternalMemoryFeatureFlagBitsKHR; - typedef VkExternalMemoryProperties VkExternalMemoryPropertiesKHR; typedef VkPhysicalDeviceExternalImageFormatInfo VkPhysicalDeviceExternalImageFormatInfoKHR; @@ -5167,7 +6801,6 @@ typedef VkExternalBufferProperties VkExternalBufferPropertiesKHR; typedef VkPhysicalDeviceIDProperties VkPhysicalDeviceIDPropertiesKHR; - typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties); #ifndef VK_NO_PROTOTYPES @@ -5177,11 +6810,11 @@ VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalBufferPropertiesKHR( VkExternalBufferProperties* pExternalBufferProperties); #endif + #define VK_KHR_external_memory 1 #define VK_KHR_EXTERNAL_MEMORY_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHR_external_memory" #define VK_QUEUE_FAMILY_EXTERNAL_KHR VK_QUEUE_FAMILY_EXTERNAL - typedef VkExternalMemoryImageCreateInfo VkExternalMemoryImageCreateInfoKHR; typedef VkExternalMemoryBufferCreateInfo VkExternalMemoryBufferCreateInfoKHR; @@ -5193,7 +6826,6 @@ typedef VkExportMemoryAllocateInfo VkExportMemoryAllocateInfoKHR; #define VK_KHR_external_memory_fd 1 #define VK_KHR_EXTERNAL_MEMORY_FD_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHR_external_memory_fd" - typedef struct VkImportMemoryFdInfoKHR { VkStructureType sType; const void* pNext; @@ -5214,7 +6846,6 @@ typedef struct VkMemoryGetFdInfoKHR { VkExternalMemoryHandleTypeFlagBits handleType; } VkMemoryGetFdInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdKHR)(VkDevice device, const VkMemoryGetFdInfoKHR* pGetFdInfo, int* pFd); typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdPropertiesKHR)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, int fd, VkMemoryFdPropertiesKHR* pMemoryFdProperties); @@ -5231,10 +6862,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdPropertiesKHR( VkMemoryFdPropertiesKHR* pMemoryFdProperties); #endif + #define VK_KHR_external_semaphore_capabilities 1 #define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_semaphore_capabilities" - typedef VkExternalSemaphoreHandleTypeFlags VkExternalSemaphoreHandleTypeFlagsKHR; typedef VkExternalSemaphoreHandleTypeFlagBits VkExternalSemaphoreHandleTypeFlagBitsKHR; @@ -5243,12 +6874,10 @@ typedef VkExternalSemaphoreFeatureFlags VkExternalSemaphoreFeatureFlagsKHR; typedef VkExternalSemaphoreFeatureFlagBits VkExternalSemaphoreFeatureFlagBitsKHR; - typedef VkPhysicalDeviceExternalSemaphoreInfo VkPhysicalDeviceExternalSemaphoreInfoKHR; typedef VkExternalSemaphoreProperties VkExternalSemaphorePropertiesKHR; - typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties); #ifndef VK_NO_PROTOTYPES @@ -5258,15 +6887,14 @@ VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalSemaphorePropertiesKHR( VkExternalSemaphoreProperties* pExternalSemaphoreProperties); #endif + #define VK_KHR_external_semaphore 1 #define VK_KHR_EXTERNAL_SEMAPHORE_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHR_external_semaphore" - typedef VkSemaphoreImportFlags VkSemaphoreImportFlagsKHR; typedef VkSemaphoreImportFlagBits VkSemaphoreImportFlagBitsKHR; - typedef VkExportSemaphoreCreateInfo VkExportSemaphoreCreateInfoKHR; @@ -5274,7 +6902,6 @@ typedef VkExportSemaphoreCreateInfo VkExportSemaphoreCreateInfoKHR; #define VK_KHR_external_semaphore_fd 1 #define VK_KHR_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHR_external_semaphore_fd" - typedef struct VkImportSemaphoreFdInfoKHR { VkStructureType sType; const void* pNext; @@ -5291,7 +6918,6 @@ typedef struct VkSemaphoreGetFdInfoKHR { VkExternalSemaphoreHandleTypeFlagBits handleType; } VkSemaphoreGetFdInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreFdKHR)(VkDevice device, const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo); typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreFdKHR)(VkDevice device, const VkSemaphoreGetFdInfoKHR* pGetFdInfo, int* pFd); @@ -5306,17 +6932,16 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreFdKHR( int* pFd); #endif + #define VK_KHR_push_descriptor 1 #define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 2 #define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor" - typedef struct VkPhysicalDevicePushDescriptorPropertiesKHR { VkStructureType sType; void* pNext; uint32_t maxPushDescriptors; } VkPhysicalDevicePushDescriptorPropertiesKHR; - typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetKHR)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites); typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetWithTemplateKHR)(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData); @@ -5337,18 +6962,26 @@ VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetWithTemplateKHR( const void* pData); #endif + +#define VK_KHR_shader_float16_int8 1 +#define VK_KHR_SHADER_FLOAT16_INT8_SPEC_VERSION 1 +#define VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME "VK_KHR_shader_float16_int8" +typedef VkPhysicalDeviceShaderFloat16Int8Features VkPhysicalDeviceShaderFloat16Int8FeaturesKHR; + +typedef VkPhysicalDeviceShaderFloat16Int8Features VkPhysicalDeviceFloat16Int8FeaturesKHR; + + + #define VK_KHR_16bit_storage 1 #define VK_KHR_16BIT_STORAGE_SPEC_VERSION 1 #define VK_KHR_16BIT_STORAGE_EXTENSION_NAME "VK_KHR_16bit_storage" - typedef VkPhysicalDevice16BitStorageFeatures VkPhysicalDevice16BitStorageFeaturesKHR; #define VK_KHR_incremental_present 1 -#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1 +#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 2 #define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present" - typedef struct VkRectLayerKHR { VkOffset2D offset; VkExtent2D extent; @@ -5372,21 +7005,16 @@ typedef struct VkPresentRegionsKHR { #define VK_KHR_descriptor_update_template 1 typedef VkDescriptorUpdateTemplate VkDescriptorUpdateTemplateKHR; - #define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1 #define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template" - typedef VkDescriptorUpdateTemplateType VkDescriptorUpdateTemplateTypeKHR; - typedef VkDescriptorUpdateTemplateCreateFlags VkDescriptorUpdateTemplateCreateFlagsKHR; - typedef VkDescriptorUpdateTemplateEntry VkDescriptorUpdateTemplateEntryKHR; typedef VkDescriptorUpdateTemplateCreateInfo VkDescriptorUpdateTemplateCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorUpdateTemplateKHR)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate); typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorUpdateTemplateKHR)(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator); typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSetWithTemplateKHR)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData); @@ -5410,17 +7038,74 @@ VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSetWithTemplateKHR( const void* pData); #endif + +#define VK_KHR_imageless_framebuffer 1 +#define VK_KHR_IMAGELESS_FRAMEBUFFER_SPEC_VERSION 1 +#define VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME "VK_KHR_imageless_framebuffer" +typedef VkPhysicalDeviceImagelessFramebufferFeatures VkPhysicalDeviceImagelessFramebufferFeaturesKHR; + +typedef VkFramebufferAttachmentsCreateInfo VkFramebufferAttachmentsCreateInfoKHR; + +typedef VkFramebufferAttachmentImageInfo VkFramebufferAttachmentImageInfoKHR; + +typedef VkRenderPassAttachmentBeginInfo VkRenderPassAttachmentBeginInfoKHR; + + + +#define VK_KHR_create_renderpass2 1 +#define VK_KHR_CREATE_RENDERPASS_2_SPEC_VERSION 1 +#define VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME "VK_KHR_create_renderpass2" +typedef VkRenderPassCreateInfo2 VkRenderPassCreateInfo2KHR; + +typedef VkAttachmentDescription2 VkAttachmentDescription2KHR; + +typedef VkAttachmentReference2 VkAttachmentReference2KHR; + +typedef VkSubpassDescription2 VkSubpassDescription2KHR; + +typedef VkSubpassDependency2 VkSubpassDependency2KHR; + +typedef VkSubpassBeginInfo VkSubpassBeginInfoKHR; + +typedef VkSubpassEndInfo VkSubpassEndInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateRenderPass2KHR)(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); +typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderPass2KHR)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo); +typedef void (VKAPI_PTR *PFN_vkCmdNextSubpass2KHR)(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo); +typedef void (VKAPI_PTR *PFN_vkCmdEndRenderPass2KHR)(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass2KHR( + VkDevice device, + const VkRenderPassCreateInfo2* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkRenderPass* pRenderPass); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass2KHR( + VkCommandBuffer commandBuffer, + const VkRenderPassBeginInfo* pRenderPassBegin, + const VkSubpassBeginInfo* pSubpassBeginInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass2KHR( + VkCommandBuffer commandBuffer, + const VkSubpassBeginInfo* pSubpassBeginInfo, + const VkSubpassEndInfo* pSubpassEndInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderPass2KHR( + VkCommandBuffer commandBuffer, + const VkSubpassEndInfo* pSubpassEndInfo); +#endif + + #define VK_KHR_shared_presentable_image 1 #define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1 #define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image" - typedef struct VkSharedPresentSurfaceCapabilitiesKHR { VkStructureType sType; void* pNext; VkImageUsageFlags sharedPresentSupportedUsageFlags; } VkSharedPresentSurfaceCapabilitiesKHR; - typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainStatusKHR)(VkDevice device, VkSwapchainKHR swapchain); #ifndef VK_NO_PROTOTYPES @@ -5429,10 +7114,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainStatusKHR( VkSwapchainKHR swapchain); #endif + #define VK_KHR_external_fence_capabilities 1 #define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_fence_capabilities" - typedef VkExternalFenceHandleTypeFlags VkExternalFenceHandleTypeFlagsKHR; typedef VkExternalFenceHandleTypeFlagBits VkExternalFenceHandleTypeFlagBitsKHR; @@ -5441,12 +7126,10 @@ typedef VkExternalFenceFeatureFlags VkExternalFenceFeatureFlagsKHR; typedef VkExternalFenceFeatureFlagBits VkExternalFenceFeatureFlagBitsKHR; - typedef VkPhysicalDeviceExternalFenceInfo VkPhysicalDeviceExternalFenceInfoKHR; typedef VkExternalFenceProperties VkExternalFencePropertiesKHR; - typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties); #ifndef VK_NO_PROTOTYPES @@ -5456,15 +7139,14 @@ VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalFencePropertiesKHR( VkExternalFenceProperties* pExternalFenceProperties); #endif + #define VK_KHR_external_fence 1 #define VK_KHR_EXTERNAL_FENCE_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME "VK_KHR_external_fence" - typedef VkFenceImportFlags VkFenceImportFlagsKHR; typedef VkFenceImportFlagBits VkFenceImportFlagBitsKHR; - typedef VkExportFenceCreateInfo VkExportFenceCreateInfoKHR; @@ -5472,7 +7154,6 @@ typedef VkExportFenceCreateInfo VkExportFenceCreateInfoKHR; #define VK_KHR_external_fence_fd 1 #define VK_KHR_EXTERNAL_FENCE_FD_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME "VK_KHR_external_fence_fd" - typedef struct VkImportFenceFdInfoKHR { VkStructureType sType; const void* pNext; @@ -5489,7 +7170,6 @@ typedef struct VkFenceGetFdInfoKHR { VkExternalFenceHandleTypeFlagBits handleType; } VkFenceGetFdInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkImportFenceFdKHR)(VkDevice device, const VkImportFenceFdInfoKHR* pImportFenceFdInfo); typedef VkResult (VKAPI_PTR *PFN_vkGetFenceFdKHR)(VkDevice device, const VkFenceGetFdInfoKHR* pGetFdInfo, int* pFd); @@ -5504,15 +7184,156 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceFdKHR( int* pFd); #endif -#define VK_KHR_maintenance2 1 -#define VK_KHR_MAINTENANCE2_SPEC_VERSION 1 -#define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2" +#define VK_KHR_performance_query 1 +#define VK_KHR_PERFORMANCE_QUERY_SPEC_VERSION 1 +#define VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME "VK_KHR_performance_query" + +typedef enum VkPerformanceCounterUnitKHR { + VK_PERFORMANCE_COUNTER_UNIT_GENERIC_KHR = 0, + VK_PERFORMANCE_COUNTER_UNIT_PERCENTAGE_KHR = 1, + VK_PERFORMANCE_COUNTER_UNIT_NANOSECONDS_KHR = 2, + VK_PERFORMANCE_COUNTER_UNIT_BYTES_KHR = 3, + VK_PERFORMANCE_COUNTER_UNIT_BYTES_PER_SECOND_KHR = 4, + VK_PERFORMANCE_COUNTER_UNIT_KELVIN_KHR = 5, + VK_PERFORMANCE_COUNTER_UNIT_WATTS_KHR = 6, + VK_PERFORMANCE_COUNTER_UNIT_VOLTS_KHR = 7, + VK_PERFORMANCE_COUNTER_UNIT_AMPS_KHR = 8, + VK_PERFORMANCE_COUNTER_UNIT_HERTZ_KHR = 9, + VK_PERFORMANCE_COUNTER_UNIT_CYCLES_KHR = 10, + VK_PERFORMANCE_COUNTER_UNIT_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPerformanceCounterUnitKHR; + +typedef enum VkPerformanceCounterScopeKHR { + VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_BUFFER_KHR = 0, + VK_PERFORMANCE_COUNTER_SCOPE_RENDER_PASS_KHR = 1, + VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_KHR = 2, + VK_QUERY_SCOPE_COMMAND_BUFFER_KHR = VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_BUFFER_KHR, + VK_QUERY_SCOPE_RENDER_PASS_KHR = VK_PERFORMANCE_COUNTER_SCOPE_RENDER_PASS_KHR, + VK_QUERY_SCOPE_COMMAND_KHR = VK_PERFORMANCE_COUNTER_SCOPE_COMMAND_KHR, + VK_PERFORMANCE_COUNTER_SCOPE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPerformanceCounterScopeKHR; + +typedef enum VkPerformanceCounterStorageKHR { + VK_PERFORMANCE_COUNTER_STORAGE_INT32_KHR = 0, + VK_PERFORMANCE_COUNTER_STORAGE_INT64_KHR = 1, + VK_PERFORMANCE_COUNTER_STORAGE_UINT32_KHR = 2, + VK_PERFORMANCE_COUNTER_STORAGE_UINT64_KHR = 3, + VK_PERFORMANCE_COUNTER_STORAGE_FLOAT32_KHR = 4, + VK_PERFORMANCE_COUNTER_STORAGE_FLOAT64_KHR = 5, + VK_PERFORMANCE_COUNTER_STORAGE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPerformanceCounterStorageKHR; + +typedef enum VkPerformanceCounterDescriptionFlagBitsKHR { + VK_PERFORMANCE_COUNTER_DESCRIPTION_PERFORMANCE_IMPACTING_BIT_KHR = 0x00000001, + VK_PERFORMANCE_COUNTER_DESCRIPTION_CONCURRENTLY_IMPACTED_BIT_KHR = 0x00000002, + VK_PERFORMANCE_COUNTER_DESCRIPTION_PERFORMANCE_IMPACTING_KHR = VK_PERFORMANCE_COUNTER_DESCRIPTION_PERFORMANCE_IMPACTING_BIT_KHR, + VK_PERFORMANCE_COUNTER_DESCRIPTION_CONCURRENTLY_IMPACTED_KHR = VK_PERFORMANCE_COUNTER_DESCRIPTION_CONCURRENTLY_IMPACTED_BIT_KHR, + VK_PERFORMANCE_COUNTER_DESCRIPTION_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPerformanceCounterDescriptionFlagBitsKHR; +typedef VkFlags VkPerformanceCounterDescriptionFlagsKHR; + +typedef enum VkAcquireProfilingLockFlagBitsKHR { + VK_ACQUIRE_PROFILING_LOCK_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkAcquireProfilingLockFlagBitsKHR; +typedef VkFlags VkAcquireProfilingLockFlagsKHR; +typedef struct VkPhysicalDevicePerformanceQueryFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 performanceCounterQueryPools; + VkBool32 performanceCounterMultipleQueryPools; +} VkPhysicalDevicePerformanceQueryFeaturesKHR; + +typedef struct VkPhysicalDevicePerformanceQueryPropertiesKHR { + VkStructureType sType; + void* pNext; + VkBool32 allowCommandBufferQueryCopies; +} VkPhysicalDevicePerformanceQueryPropertiesKHR; + +typedef struct VkPerformanceCounterKHR { + VkStructureType sType; + void* pNext; + VkPerformanceCounterUnitKHR unit; + VkPerformanceCounterScopeKHR scope; + VkPerformanceCounterStorageKHR storage; + uint8_t uuid[VK_UUID_SIZE]; +} VkPerformanceCounterKHR; + +typedef struct VkPerformanceCounterDescriptionKHR { + VkStructureType sType; + void* pNext; + VkPerformanceCounterDescriptionFlagsKHR flags; + char name[VK_MAX_DESCRIPTION_SIZE]; + char category[VK_MAX_DESCRIPTION_SIZE]; + char description[VK_MAX_DESCRIPTION_SIZE]; +} VkPerformanceCounterDescriptionKHR; + +typedef struct VkQueryPoolPerformanceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t queueFamilyIndex; + uint32_t counterIndexCount; + const uint32_t* pCounterIndices; +} VkQueryPoolPerformanceCreateInfoKHR; + +typedef union VkPerformanceCounterResultKHR { + int32_t int32; + int64_t int64; + uint32_t uint32; + uint64_t uint64; + float float32; + double float64; +} VkPerformanceCounterResultKHR; + +typedef struct VkAcquireProfilingLockInfoKHR { + VkStructureType sType; + const void* pNext; + VkAcquireProfilingLockFlagsKHR flags; + uint64_t timeout; +} VkAcquireProfilingLockInfoKHR; + +typedef struct VkPerformanceQuerySubmitInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t counterPassIndex; +} VkPerformanceQuerySubmitInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, uint32_t* pCounterCount, VkPerformanceCounterKHR* pCounters, VkPerformanceCounterDescriptionKHR* pCounterDescriptions); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR)(VkPhysicalDevice physicalDevice, const VkQueryPoolPerformanceCreateInfoKHR* pPerformanceQueryCreateInfo, uint32_t* pNumPasses); +typedef VkResult (VKAPI_PTR *PFN_vkAcquireProfilingLockKHR)(VkDevice device, const VkAcquireProfilingLockInfoKHR* pInfo); +typedef void (VKAPI_PTR *PFN_vkReleaseProfilingLockKHR)(VkDevice device); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + uint32_t* pCounterCount, + VkPerformanceCounterKHR* pCounters, + VkPerformanceCounterDescriptionKHR* pCounterDescriptions); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR( + VkPhysicalDevice physicalDevice, + const VkQueryPoolPerformanceCreateInfoKHR* pPerformanceQueryCreateInfo, + uint32_t* pNumPasses); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireProfilingLockKHR( + VkDevice device, + const VkAcquireProfilingLockInfoKHR* pInfo); + +VKAPI_ATTR void VKAPI_CALL vkReleaseProfilingLockKHR( + VkDevice device); +#endif + + +#define VK_KHR_maintenance2 1 +#define VK_KHR_MAINTENANCE_2_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE_2_EXTENSION_NAME "VK_KHR_maintenance2" +#define VK_KHR_MAINTENANCE2_SPEC_VERSION VK_KHR_MAINTENANCE_2_SPEC_VERSION +#define VK_KHR_MAINTENANCE2_EXTENSION_NAME VK_KHR_MAINTENANCE_2_EXTENSION_NAME typedef VkPointClippingBehavior VkPointClippingBehaviorKHR; typedef VkTessellationDomainOrigin VkTessellationDomainOriginKHR; - typedef VkPhysicalDevicePointClippingProperties VkPhysicalDevicePointClippingPropertiesKHR; typedef VkRenderPassInputAttachmentAspectCreateInfo VkRenderPassInputAttachmentAspectCreateInfoKHR; @@ -5528,7 +7349,6 @@ typedef VkPipelineTessellationDomainOriginStateCreateInfo VkPipelineTessellation #define VK_KHR_get_surface_capabilities2 1 #define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1 #define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2" - typedef struct VkPhysicalDeviceSurfaceInfo2KHR { VkStructureType sType; const void* pNext; @@ -5547,7 +7367,6 @@ typedef struct VkSurfaceFormat2KHR { VkSurfaceFormatKHR surfaceFormat; } VkSurfaceFormat2KHR; - typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceFormats2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats); @@ -5564,18 +7383,82 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormats2KHR( VkSurfaceFormat2KHR* pSurfaceFormats); #endif + #define VK_KHR_variable_pointers 1 #define VK_KHR_VARIABLE_POINTERS_SPEC_VERSION 1 #define VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME "VK_KHR_variable_pointers" +typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointerFeaturesKHR; -typedef VkPhysicalDeviceVariablePointerFeatures VkPhysicalDeviceVariablePointerFeaturesKHR; +typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointersFeaturesKHR; +#define VK_KHR_get_display_properties2 1 +#define VK_KHR_GET_DISPLAY_PROPERTIES_2_SPEC_VERSION 1 +#define VK_KHR_GET_DISPLAY_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_display_properties2" +typedef struct VkDisplayProperties2KHR { + VkStructureType sType; + void* pNext; + VkDisplayPropertiesKHR displayProperties; +} VkDisplayProperties2KHR; + +typedef struct VkDisplayPlaneProperties2KHR { + VkStructureType sType; + void* pNext; + VkDisplayPlanePropertiesKHR displayPlaneProperties; +} VkDisplayPlaneProperties2KHR; + +typedef struct VkDisplayModeProperties2KHR { + VkStructureType sType; + void* pNext; + VkDisplayModePropertiesKHR displayModeProperties; +} VkDisplayModeProperties2KHR; + +typedef struct VkDisplayPlaneInfo2KHR { + VkStructureType sType; + const void* pNext; + VkDisplayModeKHR mode; + uint32_t planeIndex; +} VkDisplayPlaneInfo2KHR; + +typedef struct VkDisplayPlaneCapabilities2KHR { + VkStructureType sType; + void* pNext; + VkDisplayPlaneCapabilitiesKHR capabilities; +} VkDisplayPlaneCapabilities2KHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayProperties2KHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlaneProperties2KHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayModeProperties2KHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t* pPropertyCount, VkDisplayModeProperties2KHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneCapabilities2KHR)(VkPhysicalDevice physicalDevice, const VkDisplayPlaneInfo2KHR* pDisplayPlaneInfo, VkDisplayPlaneCapabilities2KHR* pCapabilities); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayProperties2KHR( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkDisplayProperties2KHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPlaneProperties2KHR( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkDisplayPlaneProperties2KHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayModeProperties2KHR( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display, + uint32_t* pPropertyCount, + VkDisplayModeProperties2KHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneCapabilities2KHR( + VkPhysicalDevice physicalDevice, + const VkDisplayPlaneInfo2KHR* pDisplayPlaneInfo, + VkDisplayPlaneCapabilities2KHR* pCapabilities); +#endif + + #define VK_KHR_dedicated_allocation 1 #define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3 #define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation" - typedef VkMemoryDedicatedRequirements VkMemoryDedicatedRequirementsKHR; typedef VkMemoryDedicatedAllocateInfo VkMemoryDedicatedAllocateInfoKHR; @@ -5595,7 +7478,6 @@ typedef VkMemoryDedicatedAllocateInfo VkMemoryDedicatedAllocateInfoKHR; #define VK_KHR_get_memory_requirements2 1 #define VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION 1 #define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME "VK_KHR_get_memory_requirements2" - typedef VkBufferMemoryRequirementsInfo2 VkBufferMemoryRequirementsInfo2KHR; typedef VkImageMemoryRequirementsInfo2 VkImageMemoryRequirementsInfo2KHR; @@ -5606,7 +7488,6 @@ typedef VkMemoryRequirements2 VkMemoryRequirements2KHR; typedef VkSparseImageMemoryRequirements2 VkSparseImageMemoryRequirements2KHR; - typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements2KHR)(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); typedef void (VKAPI_PTR *PFN_vkGetBufferMemoryRequirements2KHR)(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); typedef void (VKAPI_PTR *PFN_vkGetImageSparseMemoryRequirements2KHR)(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); @@ -5629,33 +7510,25 @@ VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements2KHR( VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); #endif + #define VK_KHR_image_format_list 1 #define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1 #define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list" - -typedef struct VkImageFormatListCreateInfoKHR { - VkStructureType sType; - const void* pNext; - uint32_t viewFormatCount; - const VkFormat* pViewFormats; -} VkImageFormatListCreateInfoKHR; +typedef VkImageFormatListCreateInfo VkImageFormatListCreateInfoKHR; #define VK_KHR_sampler_ycbcr_conversion 1 typedef VkSamplerYcbcrConversion VkSamplerYcbcrConversionKHR; - -#define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1 +#define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 14 #define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion" - typedef VkSamplerYcbcrModelConversion VkSamplerYcbcrModelConversionKHR; typedef VkSamplerYcbcrRange VkSamplerYcbcrRangeKHR; typedef VkChromaLocation VkChromaLocationKHR; - typedef VkSamplerYcbcrConversionCreateInfo VkSamplerYcbcrConversionCreateInfoKHR; typedef VkSamplerYcbcrConversionInfo VkSamplerYcbcrConversionInfoKHR; @@ -5668,7 +7541,6 @@ typedef VkPhysicalDeviceSamplerYcbcrConversionFeatures VkPhysicalDeviceSamplerYc typedef VkSamplerYcbcrConversionImageFormatProperties VkSamplerYcbcrConversionImageFormatPropertiesKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateSamplerYcbcrConversionKHR)(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); typedef void (VKAPI_PTR *PFN_vkDestroySamplerYcbcrConversionKHR)(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); @@ -5685,15 +7557,14 @@ VKAPI_ATTR void VKAPI_CALL vkDestroySamplerYcbcrConversionKHR( const VkAllocationCallbacks* pAllocator); #endif + #define VK_KHR_bind_memory2 1 #define VK_KHR_BIND_MEMORY_2_SPEC_VERSION 1 #define VK_KHR_BIND_MEMORY_2_EXTENSION_NAME "VK_KHR_bind_memory2" - typedef VkBindBufferMemoryInfo VkBindBufferMemoryInfoKHR; typedef VkBindImageMemoryInfo VkBindImageMemoryInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos); typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); @@ -5709,15 +7580,16 @@ VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHR( const VkBindImageMemoryInfo* pBindInfos); #endif -#define VK_KHR_maintenance3 1 -#define VK_KHR_MAINTENANCE3_SPEC_VERSION 1 -#define VK_KHR_MAINTENANCE3_EXTENSION_NAME "VK_KHR_maintenance3" +#define VK_KHR_maintenance3 1 +#define VK_KHR_MAINTENANCE_3_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE_3_EXTENSION_NAME "VK_KHR_maintenance3" +#define VK_KHR_MAINTENANCE3_SPEC_VERSION VK_KHR_MAINTENANCE_3_SPEC_VERSION +#define VK_KHR_MAINTENANCE3_EXTENSION_NAME VK_KHR_MAINTENANCE_3_EXTENSION_NAME typedef VkPhysicalDeviceMaintenance3Properties VkPhysicalDeviceMaintenance3PropertiesKHR; typedef VkDescriptorSetLayoutSupport VkDescriptorSetLayoutSupportKHR; - typedef void (VKAPI_PTR *PFN_vkGetDescriptorSetLayoutSupportKHR)(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport); #ifndef VK_NO_PROTOTYPES @@ -5727,14 +7599,1104 @@ VKAPI_ATTR void VKAPI_CALL vkGetDescriptorSetLayoutSupportKHR( VkDescriptorSetLayoutSupport* pSupport); #endif + +#define VK_KHR_draw_indirect_count 1 +#define VK_KHR_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 +#define VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_KHR_draw_indirect_count" +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountKHR)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountKHR)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountKHR( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountKHR( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); +#endif + + +#define VK_KHR_shader_subgroup_extended_types 1 +#define VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_SPEC_VERSION 1 +#define VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME "VK_KHR_shader_subgroup_extended_types" +typedef VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR; + + + +#define VK_KHR_8bit_storage 1 +#define VK_KHR_8BIT_STORAGE_SPEC_VERSION 1 +#define VK_KHR_8BIT_STORAGE_EXTENSION_NAME "VK_KHR_8bit_storage" +typedef VkPhysicalDevice8BitStorageFeatures VkPhysicalDevice8BitStorageFeaturesKHR; + + + +#define VK_KHR_shader_atomic_int64 1 +#define VK_KHR_SHADER_ATOMIC_INT64_SPEC_VERSION 1 +#define VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME "VK_KHR_shader_atomic_int64" +typedef VkPhysicalDeviceShaderAtomicInt64Features VkPhysicalDeviceShaderAtomicInt64FeaturesKHR; + + + +#define VK_KHR_shader_clock 1 +#define VK_KHR_SHADER_CLOCK_SPEC_VERSION 1 +#define VK_KHR_SHADER_CLOCK_EXTENSION_NAME "VK_KHR_shader_clock" +typedef struct VkPhysicalDeviceShaderClockFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderSubgroupClock; + VkBool32 shaderDeviceClock; +} VkPhysicalDeviceShaderClockFeaturesKHR; + + + +#define VK_KHR_driver_properties 1 +#define VK_KHR_DRIVER_PROPERTIES_SPEC_VERSION 1 +#define VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME "VK_KHR_driver_properties" +#define VK_MAX_DRIVER_NAME_SIZE_KHR VK_MAX_DRIVER_NAME_SIZE +#define VK_MAX_DRIVER_INFO_SIZE_KHR VK_MAX_DRIVER_INFO_SIZE +typedef VkDriverId VkDriverIdKHR; + +typedef VkConformanceVersion VkConformanceVersionKHR; + +typedef VkPhysicalDeviceDriverProperties VkPhysicalDeviceDriverPropertiesKHR; + + + +#define VK_KHR_shader_float_controls 1 +#define VK_KHR_SHADER_FLOAT_CONTROLS_SPEC_VERSION 4 +#define VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME "VK_KHR_shader_float_controls" +typedef VkShaderFloatControlsIndependence VkShaderFloatControlsIndependenceKHR; + +typedef VkPhysicalDeviceFloatControlsProperties VkPhysicalDeviceFloatControlsPropertiesKHR; + + + +#define VK_KHR_depth_stencil_resolve 1 +#define VK_KHR_DEPTH_STENCIL_RESOLVE_SPEC_VERSION 1 +#define VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME "VK_KHR_depth_stencil_resolve" +typedef VkResolveModeFlagBits VkResolveModeFlagBitsKHR; + +typedef VkResolveModeFlags VkResolveModeFlagsKHR; + +typedef VkSubpassDescriptionDepthStencilResolve VkSubpassDescriptionDepthStencilResolveKHR; + +typedef VkPhysicalDeviceDepthStencilResolveProperties VkPhysicalDeviceDepthStencilResolvePropertiesKHR; + + + +#define VK_KHR_swapchain_mutable_format 1 +#define VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION 1 +#define VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME "VK_KHR_swapchain_mutable_format" + + +#define VK_KHR_timeline_semaphore 1 +#define VK_KHR_TIMELINE_SEMAPHORE_SPEC_VERSION 2 +#define VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME "VK_KHR_timeline_semaphore" +typedef VkSemaphoreType VkSemaphoreTypeKHR; + +typedef VkSemaphoreWaitFlagBits VkSemaphoreWaitFlagBitsKHR; + +typedef VkSemaphoreWaitFlags VkSemaphoreWaitFlagsKHR; + +typedef VkPhysicalDeviceTimelineSemaphoreFeatures VkPhysicalDeviceTimelineSemaphoreFeaturesKHR; + +typedef VkPhysicalDeviceTimelineSemaphoreProperties VkPhysicalDeviceTimelineSemaphorePropertiesKHR; + +typedef VkSemaphoreTypeCreateInfo VkSemaphoreTypeCreateInfoKHR; + +typedef VkTimelineSemaphoreSubmitInfo VkTimelineSemaphoreSubmitInfoKHR; + +typedef VkSemaphoreWaitInfo VkSemaphoreWaitInfoKHR; + +typedef VkSemaphoreSignalInfo VkSemaphoreSignalInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreCounterValueKHR)(VkDevice device, VkSemaphore semaphore, uint64_t* pValue); +typedef VkResult (VKAPI_PTR *PFN_vkWaitSemaphoresKHR)(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout); +typedef VkResult (VKAPI_PTR *PFN_vkSignalSemaphoreKHR)(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreCounterValueKHR( + VkDevice device, + VkSemaphore semaphore, + uint64_t* pValue); + +VKAPI_ATTR VkResult VKAPI_CALL vkWaitSemaphoresKHR( + VkDevice device, + const VkSemaphoreWaitInfo* pWaitInfo, + uint64_t timeout); + +VKAPI_ATTR VkResult VKAPI_CALL vkSignalSemaphoreKHR( + VkDevice device, + const VkSemaphoreSignalInfo* pSignalInfo); +#endif + + +#define VK_KHR_vulkan_memory_model 1 +#define VK_KHR_VULKAN_MEMORY_MODEL_SPEC_VERSION 3 +#define VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME "VK_KHR_vulkan_memory_model" +typedef VkPhysicalDeviceVulkanMemoryModelFeatures VkPhysicalDeviceVulkanMemoryModelFeaturesKHR; + + + +#define VK_KHR_shader_terminate_invocation 1 +#define VK_KHR_SHADER_TERMINATE_INVOCATION_SPEC_VERSION 1 +#define VK_KHR_SHADER_TERMINATE_INVOCATION_EXTENSION_NAME "VK_KHR_shader_terminate_invocation" +typedef struct VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderTerminateInvocation; +} VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR; + + + +#define VK_KHR_fragment_shading_rate 1 +#define VK_KHR_FRAGMENT_SHADING_RATE_SPEC_VERSION 2 +#define VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME "VK_KHR_fragment_shading_rate" + +typedef enum VkFragmentShadingRateCombinerOpKHR { + VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR = 0, + VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR = 1, + VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_KHR = 2, + VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_KHR = 3, + VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR = 4, + VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_ENUM_KHR = 0x7FFFFFFF +} VkFragmentShadingRateCombinerOpKHR; +typedef struct VkFragmentShadingRateAttachmentInfoKHR { + VkStructureType sType; + const void* pNext; + const VkAttachmentReference2* pFragmentShadingRateAttachment; + VkExtent2D shadingRateAttachmentTexelSize; +} VkFragmentShadingRateAttachmentInfoKHR; + +typedef struct VkPipelineFragmentShadingRateStateCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkExtent2D fragmentSize; + VkFragmentShadingRateCombinerOpKHR combinerOps[2]; +} VkPipelineFragmentShadingRateStateCreateInfoKHR; + +typedef struct VkPhysicalDeviceFragmentShadingRateFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 pipelineFragmentShadingRate; + VkBool32 primitiveFragmentShadingRate; + VkBool32 attachmentFragmentShadingRate; +} VkPhysicalDeviceFragmentShadingRateFeaturesKHR; + +typedef struct VkPhysicalDeviceFragmentShadingRatePropertiesKHR { + VkStructureType sType; + void* pNext; + VkExtent2D minFragmentShadingRateAttachmentTexelSize; + VkExtent2D maxFragmentShadingRateAttachmentTexelSize; + uint32_t maxFragmentShadingRateAttachmentTexelSizeAspectRatio; + VkBool32 primitiveFragmentShadingRateWithMultipleViewports; + VkBool32 layeredShadingRateAttachments; + VkBool32 fragmentShadingRateNonTrivialCombinerOps; + VkExtent2D maxFragmentSize; + uint32_t maxFragmentSizeAspectRatio; + uint32_t maxFragmentShadingRateCoverageSamples; + VkSampleCountFlagBits maxFragmentShadingRateRasterizationSamples; + VkBool32 fragmentShadingRateWithShaderDepthStencilWrites; + VkBool32 fragmentShadingRateWithSampleMask; + VkBool32 fragmentShadingRateWithShaderSampleMask; + VkBool32 fragmentShadingRateWithConservativeRasterization; + VkBool32 fragmentShadingRateWithFragmentShaderInterlock; + VkBool32 fragmentShadingRateWithCustomSampleLocations; + VkBool32 fragmentShadingRateStrictMultiplyCombiner; +} VkPhysicalDeviceFragmentShadingRatePropertiesKHR; + +typedef struct VkPhysicalDeviceFragmentShadingRateKHR { + VkStructureType sType; + void* pNext; + VkSampleCountFlags sampleCounts; + VkExtent2D fragmentSize; +} VkPhysicalDeviceFragmentShadingRateKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pFragmentShadingRateCount, VkPhysicalDeviceFragmentShadingRateKHR* pFragmentShadingRates); +typedef void (VKAPI_PTR *PFN_vkCmdSetFragmentShadingRateKHR)(VkCommandBuffer commandBuffer, const VkExtent2D* pFragmentSize, const VkFragmentShadingRateCombinerOpKHR combinerOps[2]); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceFragmentShadingRatesKHR( + VkPhysicalDevice physicalDevice, + uint32_t* pFragmentShadingRateCount, + VkPhysicalDeviceFragmentShadingRateKHR* pFragmentShadingRates); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetFragmentShadingRateKHR( + VkCommandBuffer commandBuffer, + const VkExtent2D* pFragmentSize, + const VkFragmentShadingRateCombinerOpKHR combinerOps[2]); +#endif + + +#define VK_KHR_spirv_1_4 1 +#define VK_KHR_SPIRV_1_4_SPEC_VERSION 1 +#define VK_KHR_SPIRV_1_4_EXTENSION_NAME "VK_KHR_spirv_1_4" + + +#define VK_KHR_surface_protected_capabilities 1 +#define VK_KHR_SURFACE_PROTECTED_CAPABILITIES_SPEC_VERSION 1 +#define VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME "VK_KHR_surface_protected_capabilities" +typedef struct VkSurfaceProtectedCapabilitiesKHR { + VkStructureType sType; + const void* pNext; + VkBool32 supportsProtected; +} VkSurfaceProtectedCapabilitiesKHR; + + + +#define VK_KHR_separate_depth_stencil_layouts 1 +#define VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_SPEC_VERSION 1 +#define VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME "VK_KHR_separate_depth_stencil_layouts" +typedef VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR; + +typedef VkAttachmentReferenceStencilLayout VkAttachmentReferenceStencilLayoutKHR; + +typedef VkAttachmentDescriptionStencilLayout VkAttachmentDescriptionStencilLayoutKHR; + + + +#define VK_KHR_present_wait 1 +#define VK_KHR_PRESENT_WAIT_SPEC_VERSION 1 +#define VK_KHR_PRESENT_WAIT_EXTENSION_NAME "VK_KHR_present_wait" +typedef struct VkPhysicalDevicePresentWaitFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 presentWait; +} VkPhysicalDevicePresentWaitFeaturesKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkWaitForPresentKHR)(VkDevice device, VkSwapchainKHR swapchain, uint64_t presentId, uint64_t timeout); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkWaitForPresentKHR( + VkDevice device, + VkSwapchainKHR swapchain, + uint64_t presentId, + uint64_t timeout); +#endif + + +#define VK_KHR_uniform_buffer_standard_layout 1 +#define VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_SPEC_VERSION 1 +#define VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME "VK_KHR_uniform_buffer_standard_layout" +typedef VkPhysicalDeviceUniformBufferStandardLayoutFeatures VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR; + + + +#define VK_KHR_buffer_device_address 1 +#define VK_KHR_BUFFER_DEVICE_ADDRESS_SPEC_VERSION 1 +#define VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME "VK_KHR_buffer_device_address" +typedef VkPhysicalDeviceBufferDeviceAddressFeatures VkPhysicalDeviceBufferDeviceAddressFeaturesKHR; + +typedef VkBufferDeviceAddressInfo VkBufferDeviceAddressInfoKHR; + +typedef VkBufferOpaqueCaptureAddressCreateInfo VkBufferOpaqueCaptureAddressCreateInfoKHR; + +typedef VkMemoryOpaqueCaptureAddressAllocateInfo VkMemoryOpaqueCaptureAddressAllocateInfoKHR; + +typedef VkDeviceMemoryOpaqueCaptureAddressInfo VkDeviceMemoryOpaqueCaptureAddressInfoKHR; + +typedef VkDeviceAddress (VKAPI_PTR *PFN_vkGetBufferDeviceAddressKHR)(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); +typedef uint64_t (VKAPI_PTR *PFN_vkGetBufferOpaqueCaptureAddressKHR)(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); +typedef uint64_t (VKAPI_PTR *PFN_vkGetDeviceMemoryOpaqueCaptureAddressKHR)(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkDeviceAddress VKAPI_CALL vkGetBufferDeviceAddressKHR( + VkDevice device, + const VkBufferDeviceAddressInfo* pInfo); + +VKAPI_ATTR uint64_t VKAPI_CALL vkGetBufferOpaqueCaptureAddressKHR( + VkDevice device, + const VkBufferDeviceAddressInfo* pInfo); + +VKAPI_ATTR uint64_t VKAPI_CALL vkGetDeviceMemoryOpaqueCaptureAddressKHR( + VkDevice device, + const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo); +#endif + + +#define VK_KHR_deferred_host_operations 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeferredOperationKHR) +#define VK_KHR_DEFERRED_HOST_OPERATIONS_SPEC_VERSION 4 +#define VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME "VK_KHR_deferred_host_operations" +typedef VkResult (VKAPI_PTR *PFN_vkCreateDeferredOperationKHR)(VkDevice device, const VkAllocationCallbacks* pAllocator, VkDeferredOperationKHR* pDeferredOperation); +typedef void (VKAPI_PTR *PFN_vkDestroyDeferredOperationKHR)(VkDevice device, VkDeferredOperationKHR operation, const VkAllocationCallbacks* pAllocator); +typedef uint32_t (VKAPI_PTR *PFN_vkGetDeferredOperationMaxConcurrencyKHR)(VkDevice device, VkDeferredOperationKHR operation); +typedef VkResult (VKAPI_PTR *PFN_vkGetDeferredOperationResultKHR)(VkDevice device, VkDeferredOperationKHR operation); +typedef VkResult (VKAPI_PTR *PFN_vkDeferredOperationJoinKHR)(VkDevice device, VkDeferredOperationKHR operation); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDeferredOperationKHR( + VkDevice device, + const VkAllocationCallbacks* pAllocator, + VkDeferredOperationKHR* pDeferredOperation); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDeferredOperationKHR( + VkDevice device, + VkDeferredOperationKHR operation, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR uint32_t VKAPI_CALL vkGetDeferredOperationMaxConcurrencyKHR( + VkDevice device, + VkDeferredOperationKHR operation); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDeferredOperationResultKHR( + VkDevice device, + VkDeferredOperationKHR operation); + +VKAPI_ATTR VkResult VKAPI_CALL vkDeferredOperationJoinKHR( + VkDevice device, + VkDeferredOperationKHR operation); +#endif + + +#define VK_KHR_pipeline_executable_properties 1 +#define VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_SPEC_VERSION 1 +#define VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME "VK_KHR_pipeline_executable_properties" + +typedef enum VkPipelineExecutableStatisticFormatKHR { + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_BOOL32_KHR = 0, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR = 1, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR = 2, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR = 3, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPipelineExecutableStatisticFormatKHR; +typedef struct VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 pipelineExecutableInfo; +} VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR; + +typedef struct VkPipelineInfoKHR { + VkStructureType sType; + const void* pNext; + VkPipeline pipeline; +} VkPipelineInfoKHR; + +typedef struct VkPipelineExecutablePropertiesKHR { + VkStructureType sType; + void* pNext; + VkShaderStageFlags stages; + char name[VK_MAX_DESCRIPTION_SIZE]; + char description[VK_MAX_DESCRIPTION_SIZE]; + uint32_t subgroupSize; +} VkPipelineExecutablePropertiesKHR; + +typedef struct VkPipelineExecutableInfoKHR { + VkStructureType sType; + const void* pNext; + VkPipeline pipeline; + uint32_t executableIndex; +} VkPipelineExecutableInfoKHR; + +typedef union VkPipelineExecutableStatisticValueKHR { + VkBool32 b32; + int64_t i64; + uint64_t u64; + double f64; +} VkPipelineExecutableStatisticValueKHR; + +typedef struct VkPipelineExecutableStatisticKHR { + VkStructureType sType; + void* pNext; + char name[VK_MAX_DESCRIPTION_SIZE]; + char description[VK_MAX_DESCRIPTION_SIZE]; + VkPipelineExecutableStatisticFormatKHR format; + VkPipelineExecutableStatisticValueKHR value; +} VkPipelineExecutableStatisticKHR; + +typedef struct VkPipelineExecutableInternalRepresentationKHR { + VkStructureType sType; + void* pNext; + char name[VK_MAX_DESCRIPTION_SIZE]; + char description[VK_MAX_DESCRIPTION_SIZE]; + VkBool32 isText; + size_t dataSize; + void* pData; +} VkPipelineExecutableInternalRepresentationKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineExecutablePropertiesKHR)(VkDevice device, const VkPipelineInfoKHR* pPipelineInfo, uint32_t* pExecutableCount, VkPipelineExecutablePropertiesKHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineExecutableStatisticsKHR)(VkDevice device, const VkPipelineExecutableInfoKHR* pExecutableInfo, uint32_t* pStatisticCount, VkPipelineExecutableStatisticKHR* pStatistics); +typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineExecutableInternalRepresentationsKHR)(VkDevice device, const VkPipelineExecutableInfoKHR* pExecutableInfo, uint32_t* pInternalRepresentationCount, VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineExecutablePropertiesKHR( + VkDevice device, + const VkPipelineInfoKHR* pPipelineInfo, + uint32_t* pExecutableCount, + VkPipelineExecutablePropertiesKHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineExecutableStatisticsKHR( + VkDevice device, + const VkPipelineExecutableInfoKHR* pExecutableInfo, + uint32_t* pStatisticCount, + VkPipelineExecutableStatisticKHR* pStatistics); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineExecutableInternalRepresentationsKHR( + VkDevice device, + const VkPipelineExecutableInfoKHR* pExecutableInfo, + uint32_t* pInternalRepresentationCount, + VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations); +#endif + + +#define VK_KHR_shader_integer_dot_product 1 +#define VK_KHR_SHADER_INTEGER_DOT_PRODUCT_SPEC_VERSION 1 +#define VK_KHR_SHADER_INTEGER_DOT_PRODUCT_EXTENSION_NAME "VK_KHR_shader_integer_dot_product" +typedef struct VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderIntegerDotProduct; +} VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR; + +typedef struct VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR { + VkStructureType sType; + void* pNext; + VkBool32 integerDotProduct8BitUnsignedAccelerated; + VkBool32 integerDotProduct8BitSignedAccelerated; + VkBool32 integerDotProduct8BitMixedSignednessAccelerated; + VkBool32 integerDotProduct4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedSignedAccelerated; + VkBool32 integerDotProduct4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProduct16BitUnsignedAccelerated; + VkBool32 integerDotProduct16BitSignedAccelerated; + VkBool32 integerDotProduct16BitMixedSignednessAccelerated; + VkBool32 integerDotProduct32BitUnsignedAccelerated; + VkBool32 integerDotProduct32BitSignedAccelerated; + VkBool32 integerDotProduct32BitMixedSignednessAccelerated; + VkBool32 integerDotProduct64BitUnsignedAccelerated; + VkBool32 integerDotProduct64BitSignedAccelerated; + VkBool32 integerDotProduct64BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitUnsignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitSignedAccelerated; + VkBool32 integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated; +} VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR; + + + +#define VK_KHR_pipeline_library 1 +#define VK_KHR_PIPELINE_LIBRARY_SPEC_VERSION 1 +#define VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME "VK_KHR_pipeline_library" +typedef struct VkPipelineLibraryCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t libraryCount; + const VkPipeline* pLibraries; +} VkPipelineLibraryCreateInfoKHR; + + + +#define VK_KHR_shader_non_semantic_info 1 +#define VK_KHR_SHADER_NON_SEMANTIC_INFO_SPEC_VERSION 1 +#define VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME "VK_KHR_shader_non_semantic_info" + + +#define VK_KHR_present_id 1 +#define VK_KHR_PRESENT_ID_SPEC_VERSION 1 +#define VK_KHR_PRESENT_ID_EXTENSION_NAME "VK_KHR_present_id" +typedef struct VkPresentIdKHR { + VkStructureType sType; + const void* pNext; + uint32_t swapchainCount; + const uint64_t* pPresentIds; +} VkPresentIdKHR; + +typedef struct VkPhysicalDevicePresentIdFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 presentId; +} VkPhysicalDevicePresentIdFeaturesKHR; + + + +#define VK_KHR_synchronization2 1 +typedef uint64_t VkFlags64; +#define VK_KHR_SYNCHRONIZATION_2_SPEC_VERSION 1 +#define VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME "VK_KHR_synchronization2" +typedef VkFlags64 VkPipelineStageFlags2KHR; + +// Flag bits for VkPipelineStageFlagBits2KHR +typedef VkFlags64 VkPipelineStageFlagBits2KHR; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_NONE_KHR = 0ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT_KHR = 0x00000001ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT_KHR = 0x00000002ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT_KHR = 0x00000004ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT_KHR = 0x00000008ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TESSELLATION_CONTROL_SHADER_BIT_KHR = 0x00000010ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TESSELLATION_EVALUATION_SHADER_BIT_KHR = 0x00000020ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_GEOMETRY_SHADER_BIT_KHR = 0x00000040ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT_KHR = 0x00000080ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT_KHR = 0x00000100ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT_KHR = 0x00000200ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR = 0x00000400ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT_KHR = 0x00000800ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ALL_TRANSFER_BIT_KHR = 0x00001000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TRANSFER_BIT_KHR = 0x00001000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT_KHR = 0x00002000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_HOST_BIT_KHR = 0x00004000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT_KHR = 0x00008000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR = 0x00010000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_COPY_BIT_KHR = 0x100000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_RESOLVE_BIT_KHR = 0x200000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_BLIT_BIT_KHR = 0x400000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_CLEAR_BIT_KHR = 0x800000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT_KHR = 0x1000000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT_KHR = 0x2000000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT_KHR = 0x4000000000ULL; +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR = 0x04000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR = 0x08000000ULL; +#endif +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT = 0x01000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_CONDITIONAL_RENDERING_BIT_EXT = 0x00040000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_COMMAND_PREPROCESS_BIT_NV = 0x00020000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x00400000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_SHADING_RATE_IMAGE_BIT_NV = 0x00400000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR = 0x02000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_KHR = 0x00200000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_RAY_TRACING_SHADER_BIT_NV = 0x00200000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_NV = 0x02000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_FRAGMENT_DENSITY_PROCESS_BIT_EXT = 0x00800000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_NV = 0x00080000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_NV = 0x00100000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_SUBPASS_SHADING_BIT_HUAWEI = 0x8000000000ULL; +static const VkPipelineStageFlagBits2KHR VK_PIPELINE_STAGE_2_INVOCATION_MASK_BIT_HUAWEI = 0x10000000000ULL; + +typedef VkFlags64 VkAccessFlags2KHR; + +// Flag bits for VkAccessFlagBits2KHR +typedef VkFlags64 VkAccessFlagBits2KHR; +static const VkAccessFlagBits2KHR VK_ACCESS_2_NONE_KHR = 0ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT_KHR = 0x00000001ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_INDEX_READ_BIT_KHR = 0x00000002ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT_KHR = 0x00000004ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_UNIFORM_READ_BIT_KHR = 0x00000008ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT_KHR = 0x00000010ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_READ_BIT_KHR = 0x00000020ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_WRITE_BIT_KHR = 0x00000040ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR = 0x00000080ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT_KHR = 0x00000100ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT_KHR = 0x00000200ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT_KHR = 0x00000400ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFER_READ_BIT_KHR = 0x00000800ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFER_WRITE_BIT_KHR = 0x00001000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_HOST_READ_BIT_KHR = 0x00002000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_HOST_WRITE_BIT_KHR = 0x00004000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_MEMORY_READ_BIT_KHR = 0x00008000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_MEMORY_WRITE_BIT_KHR = 0x00010000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_SAMPLED_READ_BIT_KHR = 0x100000000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_STORAGE_READ_BIT_KHR = 0x200000000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT_KHR = 0x400000000ULL; +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkAccessFlagBits2KHR VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR = 0x800000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkAccessFlagBits2KHR VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR = 0x1000000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkAccessFlagBits2KHR VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR = 0x2000000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkAccessFlagBits2KHR VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR = 0x4000000000ULL; +#endif +static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT = 0x02000000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT = 0x04000000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT = 0x08000000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_CONDITIONAL_RENDERING_READ_BIT_EXT = 0x00100000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_COMMAND_PREPROCESS_READ_BIT_NV = 0x00020000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_COMMAND_PREPROCESS_WRITE_BIT_NV = 0x00040000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR = 0x00800000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_SHADING_RATE_IMAGE_READ_BIT_NV = 0x00800000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR = 0x00200000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR = 0x00400000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_NV = 0x00200000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_NV = 0x00400000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_FRAGMENT_DENSITY_MAP_READ_BIT_EXT = 0x01000000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT = 0x00080000ULL; +static const VkAccessFlagBits2KHR VK_ACCESS_2_INVOCATION_MASK_READ_BIT_HUAWEI = 0x8000000000ULL; + + +typedef enum VkSubmitFlagBitsKHR { + VK_SUBMIT_PROTECTED_BIT_KHR = 0x00000001, + VK_SUBMIT_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSubmitFlagBitsKHR; +typedef VkFlags VkSubmitFlagsKHR; +typedef struct VkMemoryBarrier2KHR { + VkStructureType sType; + const void* pNext; + VkPipelineStageFlags2KHR srcStageMask; + VkAccessFlags2KHR srcAccessMask; + VkPipelineStageFlags2KHR dstStageMask; + VkAccessFlags2KHR dstAccessMask; +} VkMemoryBarrier2KHR; + +typedef struct VkBufferMemoryBarrier2KHR { + VkStructureType sType; + const void* pNext; + VkPipelineStageFlags2KHR srcStageMask; + VkAccessFlags2KHR srcAccessMask; + VkPipelineStageFlags2KHR dstStageMask; + VkAccessFlags2KHR dstAccessMask; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize size; +} VkBufferMemoryBarrier2KHR; + +typedef struct VkImageMemoryBarrier2KHR { + VkStructureType sType; + const void* pNext; + VkPipelineStageFlags2KHR srcStageMask; + VkAccessFlags2KHR srcAccessMask; + VkPipelineStageFlags2KHR dstStageMask; + VkAccessFlags2KHR dstAccessMask; + VkImageLayout oldLayout; + VkImageLayout newLayout; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkImage image; + VkImageSubresourceRange subresourceRange; +} VkImageMemoryBarrier2KHR; + +typedef struct VkDependencyInfoKHR { + VkStructureType sType; + const void* pNext; + VkDependencyFlags dependencyFlags; + uint32_t memoryBarrierCount; + const VkMemoryBarrier2KHR* pMemoryBarriers; + uint32_t bufferMemoryBarrierCount; + const VkBufferMemoryBarrier2KHR* pBufferMemoryBarriers; + uint32_t imageMemoryBarrierCount; + const VkImageMemoryBarrier2KHR* pImageMemoryBarriers; +} VkDependencyInfoKHR; + +typedef struct VkSemaphoreSubmitInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + uint64_t value; + VkPipelineStageFlags2KHR stageMask; + uint32_t deviceIndex; +} VkSemaphoreSubmitInfoKHR; + +typedef struct VkCommandBufferSubmitInfoKHR { + VkStructureType sType; + const void* pNext; + VkCommandBuffer commandBuffer; + uint32_t deviceMask; +} VkCommandBufferSubmitInfoKHR; + +typedef struct VkSubmitInfo2KHR { + VkStructureType sType; + const void* pNext; + VkSubmitFlagsKHR flags; + uint32_t waitSemaphoreInfoCount; + const VkSemaphoreSubmitInfoKHR* pWaitSemaphoreInfos; + uint32_t commandBufferInfoCount; + const VkCommandBufferSubmitInfoKHR* pCommandBufferInfos; + uint32_t signalSemaphoreInfoCount; + const VkSemaphoreSubmitInfoKHR* pSignalSemaphoreInfos; +} VkSubmitInfo2KHR; + +typedef struct VkPhysicalDeviceSynchronization2FeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 synchronization2; +} VkPhysicalDeviceSynchronization2FeaturesKHR; + +typedef struct VkQueueFamilyCheckpointProperties2NV { + VkStructureType sType; + void* pNext; + VkPipelineStageFlags2KHR checkpointExecutionStageMask; +} VkQueueFamilyCheckpointProperties2NV; + +typedef struct VkCheckpointData2NV { + VkStructureType sType; + void* pNext; + VkPipelineStageFlags2KHR stage; + void* pCheckpointMarker; +} VkCheckpointData2NV; + +typedef void (VKAPI_PTR *PFN_vkCmdSetEvent2KHR)(VkCommandBuffer commandBuffer, VkEvent event, const VkDependencyInfoKHR* pDependencyInfo); +typedef void (VKAPI_PTR *PFN_vkCmdResetEvent2KHR)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags2KHR stageMask); +typedef void (VKAPI_PTR *PFN_vkCmdWaitEvents2KHR)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, const VkDependencyInfoKHR* pDependencyInfos); +typedef void (VKAPI_PTR *PFN_vkCmdPipelineBarrier2KHR)(VkCommandBuffer commandBuffer, const VkDependencyInfoKHR* pDependencyInfo); +typedef void (VKAPI_PTR *PFN_vkCmdWriteTimestamp2KHR)(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR stage, VkQueryPool queryPool, uint32_t query); +typedef VkResult (VKAPI_PTR *PFN_vkQueueSubmit2KHR)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR* pSubmits, VkFence fence); +typedef void (VKAPI_PTR *PFN_vkCmdWriteBufferMarker2AMD)(VkCommandBuffer commandBuffer, VkPipelineStageFlags2KHR stage, VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker); +typedef void (VKAPI_PTR *PFN_vkGetQueueCheckpointData2NV)(VkQueue queue, uint32_t* pCheckpointDataCount, VkCheckpointData2NV* pCheckpointData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent2KHR( + VkCommandBuffer commandBuffer, + VkEvent event, + const VkDependencyInfoKHR* pDependencyInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent2KHR( + VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags2KHR stageMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents2KHR( + VkCommandBuffer commandBuffer, + uint32_t eventCount, + const VkEvent* pEvents, + const VkDependencyInfoKHR* pDependencyInfos); + +VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier2KHR( + VkCommandBuffer commandBuffer, + const VkDependencyInfoKHR* pDependencyInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp2KHR( + VkCommandBuffer commandBuffer, + VkPipelineStageFlags2KHR stage, + VkQueryPool queryPool, + uint32_t query); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit2KHR( + VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo2KHR* pSubmits, + VkFence fence); + +VKAPI_ATTR void VKAPI_CALL vkCmdWriteBufferMarker2AMD( + VkCommandBuffer commandBuffer, + VkPipelineStageFlags2KHR stage, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + uint32_t marker); + +VKAPI_ATTR void VKAPI_CALL vkGetQueueCheckpointData2NV( + VkQueue queue, + uint32_t* pCheckpointDataCount, + VkCheckpointData2NV* pCheckpointData); +#endif + + +#define VK_KHR_shader_subgroup_uniform_control_flow 1 +#define VK_KHR_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_SPEC_VERSION 1 +#define VK_KHR_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_EXTENSION_NAME "VK_KHR_shader_subgroup_uniform_control_flow" +typedef struct VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderSubgroupUniformControlFlow; +} VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR; + + + +#define VK_KHR_zero_initialize_workgroup_memory 1 +#define VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_SPEC_VERSION 1 +#define VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_EXTENSION_NAME "VK_KHR_zero_initialize_workgroup_memory" +typedef struct VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderZeroInitializeWorkgroupMemory; +} VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR; + + + +#define VK_KHR_workgroup_memory_explicit_layout 1 +#define VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_SPEC_VERSION 1 +#define VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME "VK_KHR_workgroup_memory_explicit_layout" +typedef struct VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 workgroupMemoryExplicitLayout; + VkBool32 workgroupMemoryExplicitLayoutScalarBlockLayout; + VkBool32 workgroupMemoryExplicitLayout8BitAccess; + VkBool32 workgroupMemoryExplicitLayout16BitAccess; +} VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR; + + + +#define VK_KHR_copy_commands2 1 +#define VK_KHR_COPY_COMMANDS_2_SPEC_VERSION 1 +#define VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME "VK_KHR_copy_commands2" +typedef struct VkBufferCopy2KHR { + VkStructureType sType; + const void* pNext; + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VkDeviceSize size; +} VkBufferCopy2KHR; + +typedef struct VkCopyBufferInfo2KHR { + VkStructureType sType; + const void* pNext; + VkBuffer srcBuffer; + VkBuffer dstBuffer; + uint32_t regionCount; + const VkBufferCopy2KHR* pRegions; +} VkCopyBufferInfo2KHR; + +typedef struct VkImageCopy2KHR { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageCopy2KHR; + +typedef struct VkCopyImageInfo2KHR { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageCopy2KHR* pRegions; +} VkCopyImageInfo2KHR; + +typedef struct VkBufferImageCopy2KHR { + VkStructureType sType; + const void* pNext; + VkDeviceSize bufferOffset; + uint32_t bufferRowLength; + uint32_t bufferImageHeight; + VkImageSubresourceLayers imageSubresource; + VkOffset3D imageOffset; + VkExtent3D imageExtent; +} VkBufferImageCopy2KHR; + +typedef struct VkCopyBufferToImageInfo2KHR { + VkStructureType sType; + const void* pNext; + VkBuffer srcBuffer; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkBufferImageCopy2KHR* pRegions; +} VkCopyBufferToImageInfo2KHR; + +typedef struct VkCopyImageToBufferInfo2KHR { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkBuffer dstBuffer; + uint32_t regionCount; + const VkBufferImageCopy2KHR* pRegions; +} VkCopyImageToBufferInfo2KHR; + +typedef struct VkImageBlit2KHR { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffsets[2]; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffsets[2]; +} VkImageBlit2KHR; + +typedef struct VkBlitImageInfo2KHR { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageBlit2KHR* pRegions; + VkFilter filter; +} VkBlitImageInfo2KHR; + +typedef struct VkImageResolve2KHR { + VkStructureType sType; + const void* pNext; + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageResolve2KHR; + +typedef struct VkResolveImageInfo2KHR { + VkStructureType sType; + const void* pNext; + VkImage srcImage; + VkImageLayout srcImageLayout; + VkImage dstImage; + VkImageLayout dstImageLayout; + uint32_t regionCount; + const VkImageResolve2KHR* pRegions; +} VkResolveImageInfo2KHR; + +typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferInfo2KHR* pCopyBufferInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageInfo2KHR* pCopyImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage2KHR)(VkCommandBuffer commandBuffer, const VkCopyBufferToImageInfo2KHR* pCopyBufferToImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer2KHR)(VkCommandBuffer commandBuffer, const VkCopyImageToBufferInfo2KHR* pCopyImageToBufferInfo); +typedef void (VKAPI_PTR *PFN_vkCmdBlitImage2KHR)(VkCommandBuffer commandBuffer, const VkBlitImageInfo2KHR* pBlitImageInfo); +typedef void (VKAPI_PTR *PFN_vkCmdResolveImage2KHR)(VkCommandBuffer commandBuffer, const VkResolveImageInfo2KHR* pResolveImageInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer2KHR( + VkCommandBuffer commandBuffer, + const VkCopyBufferInfo2KHR* pCopyBufferInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage2KHR( + VkCommandBuffer commandBuffer, + const VkCopyImageInfo2KHR* pCopyImageInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage2KHR( + VkCommandBuffer commandBuffer, + const VkCopyBufferToImageInfo2KHR* pCopyBufferToImageInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer2KHR( + VkCommandBuffer commandBuffer, + const VkCopyImageToBufferInfo2KHR* pCopyImageToBufferInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdBlitImage2KHR( + VkCommandBuffer commandBuffer, + const VkBlitImageInfo2KHR* pBlitImageInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdResolveImage2KHR( + VkCommandBuffer commandBuffer, + const VkResolveImageInfo2KHR* pResolveImageInfo); +#endif + + +#define VK_KHR_format_feature_flags2 1 +#define VK_KHR_FORMAT_FEATURE_FLAGS_2_SPEC_VERSION 1 +#define VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME "VK_KHR_format_feature_flags2" +typedef VkFlags64 VkFormatFeatureFlags2KHR; + +// Flag bits for VkFormatFeatureFlagBits2KHR +typedef VkFlags64 VkFormatFeatureFlagBits2KHR; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT_KHR = 0x00000001ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT_KHR = 0x00000002ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT_KHR = 0x00000004ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT_KHR = 0x00000008ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT_KHR = 0x00000010ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT_KHR = 0x00000020ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT_KHR = 0x00000040ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT_KHR = 0x00000080ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT_KHR = 0x00000100ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT_KHR = 0x00000200ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_BLIT_SRC_BIT_KHR = 0x00000400ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_BLIT_DST_BIT_KHR = 0x00000800ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT_KHR = 0x00001000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT = 0x00002000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT_KHR = 0x00004000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT_KHR = 0x00008000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT_KHR = 0x00010000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 0x00020000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 0x00040000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 0x00080000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 0x00100000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 0x00200000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_DISJOINT_BIT_KHR = 0x00400000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_COSITED_CHROMA_SAMPLES_BIT_KHR = 0x00800000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT_KHR = 0x80000000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT_KHR = 0x100000000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT_KHR = 0x200000000ULL; +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_VIDEO_DECODE_OUTPUT_BIT_KHR = 0x02000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_VIDEO_DECODE_DPB_BIT_KHR = 0x04000000ULL; +#endif +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR = 0x20000000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x01000000ULL; +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR = 0x40000000ULL; +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_VIDEO_ENCODE_INPUT_BIT_KHR = 0x08000000ULL; +#endif +#ifdef VK_ENABLE_BETA_EXTENSIONS +static const VkFormatFeatureFlagBits2KHR VK_FORMAT_FEATURE_2_VIDEO_ENCODE_DPB_BIT_KHR = 0x10000000ULL; +#endif + +typedef struct VkFormatProperties3KHR { + VkStructureType sType; + void* pNext; + VkFormatFeatureFlags2KHR linearTilingFeatures; + VkFormatFeatureFlags2KHR optimalTilingFeatures; + VkFormatFeatureFlags2KHR bufferFeatures; +} VkFormatProperties3KHR; + + + +#define VK_KHR_maintenance4 1 +#define VK_KHR_MAINTENANCE_4_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE_4_EXTENSION_NAME "VK_KHR_maintenance4" +typedef struct VkPhysicalDeviceMaintenance4FeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 maintenance4; +} VkPhysicalDeviceMaintenance4FeaturesKHR; + +typedef struct VkPhysicalDeviceMaintenance4PropertiesKHR { + VkStructureType sType; + void* pNext; + VkDeviceSize maxBufferSize; +} VkPhysicalDeviceMaintenance4PropertiesKHR; + +typedef struct VkDeviceBufferMemoryRequirementsKHR { + VkStructureType sType; + const void* pNext; + const VkBufferCreateInfo* pCreateInfo; +} VkDeviceBufferMemoryRequirementsKHR; + +typedef struct VkDeviceImageMemoryRequirementsKHR { + VkStructureType sType; + const void* pNext; + const VkImageCreateInfo* pCreateInfo; + VkImageAspectFlagBits planeAspect; +} VkDeviceImageMemoryRequirementsKHR; + +typedef void (VKAPI_PTR *PFN_vkGetDeviceBufferMemoryRequirementsKHR)(VkDevice device, const VkDeviceBufferMemoryRequirementsKHR* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetDeviceImageMemoryRequirementsKHR)(VkDevice device, const VkDeviceImageMemoryRequirementsKHR* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetDeviceImageSparseMemoryRequirementsKHR)(VkDevice device, const VkDeviceImageMemoryRequirementsKHR* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetDeviceBufferMemoryRequirementsKHR( + VkDevice device, + const VkDeviceBufferMemoryRequirementsKHR* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageMemoryRequirementsKHR( + VkDevice device, + const VkDeviceImageMemoryRequirementsKHR* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceImageSparseMemoryRequirementsKHR( + VkDevice device, + const VkDeviceImageMemoryRequirementsKHR* pInfo, + uint32_t* pSparseMemoryRequirementCount, + VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +#endif + + #define VK_EXT_debug_report 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) - -#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 9 +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 10 #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" -#define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT -#define VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT - typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, @@ -5768,20 +8730,21 @@ typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT = 28, VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, - VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, - VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT = 33, VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT = 1000156000, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT = 1000085000, + VK_DEBUG_REPORT_OBJECT_TYPE_CU_MODULE_NVX_EXT = 1000029000, + VK_DEBUG_REPORT_OBJECT_TYPE_CU_FUNCTION_NVX_EXT = 1000029001, + VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR_EXT = 1000150000, + VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT = 1000165000, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT = 1000366000, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT, - VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, - VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT, - VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportObjectTypeEXT; - typedef enum VkDebugReportFlagBitsEXT { VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001, VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002, @@ -5791,7 +8754,6 @@ typedef enum VkDebugReportFlagBitsEXT { VK_DEBUG_REPORT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportFlagBitsEXT; typedef VkFlags VkDebugReportFlagsEXT; - typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, @@ -5810,7 +8772,6 @@ typedef struct VkDebugReportCallbackCreateInfoEXT { void* pUserData; } VkDebugReportCallbackCreateInfoEXT; - typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); @@ -5838,6 +8799,7 @@ VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( const char* pMessage); #endif + #define VK_NV_glsl_shader 1 #define VK_NV_GLSL_SHADER_SPEC_VERSION 1 #define VK_NV_GLSL_SHADER_EXTENSION_NAME "VK_NV_glsl_shader" @@ -5857,16 +8819,11 @@ VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( #define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION 1 #define VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME "VK_AMD_rasterization_order" - typedef enum VkRasterizationOrderAMD { VK_RASTERIZATION_ORDER_STRICT_AMD = 0, VK_RASTERIZATION_ORDER_RELAXED_AMD = 1, - VK_RASTERIZATION_ORDER_BEGIN_RANGE_AMD = VK_RASTERIZATION_ORDER_STRICT_AMD, - VK_RASTERIZATION_ORDER_END_RANGE_AMD = VK_RASTERIZATION_ORDER_RELAXED_AMD, - VK_RASTERIZATION_ORDER_RANGE_SIZE_AMD = (VK_RASTERIZATION_ORDER_RELAXED_AMD - VK_RASTERIZATION_ORDER_STRICT_AMD + 1), VK_RASTERIZATION_ORDER_MAX_ENUM_AMD = 0x7FFFFFFF } VkRasterizationOrderAMD; - typedef struct VkPipelineRasterizationStateRasterizationOrderAMD { VkStructureType sType; const void* pNext; @@ -5888,7 +8845,6 @@ typedef struct VkPipelineRasterizationStateRasterizationOrderAMD { #define VK_EXT_debug_marker 1 #define VK_EXT_DEBUG_MARKER_SPEC_VERSION 4 #define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker" - typedef struct VkDebugMarkerObjectNameInfoEXT { VkStructureType sType; const void* pNext; @@ -5914,7 +8870,6 @@ typedef struct VkDebugMarkerMarkerInfoEXT { float color[4]; } VkDebugMarkerMarkerInfoEXT; - typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo); typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo); typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); @@ -5942,6 +8897,7 @@ VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT( const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); #endif + #define VK_AMD_gcn_shader 1 #define VK_AMD_GCN_SHADER_SPEC_VERSION 1 #define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader" @@ -5950,7 +8906,6 @@ VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT( #define VK_NV_dedicated_allocation 1 #define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1 #define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation" - typedef struct VkDedicatedAllocationImageCreateInfoNV { VkStructureType sType; const void* pNext; @@ -5972,10 +8927,200 @@ typedef struct VkDedicatedAllocationMemoryAllocateInfoNV { -#define VK_AMD_draw_indirect_count 1 -#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 -#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count" +#define VK_EXT_transform_feedback 1 +#define VK_EXT_TRANSFORM_FEEDBACK_SPEC_VERSION 1 +#define VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME "VK_EXT_transform_feedback" +typedef VkFlags VkPipelineRasterizationStateStreamCreateFlagsEXT; +typedef struct VkPhysicalDeviceTransformFeedbackFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 transformFeedback; + VkBool32 geometryStreams; +} VkPhysicalDeviceTransformFeedbackFeaturesEXT; +typedef struct VkPhysicalDeviceTransformFeedbackPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxTransformFeedbackStreams; + uint32_t maxTransformFeedbackBuffers; + VkDeviceSize maxTransformFeedbackBufferSize; + uint32_t maxTransformFeedbackStreamDataSize; + uint32_t maxTransformFeedbackBufferDataSize; + uint32_t maxTransformFeedbackBufferDataStride; + VkBool32 transformFeedbackQueries; + VkBool32 transformFeedbackStreamsLinesTriangles; + VkBool32 transformFeedbackRasterizationStreamSelect; + VkBool32 transformFeedbackDraw; +} VkPhysicalDeviceTransformFeedbackPropertiesEXT; + +typedef struct VkPipelineRasterizationStateStreamCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPipelineRasterizationStateStreamCreateFlagsEXT flags; + uint32_t rasterizationStream; +} VkPipelineRasterizationStateStreamCreateInfoEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdBindTransformFeedbackBuffersEXT)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes); +typedef void (VKAPI_PTR *PFN_vkCmdBeginTransformFeedbackEXT)(VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer, uint32_t counterBufferCount, const VkBuffer* pCounterBuffers, const VkDeviceSize* pCounterBufferOffsets); +typedef void (VKAPI_PTR *PFN_vkCmdEndTransformFeedbackEXT)(VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer, uint32_t counterBufferCount, const VkBuffer* pCounterBuffers, const VkDeviceSize* pCounterBufferOffsets); +typedef void (VKAPI_PTR *PFN_vkCmdBeginQueryIndexedEXT)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags, uint32_t index); +typedef void (VKAPI_PTR *PFN_vkCmdEndQueryIndexedEXT)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, uint32_t index); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectByteCountEXT)(VkCommandBuffer commandBuffer, uint32_t instanceCount, uint32_t firstInstance, VkBuffer counterBuffer, VkDeviceSize counterBufferOffset, uint32_t counterOffset, uint32_t vertexStride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdBindTransformFeedbackBuffersEXT( + VkCommandBuffer commandBuffer, + uint32_t firstBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets, + const VkDeviceSize* pSizes); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginTransformFeedbackEXT( + VkCommandBuffer commandBuffer, + uint32_t firstCounterBuffer, + uint32_t counterBufferCount, + const VkBuffer* pCounterBuffers, + const VkDeviceSize* pCounterBufferOffsets); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndTransformFeedbackEXT( + VkCommandBuffer commandBuffer, + uint32_t firstCounterBuffer, + uint32_t counterBufferCount, + const VkBuffer* pCounterBuffers, + const VkDeviceSize* pCounterBufferOffsets); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginQueryIndexedEXT( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + VkQueryControlFlags flags, + uint32_t index); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndQueryIndexedEXT( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + uint32_t index); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectByteCountEXT( + VkCommandBuffer commandBuffer, + uint32_t instanceCount, + uint32_t firstInstance, + VkBuffer counterBuffer, + VkDeviceSize counterBufferOffset, + uint32_t counterOffset, + uint32_t vertexStride); +#endif + + +#define VK_NVX_binary_import 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCuModuleNVX) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCuFunctionNVX) +#define VK_NVX_BINARY_IMPORT_SPEC_VERSION 1 +#define VK_NVX_BINARY_IMPORT_EXTENSION_NAME "VK_NVX_binary_import" +typedef struct VkCuModuleCreateInfoNVX { + VkStructureType sType; + const void* pNext; + size_t dataSize; + const void* pData; +} VkCuModuleCreateInfoNVX; + +typedef struct VkCuFunctionCreateInfoNVX { + VkStructureType sType; + const void* pNext; + VkCuModuleNVX module; + const char* pName; +} VkCuFunctionCreateInfoNVX; + +typedef struct VkCuLaunchInfoNVX { + VkStructureType sType; + const void* pNext; + VkCuFunctionNVX function; + uint32_t gridDimX; + uint32_t gridDimY; + uint32_t gridDimZ; + uint32_t blockDimX; + uint32_t blockDimY; + uint32_t blockDimZ; + uint32_t sharedMemBytes; + size_t paramCount; + const void* const * pParams; + size_t extraCount; + const void* const * pExtras; +} VkCuLaunchInfoNVX; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateCuModuleNVX)(VkDevice device, const VkCuModuleCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCuModuleNVX* pModule); +typedef VkResult (VKAPI_PTR *PFN_vkCreateCuFunctionNVX)(VkDevice device, const VkCuFunctionCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCuFunctionNVX* pFunction); +typedef void (VKAPI_PTR *PFN_vkDestroyCuModuleNVX)(VkDevice device, VkCuModuleNVX module, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkDestroyCuFunctionNVX)(VkDevice device, VkCuFunctionNVX function, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkCmdCuLaunchKernelNVX)(VkCommandBuffer commandBuffer, const VkCuLaunchInfoNVX* pLaunchInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateCuModuleNVX( + VkDevice device, + const VkCuModuleCreateInfoNVX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkCuModuleNVX* pModule); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateCuFunctionNVX( + VkDevice device, + const VkCuFunctionCreateInfoNVX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkCuFunctionNVX* pFunction); + +VKAPI_ATTR void VKAPI_CALL vkDestroyCuModuleNVX( + VkDevice device, + VkCuModuleNVX module, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkDestroyCuFunctionNVX( + VkDevice device, + VkCuFunctionNVX function, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkCmdCuLaunchKernelNVX( + VkCommandBuffer commandBuffer, + const VkCuLaunchInfoNVX* pLaunchInfo); +#endif + + +#define VK_NVX_image_view_handle 1 +#define VK_NVX_IMAGE_VIEW_HANDLE_SPEC_VERSION 2 +#define VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME "VK_NVX_image_view_handle" +typedef struct VkImageViewHandleInfoNVX { + VkStructureType sType; + const void* pNext; + VkImageView imageView; + VkDescriptorType descriptorType; + VkSampler sampler; +} VkImageViewHandleInfoNVX; + +typedef struct VkImageViewAddressPropertiesNVX { + VkStructureType sType; + void* pNext; + VkDeviceAddress deviceAddress; + VkDeviceSize size; +} VkImageViewAddressPropertiesNVX; + +typedef uint32_t (VKAPI_PTR *PFN_vkGetImageViewHandleNVX)(VkDevice device, const VkImageViewHandleInfoNVX* pInfo); +typedef VkResult (VKAPI_PTR *PFN_vkGetImageViewAddressNVX)(VkDevice device, VkImageView imageView, VkImageViewAddressPropertiesNVX* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR uint32_t VKAPI_CALL vkGetImageViewHandleNVX( + VkDevice device, + const VkImageViewHandleInfoNVX* pInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetImageViewAddressNVX( + VkDevice device, + VkImageView imageView, + VkImageViewAddressPropertiesNVX* pProperties); +#endif + + +#define VK_AMD_draw_indirect_count 1 +#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 2 +#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count" typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); @@ -5999,13 +9144,14 @@ VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD( uint32_t stride); #endif + #define VK_AMD_negative_viewport_height 1 #define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1 #define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height" #define VK_AMD_gpu_shader_half_float 1 -#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1 +#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 2 #define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float" @@ -6017,7 +9163,6 @@ VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD( #define VK_AMD_texture_gather_bias_lod 1 #define VK_AMD_TEXTURE_GATHER_BIAS_LOD_SPEC_VERSION 1 #define VK_AMD_TEXTURE_GATHER_BIAS_LOD_EXTENSION_NAME "VK_AMD_texture_gather_bias_lod" - typedef struct VkTextureLODGatherFormatPropertiesAMD { VkStructureType sType; void* pNext; @@ -6030,17 +9175,12 @@ typedef struct VkTextureLODGatherFormatPropertiesAMD { #define VK_AMD_SHADER_INFO_SPEC_VERSION 1 #define VK_AMD_SHADER_INFO_EXTENSION_NAME "VK_AMD_shader_info" - typedef enum VkShaderInfoTypeAMD { VK_SHADER_INFO_TYPE_STATISTICS_AMD = 0, VK_SHADER_INFO_TYPE_BINARY_AMD = 1, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD = 2, - VK_SHADER_INFO_TYPE_BEGIN_RANGE_AMD = VK_SHADER_INFO_TYPE_STATISTICS_AMD, - VK_SHADER_INFO_TYPE_END_RANGE_AMD = VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, - VK_SHADER_INFO_TYPE_RANGE_SIZE_AMD = (VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD - VK_SHADER_INFO_TYPE_STATISTICS_AMD + 1), VK_SHADER_INFO_TYPE_MAX_ENUM_AMD = 0x7FFFFFFF } VkShaderInfoTypeAMD; - typedef struct VkShaderResourceUsageAMD { uint32_t numUsedVgprs; uint32_t numUsedSgprs; @@ -6059,7 +9199,6 @@ typedef struct VkShaderStatisticsInfoAMD { uint32_t computeWorkGroupSize[3]; } VkShaderStatisticsInfoAMD; - typedef VkResult (VKAPI_PTR *PFN_vkGetShaderInfoAMD)(VkDevice device, VkPipeline pipeline, VkShaderStageFlagBits shaderStage, VkShaderInfoTypeAMD infoType, size_t* pInfoSize, void* pInfo); #ifndef VK_NO_PROTOTYPES @@ -6072,11 +9211,23 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetShaderInfoAMD( void* pInfo); #endif + #define VK_AMD_shader_image_load_store_lod 1 #define VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_SPEC_VERSION 1 #define VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME "VK_AMD_shader_image_load_store_lod" +#define VK_NV_corner_sampled_image 1 +#define VK_NV_CORNER_SAMPLED_IMAGE_SPEC_VERSION 2 +#define VK_NV_CORNER_SAMPLED_IMAGE_EXTENSION_NAME "VK_NV_corner_sampled_image" +typedef struct VkPhysicalDeviceCornerSampledImageFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 cornerSampledImage; +} VkPhysicalDeviceCornerSampledImageFeaturesNV; + + + #define VK_IMG_format_pvrtc 1 #define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1 #define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc" @@ -6086,7 +9237,6 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetShaderInfoAMD( #define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 #define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities" - typedef enum VkExternalMemoryHandleTypeFlagBitsNV { VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002, @@ -6103,7 +9253,6 @@ typedef enum VkExternalMemoryFeatureFlagBitsNV { VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF } VkExternalMemoryFeatureFlagBitsNV; typedef VkFlags VkExternalMemoryFeatureFlagsNV; - typedef struct VkExternalImageFormatPropertiesNV { VkImageFormatProperties imageFormatProperties; VkExternalMemoryFeatureFlagsNV externalMemoryFeatures; @@ -6111,7 +9260,6 @@ typedef struct VkExternalImageFormatPropertiesNV { VkExternalMemoryHandleTypeFlagsNV compatibleHandleTypes; } VkExternalImageFormatPropertiesNV; - typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); #ifndef VK_NO_PROTOTYPES @@ -6126,10 +9274,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesN VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); #endif + #define VK_NV_external_memory 1 #define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1 #define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory" - typedef struct VkExternalMemoryImageCreateInfoNV { VkStructureType sType; const void* pNext; @@ -6145,24 +9293,19 @@ typedef struct VkExportMemoryAllocateInfoNV { #define VK_EXT_validation_flags 1 -#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1 +#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 2 #define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags" - typedef enum VkValidationCheckEXT { VK_VALIDATION_CHECK_ALL_EXT = 0, VK_VALIDATION_CHECK_SHADERS_EXT = 1, - VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT, - VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_SHADERS_EXT, - VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_SHADERS_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1), VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF } VkValidationCheckEXT; - typedef struct VkValidationFlagsEXT { - VkStructureType sType; - const void* pNext; - uint32_t disabledValidationCheckCount; - VkValidationCheckEXT* pDisabledValidationChecks; + VkStructureType sType; + const void* pNext; + uint32_t disabledValidationCheckCount; + const VkValidationCheckEXT* pDisabledValidationChecks; } VkValidationFlagsEXT; @@ -6177,237 +9320,80 @@ typedef struct VkValidationFlagsEXT { #define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote" -#define VK_NVX_device_generated_commands 1 -VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX) -VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX) - -#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3 -#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands" +#define VK_EXT_texture_compression_astc_hdr 1 +#define VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_SPEC_VERSION 1 +#define VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_EXTENSION_NAME "VK_EXT_texture_compression_astc_hdr" +typedef struct VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 textureCompressionASTC_HDR; +} VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT; -typedef enum VkIndirectCommandsTokenTypeNVX { - VK_INDIRECT_COMMANDS_TOKEN_TYPE_PIPELINE_NVX = 0, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_DESCRIPTOR_SET_NVX = 1, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_NVX = 2, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_NVX = 3, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_NVX = 4, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_NVX = 5, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_NVX = 6, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NVX = 7, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PIPELINE_NVX, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NVX, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_TYPE_PIPELINE_NVX + 1), - VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF -} VkIndirectCommandsTokenTypeNVX; -typedef enum VkObjectEntryTypeNVX { - VK_OBJECT_ENTRY_TYPE_DESCRIPTOR_SET_NVX = 0, - VK_OBJECT_ENTRY_TYPE_PIPELINE_NVX = 1, - VK_OBJECT_ENTRY_TYPE_INDEX_BUFFER_NVX = 2, - VK_OBJECT_ENTRY_TYPE_VERTEX_BUFFER_NVX = 3, - VK_OBJECT_ENTRY_TYPE_PUSH_CONSTANT_NVX = 4, - VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_TYPE_DESCRIPTOR_SET_NVX, - VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_TYPE_PUSH_CONSTANT_NVX, - VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_TYPE_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_TYPE_DESCRIPTOR_SET_NVX + 1), - VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF -} VkObjectEntryTypeNVX; - - -typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX { - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001, - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002, - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004, - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008, - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF -} VkIndirectCommandsLayoutUsageFlagBitsNVX; -typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX; - -typedef enum VkObjectEntryUsageFlagBitsNVX { - VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001, - VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002, - VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF -} VkObjectEntryUsageFlagBitsNVX; -typedef VkFlags VkObjectEntryUsageFlagsNVX; - -typedef struct VkDeviceGeneratedCommandsFeaturesNVX { +#define VK_EXT_astc_decode_mode 1 +#define VK_EXT_ASTC_DECODE_MODE_SPEC_VERSION 1 +#define VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME "VK_EXT_astc_decode_mode" +typedef struct VkImageViewASTCDecodeModeEXT { VkStructureType sType; const void* pNext; - VkBool32 computeBindingPointSupport; -} VkDeviceGeneratedCommandsFeaturesNVX; + VkFormat decodeMode; +} VkImageViewASTCDecodeModeEXT; -typedef struct VkDeviceGeneratedCommandsLimitsNVX { +typedef struct VkPhysicalDeviceASTCDecodeFeaturesEXT { VkStructureType sType; - const void* pNext; - uint32_t maxIndirectCommandsLayoutTokenCount; - uint32_t maxObjectEntryCounts; - uint32_t minSequenceCountBufferOffsetAlignment; - uint32_t minSequenceIndexBufferOffsetAlignment; - uint32_t minCommandsTokenBufferOffsetAlignment; -} VkDeviceGeneratedCommandsLimitsNVX; + void* pNext; + VkBool32 decodeModeSharedExponent; +} VkPhysicalDeviceASTCDecodeFeaturesEXT; -typedef struct VkIndirectCommandsTokenNVX { - VkIndirectCommandsTokenTypeNVX tokenType; + + +#define VK_EXT_conditional_rendering 1 +#define VK_EXT_CONDITIONAL_RENDERING_SPEC_VERSION 2 +#define VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME "VK_EXT_conditional_rendering" + +typedef enum VkConditionalRenderingFlagBitsEXT { + VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT = 0x00000001, + VK_CONDITIONAL_RENDERING_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkConditionalRenderingFlagBitsEXT; +typedef VkFlags VkConditionalRenderingFlagsEXT; +typedef struct VkConditionalRenderingBeginInfoEXT { + VkStructureType sType; + const void* pNext; VkBuffer buffer; VkDeviceSize offset; -} VkIndirectCommandsTokenNVX; + VkConditionalRenderingFlagsEXT flags; +} VkConditionalRenderingBeginInfoEXT; -typedef struct VkIndirectCommandsLayoutTokenNVX { - VkIndirectCommandsTokenTypeNVX tokenType; - uint32_t bindingUnit; - uint32_t dynamicCount; - uint32_t divisor; -} VkIndirectCommandsLayoutTokenNVX; +typedef struct VkPhysicalDeviceConditionalRenderingFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 conditionalRendering; + VkBool32 inheritedConditionalRendering; +} VkPhysicalDeviceConditionalRenderingFeaturesEXT; -typedef struct VkIndirectCommandsLayoutCreateInfoNVX { - VkStructureType sType; - const void* pNext; - VkPipelineBindPoint pipelineBindPoint; - VkIndirectCommandsLayoutUsageFlagsNVX flags; - uint32_t tokenCount; - const VkIndirectCommandsLayoutTokenNVX* pTokens; -} VkIndirectCommandsLayoutCreateInfoNVX; +typedef struct VkCommandBufferInheritanceConditionalRenderingInfoEXT { + VkStructureType sType; + const void* pNext; + VkBool32 conditionalRenderingEnable; +} VkCommandBufferInheritanceConditionalRenderingInfoEXT; -typedef struct VkCmdProcessCommandsInfoNVX { - VkStructureType sType; - const void* pNext; - VkObjectTableNVX objectTable; - VkIndirectCommandsLayoutNVX indirectCommandsLayout; - uint32_t indirectCommandsTokenCount; - const VkIndirectCommandsTokenNVX* pIndirectCommandsTokens; - uint32_t maxSequencesCount; - VkCommandBuffer targetCommandBuffer; - VkBuffer sequencesCountBuffer; - VkDeviceSize sequencesCountOffset; - VkBuffer sequencesIndexBuffer; - VkDeviceSize sequencesIndexOffset; -} VkCmdProcessCommandsInfoNVX; - -typedef struct VkCmdReserveSpaceForCommandsInfoNVX { - VkStructureType sType; - const void* pNext; - VkObjectTableNVX objectTable; - VkIndirectCommandsLayoutNVX indirectCommandsLayout; - uint32_t maxSequencesCount; -} VkCmdReserveSpaceForCommandsInfoNVX; - -typedef struct VkObjectTableCreateInfoNVX { - VkStructureType sType; - const void* pNext; - uint32_t objectCount; - const VkObjectEntryTypeNVX* pObjectEntryTypes; - const uint32_t* pObjectEntryCounts; - const VkObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags; - uint32_t maxUniformBuffersPerDescriptor; - uint32_t maxStorageBuffersPerDescriptor; - uint32_t maxStorageImagesPerDescriptor; - uint32_t maxSampledImagesPerDescriptor; - uint32_t maxPipelineLayouts; -} VkObjectTableCreateInfoNVX; - -typedef struct VkObjectTableEntryNVX { - VkObjectEntryTypeNVX type; - VkObjectEntryUsageFlagsNVX flags; -} VkObjectTableEntryNVX; - -typedef struct VkObjectTablePipelineEntryNVX { - VkObjectEntryTypeNVX type; - VkObjectEntryUsageFlagsNVX flags; - VkPipeline pipeline; -} VkObjectTablePipelineEntryNVX; - -typedef struct VkObjectTableDescriptorSetEntryNVX { - VkObjectEntryTypeNVX type; - VkObjectEntryUsageFlagsNVX flags; - VkPipelineLayout pipelineLayout; - VkDescriptorSet descriptorSet; -} VkObjectTableDescriptorSetEntryNVX; - -typedef struct VkObjectTableVertexBufferEntryNVX { - VkObjectEntryTypeNVX type; - VkObjectEntryUsageFlagsNVX flags; - VkBuffer buffer; -} VkObjectTableVertexBufferEntryNVX; - -typedef struct VkObjectTableIndexBufferEntryNVX { - VkObjectEntryTypeNVX type; - VkObjectEntryUsageFlagsNVX flags; - VkBuffer buffer; - VkIndexType indexType; -} VkObjectTableIndexBufferEntryNVX; - -typedef struct VkObjectTablePushConstantEntryNVX { - VkObjectEntryTypeNVX type; - VkObjectEntryUsageFlagsNVX flags; - VkPipelineLayout pipelineLayout; - VkShaderStageFlags stageFlags; -} VkObjectTablePushConstantEntryNVX; - - -typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); -typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); -typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); -typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator); -typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable); -typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator); -typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices); -typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices); -typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits); +typedef void (VKAPI_PTR *PFN_vkCmdBeginConditionalRenderingEXT)(VkCommandBuffer commandBuffer, const VkConditionalRenderingBeginInfoEXT* pConditionalRenderingBegin); +typedef void (VKAPI_PTR *PFN_vkCmdEndConditionalRenderingEXT)(VkCommandBuffer commandBuffer); #ifndef VK_NO_PROTOTYPES -VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX( +VKAPI_ATTR void VKAPI_CALL vkCmdBeginConditionalRenderingEXT( VkCommandBuffer commandBuffer, - const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); + const VkConditionalRenderingBeginInfoEXT* pConditionalRenderingBegin); -VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX( - VkCommandBuffer commandBuffer, - const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); - -VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX( - VkDevice device, - const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); - -VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX( - VkDevice device, - VkIndirectCommandsLayoutNVX indirectCommandsLayout, - const VkAllocationCallbacks* pAllocator); - -VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX( - VkDevice device, - const VkObjectTableCreateInfoNVX* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkObjectTableNVX* pObjectTable); - -VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX( - VkDevice device, - VkObjectTableNVX objectTable, - const VkAllocationCallbacks* pAllocator); - -VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX( - VkDevice device, - VkObjectTableNVX objectTable, - uint32_t objectCount, - const VkObjectTableEntryNVX* const* ppObjectTableEntries, - const uint32_t* pObjectIndices); - -VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX( - VkDevice device, - VkObjectTableNVX objectTable, - uint32_t objectCount, - const VkObjectEntryTypeNVX* pObjectEntryTypes, - const uint32_t* pObjectIndices); - -VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( - VkPhysicalDevice physicalDevice, - VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, - VkDeviceGeneratedCommandsLimitsNVX* pLimits); +VKAPI_ATTR void VKAPI_CALL vkCmdEndConditionalRenderingEXT( + VkCommandBuffer commandBuffer); #endif + #define VK_NV_clip_space_w_scaling 1 #define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1 #define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling" - typedef struct VkViewportWScalingNV { float xcoeff; float ycoeff; @@ -6421,7 +9407,6 @@ typedef struct VkPipelineViewportWScalingStateCreateInfoNV { const VkViewportWScalingNV* pViewportWScalings; } VkPipelineViewportWScalingStateCreateInfoNV; - typedef void (VKAPI_PTR *PFN_vkCmdSetViewportWScalingNV)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewportWScalingNV* pViewportWScalings); #ifndef VK_NO_PROTOTYPES @@ -6432,10 +9417,10 @@ VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWScalingNV( const VkViewportWScalingNV* pViewportWScalings); #endif + #define VK_EXT_direct_mode_display 1 #define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1 #define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display" - typedef VkResult (VKAPI_PTR *PFN_vkReleaseDisplayEXT)(VkPhysicalDevice physicalDevice, VkDisplayKHR display); #ifndef VK_NO_PROTOTYPES @@ -6444,18 +9429,17 @@ VKAPI_ATTR VkResult VKAPI_CALL vkReleaseDisplayEXT( VkDisplayKHR display); #endif + #define VK_EXT_display_surface_counter 1 #define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1 #define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter" -#define VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT - typedef enum VkSurfaceCounterFlagBitsEXT { - VK_SURFACE_COUNTER_VBLANK_EXT = 0x00000001, + VK_SURFACE_COUNTER_VBLANK_BIT_EXT = 0x00000001, + VK_SURFACE_COUNTER_VBLANK_EXT = VK_SURFACE_COUNTER_VBLANK_BIT_EXT, VK_SURFACE_COUNTER_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF } VkSurfaceCounterFlagBitsEXT; typedef VkFlags VkSurfaceCounterFlagsEXT; - typedef struct VkSurfaceCapabilities2EXT { VkStructureType sType; void* pNext; @@ -6472,7 +9456,6 @@ typedef struct VkSurfaceCapabilities2EXT { VkSurfaceCounterFlagsEXT supportedSurfaceCounters; } VkSurfaceCapabilities2EXT; - typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilities2EXT* pSurfaceCapabilities); #ifndef VK_NO_PROTOTYPES @@ -6482,37 +9465,27 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2EXT( VkSurfaceCapabilities2EXT* pSurfaceCapabilities); #endif + #define VK_EXT_display_control 1 #define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1 #define VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME "VK_EXT_display_control" - typedef enum VkDisplayPowerStateEXT { VK_DISPLAY_POWER_STATE_OFF_EXT = 0, VK_DISPLAY_POWER_STATE_SUSPEND_EXT = 1, VK_DISPLAY_POWER_STATE_ON_EXT = 2, - VK_DISPLAY_POWER_STATE_BEGIN_RANGE_EXT = VK_DISPLAY_POWER_STATE_OFF_EXT, - VK_DISPLAY_POWER_STATE_END_RANGE_EXT = VK_DISPLAY_POWER_STATE_ON_EXT, - VK_DISPLAY_POWER_STATE_RANGE_SIZE_EXT = (VK_DISPLAY_POWER_STATE_ON_EXT - VK_DISPLAY_POWER_STATE_OFF_EXT + 1), VK_DISPLAY_POWER_STATE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDisplayPowerStateEXT; typedef enum VkDeviceEventTypeEXT { VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT = 0, - VK_DEVICE_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT, - VK_DEVICE_EVENT_TYPE_END_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT, - VK_DEVICE_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT - VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT + 1), VK_DEVICE_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDeviceEventTypeEXT; typedef enum VkDisplayEventTypeEXT { VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT = 0, - VK_DISPLAY_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT, - VK_DISPLAY_EVENT_TYPE_END_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT, - VK_DISPLAY_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT - VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT + 1), VK_DISPLAY_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDisplayEventTypeEXT; - typedef struct VkDisplayPowerInfoEXT { VkStructureType sType; const void* pNext; @@ -6537,7 +9510,6 @@ typedef struct VkSwapchainCounterCreateInfoEXT { VkSurfaceCounterFlagsEXT surfaceCounters; } VkSwapchainCounterCreateInfoEXT; - typedef VkResult (VKAPI_PTR *PFN_vkDisplayPowerControlEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayPowerInfoEXT* pDisplayPowerInfo); typedef VkResult (VKAPI_PTR *PFN_vkRegisterDeviceEventEXT)(VkDevice device, const VkDeviceEventInfoEXT* pDeviceEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); typedef VkResult (VKAPI_PTR *PFN_vkRegisterDisplayEventEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT* pDisplayEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); @@ -6569,10 +9541,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainCounterEXT( uint64_t* pCounterValue); #endif + #define VK_GOOGLE_display_timing 1 #define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1 #define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing" - typedef struct VkRefreshCycleDurationGOOGLE { uint64_t refreshDuration; } VkRefreshCycleDurationGOOGLE; @@ -6597,7 +9569,6 @@ typedef struct VkPresentTimesInfoGOOGLE { const VkPresentTimeGOOGLE* pTimes; } VkPresentTimesInfoGOOGLE; - typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties); typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings); @@ -6614,6 +9585,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE( VkPastPresentationTimingGOOGLE* pPresentationTimings); #endif + #define VK_NV_sample_mask_override_coverage 1 #define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1 #define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage" @@ -6625,14 +9597,15 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE( #define VK_NV_viewport_array2 1 -#define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1 -#define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2" +#define VK_NV_VIEWPORT_ARRAY_2_SPEC_VERSION 1 +#define VK_NV_VIEWPORT_ARRAY_2_EXTENSION_NAME "VK_NV_viewport_array2" +#define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION VK_NV_VIEWPORT_ARRAY_2_SPEC_VERSION +#define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME VK_NV_VIEWPORT_ARRAY_2_EXTENSION_NAME #define VK_NVX_multiview_per_view_attributes 1 #define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1 #define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes" - typedef struct VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX { VkStructureType sType; void* pNext; @@ -6645,7 +9618,6 @@ typedef struct VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX { #define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1 #define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle" - typedef enum VkViewportCoordinateSwizzleNV { VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV = 0, VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV = 1, @@ -6655,14 +9627,9 @@ typedef enum VkViewportCoordinateSwizzleNV { VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV = 5, VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV = 6, VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV = 7, - VK_VIEWPORT_COORDINATE_SWIZZLE_BEGIN_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV, - VK_VIEWPORT_COORDINATE_SWIZZLE_END_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV, - VK_VIEWPORT_COORDINATE_SWIZZLE_RANGE_SIZE_NV = (VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV + 1), VK_VIEWPORT_COORDINATE_SWIZZLE_MAX_ENUM_NV = 0x7FFFFFFF } VkViewportCoordinateSwizzleNV; - typedef VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV; - typedef struct VkViewportSwizzleNV { VkViewportCoordinateSwizzleNV x; VkViewportCoordinateSwizzleNV y; @@ -6684,18 +9651,12 @@ typedef struct VkPipelineViewportSwizzleStateCreateInfoNV { #define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1 #define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles" - typedef enum VkDiscardRectangleModeEXT { VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0, VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1, - VK_DISCARD_RECTANGLE_MODE_BEGIN_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT, - VK_DISCARD_RECTANGLE_MODE_END_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT, - VK_DISCARD_RECTANGLE_MODE_RANGE_SIZE_EXT = (VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT - VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT + 1), VK_DISCARD_RECTANGLE_MODE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDiscardRectangleModeEXT; - typedef VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT; - typedef struct VkPhysicalDeviceDiscardRectanglePropertiesEXT { VkStructureType sType; void* pNext; @@ -6711,7 +9672,6 @@ typedef struct VkPipelineDiscardRectangleStateCreateInfoEXT { const VkRect2D* pDiscardRectangles; } VkPipelineDiscardRectangleStateCreateInfoEXT; - typedef void (VKAPI_PTR *PFN_vkCmdSetDiscardRectangleEXT)(VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const VkRect2D* pDiscardRectangles); #ifndef VK_NO_PROTOTYPES @@ -6722,23 +9682,18 @@ VKAPI_ATTR void VKAPI_CALL vkCmdSetDiscardRectangleEXT( const VkRect2D* pDiscardRectangles); #endif + #define VK_EXT_conservative_rasterization 1 #define VK_EXT_CONSERVATIVE_RASTERIZATION_SPEC_VERSION 1 #define VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME "VK_EXT_conservative_rasterization" - typedef enum VkConservativeRasterizationModeEXT { VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT = 0, VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT = 1, VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT = 2, - VK_CONSERVATIVE_RASTERIZATION_MODE_BEGIN_RANGE_EXT = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, - VK_CONSERVATIVE_RASTERIZATION_MODE_END_RANGE_EXT = VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT, - VK_CONSERVATIVE_RASTERIZATION_MODE_RANGE_SIZE_EXT = (VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT - VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT + 1), VK_CONSERVATIVE_RASTERIZATION_MODE_MAX_ENUM_EXT = 0x7FFFFFFF } VkConservativeRasterizationModeEXT; - typedef VkFlags VkPipelineRasterizationConservativeStateCreateFlagsEXT; - typedef struct VkPhysicalDeviceConservativeRasterizationPropertiesEXT { VkStructureType sType; void* pNext; @@ -6763,15 +9718,33 @@ typedef struct VkPipelineRasterizationConservativeStateCreateInfoEXT { +#define VK_EXT_depth_clip_enable 1 +#define VK_EXT_DEPTH_CLIP_ENABLE_SPEC_VERSION 1 +#define VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME "VK_EXT_depth_clip_enable" +typedef VkFlags VkPipelineRasterizationDepthClipStateCreateFlagsEXT; +typedef struct VkPhysicalDeviceDepthClipEnableFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 depthClipEnable; +} VkPhysicalDeviceDepthClipEnableFeaturesEXT; + +typedef struct VkPipelineRasterizationDepthClipStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPipelineRasterizationDepthClipStateCreateFlagsEXT flags; + VkBool32 depthClipEnable; +} VkPipelineRasterizationDepthClipStateCreateInfoEXT; + + + #define VK_EXT_swapchain_colorspace 1 -#define VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 3 +#define VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 4 #define VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace" #define VK_EXT_hdr_metadata 1 -#define VK_EXT_HDR_METADATA_SPEC_VERSION 1 +#define VK_EXT_HDR_METADATA_SPEC_VERSION 2 #define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata" - typedef struct VkXYColorEXT { float x; float y; @@ -6790,7 +9763,6 @@ typedef struct VkHdrMetadataEXT { float maxFrameAverageLightLevel; } VkHdrMetadataEXT; - typedef void (VKAPI_PTR *PFN_vkSetHdrMetadataEXT)(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata); #ifndef VK_NO_PROTOTYPES @@ -6801,6 +9773,7 @@ VKAPI_ATTR void VKAPI_CALL vkSetHdrMetadataEXT( const VkHdrMetadataEXT* pMetadata); #endif + #define VK_EXT_external_memory_dma_buf 1 #define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_SPEC_VERSION 1 #define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME "VK_EXT_external_memory_dma_buf" @@ -6809,17 +9782,14 @@ VKAPI_ATTR void VKAPI_CALL vkSetHdrMetadataEXT( #define VK_EXT_queue_family_foreign 1 #define VK_EXT_QUEUE_FAMILY_FOREIGN_SPEC_VERSION 1 #define VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME "VK_EXT_queue_family_foreign" -#define VK_QUEUE_FAMILY_FOREIGN_EXT (~0U-2) +#define VK_QUEUE_FAMILY_FOREIGN_EXT (~2U) #define VK_EXT_debug_utils 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugUtilsMessengerEXT) - -#define VK_EXT_DEBUG_UTILS_SPEC_VERSION 1 +#define VK_EXT_DEBUG_UTILS_SPEC_VERSION 2 #define VK_EXT_DEBUG_UTILS_EXTENSION_NAME "VK_EXT_debug_utils" - typedef VkFlags VkDebugUtilsMessengerCallbackDataFlagsEXT; -typedef VkFlags VkDebugUtilsMessengerCreateFlagsEXT; typedef enum VkDebugUtilsMessageSeverityFlagBitsEXT { VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 0x00000001, @@ -6828,7 +9798,6 @@ typedef enum VkDebugUtilsMessageSeverityFlagBitsEXT { VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT = 0x00001000, VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugUtilsMessageSeverityFlagBitsEXT; -typedef VkFlags VkDebugUtilsMessageSeverityFlagsEXT; typedef enum VkDebugUtilsMessageTypeFlagBitsEXT { VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT = 0x00000001, @@ -6837,6 +9806,14 @@ typedef enum VkDebugUtilsMessageTypeFlagBitsEXT { VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugUtilsMessageTypeFlagBitsEXT; typedef VkFlags VkDebugUtilsMessageTypeFlagsEXT; +typedef VkFlags VkDebugUtilsMessageSeverityFlagsEXT; +typedef VkFlags VkDebugUtilsMessengerCreateFlagsEXT; +typedef struct VkDebugUtilsLabelEXT { + VkStructureType sType; + const void* pNext; + const char* pLabelName; + float color[4]; +} VkDebugUtilsLabelEXT; typedef struct VkDebugUtilsObjectNameInfoEXT { VkStructureType sType; @@ -6846,23 +9823,6 @@ typedef struct VkDebugUtilsObjectNameInfoEXT { const char* pObjectName; } VkDebugUtilsObjectNameInfoEXT; -typedef struct VkDebugUtilsObjectTagInfoEXT { - VkStructureType sType; - const void* pNext; - VkObjectType objectType; - uint64_t objectHandle; - uint64_t tagName; - size_t tagSize; - const void* pTag; -} VkDebugUtilsObjectTagInfoEXT; - -typedef struct VkDebugUtilsLabelEXT { - VkStructureType sType; - const void* pNext; - const char* pLabelName; - float color[4]; -} VkDebugUtilsLabelEXT; - typedef struct VkDebugUtilsMessengerCallbackDataEXT { VkStructureType sType; const void* pNext; @@ -6871,16 +9831,16 @@ typedef struct VkDebugUtilsMessengerCallbackDataEXT { int32_t messageIdNumber; const char* pMessage; uint32_t queueLabelCount; - VkDebugUtilsLabelEXT* pQueueLabels; + const VkDebugUtilsLabelEXT* pQueueLabels; uint32_t cmdBufLabelCount; - VkDebugUtilsLabelEXT* pCmdBufLabels; + const VkDebugUtilsLabelEXT* pCmdBufLabels; uint32_t objectCount; - VkDebugUtilsObjectNameInfoEXT* pObjects; + const VkDebugUtilsObjectNameInfoEXT* pObjects; } VkDebugUtilsMessengerCallbackDataEXT; typedef VkBool32 (VKAPI_PTR *PFN_vkDebugUtilsMessengerCallbackEXT)( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); @@ -6894,6 +9854,15 @@ typedef struct VkDebugUtilsMessengerCreateInfoEXT { void* pUserData; } VkDebugUtilsMessengerCreateInfoEXT; +typedef struct VkDebugUtilsObjectTagInfoEXT { + VkStructureType sType; + const void* pNext; + VkObjectType objectType; + uint64_t objectHandle; + uint64_t tagName; + size_t tagSize; + const void* pTag; +} VkDebugUtilsObjectTagInfoEXT; typedef VkResult (VKAPI_PTR *PFN_vkSetDebugUtilsObjectNameEXT)(VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo); typedef VkResult (VKAPI_PTR *PFN_vkSetDebugUtilsObjectTagEXT)(VkDevice device, const VkDebugUtilsObjectTagInfoEXT* pTagInfo); @@ -6956,38 +9925,20 @@ VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT( const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData); #endif + #define VK_EXT_sampler_filter_minmax 1 -#define VK_EXT_SAMPLER_FILTER_MINMAX_SPEC_VERSION 1 +#define VK_EXT_SAMPLER_FILTER_MINMAX_SPEC_VERSION 2 #define VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME "VK_EXT_sampler_filter_minmax" +typedef VkSamplerReductionMode VkSamplerReductionModeEXT; +typedef VkSamplerReductionModeCreateInfo VkSamplerReductionModeCreateInfoEXT; -typedef enum VkSamplerReductionModeEXT { - VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT = 0, - VK_SAMPLER_REDUCTION_MODE_MIN_EXT = 1, - VK_SAMPLER_REDUCTION_MODE_MAX_EXT = 2, - VK_SAMPLER_REDUCTION_MODE_BEGIN_RANGE_EXT = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT, - VK_SAMPLER_REDUCTION_MODE_END_RANGE_EXT = VK_SAMPLER_REDUCTION_MODE_MAX_EXT, - VK_SAMPLER_REDUCTION_MODE_RANGE_SIZE_EXT = (VK_SAMPLER_REDUCTION_MODE_MAX_EXT - VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT + 1), - VK_SAMPLER_REDUCTION_MODE_MAX_ENUM_EXT = 0x7FFFFFFF -} VkSamplerReductionModeEXT; - -typedef struct VkSamplerReductionModeCreateInfoEXT { - VkStructureType sType; - const void* pNext; - VkSamplerReductionModeEXT reductionMode; -} VkSamplerReductionModeCreateInfoEXT; - -typedef struct VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT { - VkStructureType sType; - void* pNext; - VkBool32 filterMinmaxSingleComponentFormats; - VkBool32 filterMinmaxImageComponentMapping; -} VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT; +typedef VkPhysicalDeviceSamplerFilterMinmaxProperties VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT; #define VK_AMD_gpu_shader_int16 1 -#define VK_AMD_GPU_SHADER_INT16_SPEC_VERSION 1 +#define VK_AMD_GPU_SHADER_INT16_SPEC_VERSION 2 #define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16" @@ -7001,6 +9952,41 @@ typedef struct VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT { #define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask" +#define VK_EXT_inline_uniform_block 1 +#define VK_EXT_INLINE_UNIFORM_BLOCK_SPEC_VERSION 1 +#define VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME "VK_EXT_inline_uniform_block" +typedef struct VkPhysicalDeviceInlineUniformBlockFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 inlineUniformBlock; + VkBool32 descriptorBindingInlineUniformBlockUpdateAfterBind; +} VkPhysicalDeviceInlineUniformBlockFeaturesEXT; + +typedef struct VkPhysicalDeviceInlineUniformBlockPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxInlineUniformBlockSize; + uint32_t maxPerStageDescriptorInlineUniformBlocks; + uint32_t maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks; + uint32_t maxDescriptorSetInlineUniformBlocks; + uint32_t maxDescriptorSetUpdateAfterBindInlineUniformBlocks; +} VkPhysicalDeviceInlineUniformBlockPropertiesEXT; + +typedef struct VkWriteDescriptorSetInlineUniformBlockEXT { + VkStructureType sType; + const void* pNext; + uint32_t dataSize; + const void* pData; +} VkWriteDescriptorSetInlineUniformBlockEXT; + +typedef struct VkDescriptorPoolInlineUniformBlockCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t maxInlineUniformBlockBindings; +} VkDescriptorPoolInlineUniformBlockCreateInfoEXT; + + + #define VK_EXT_shader_stencil_export 1 #define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1 #define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export" @@ -7009,7 +9995,6 @@ typedef struct VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT { #define VK_EXT_sample_locations 1 #define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1 #define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations" - typedef struct VkSampleLocationEXT { float x; float y; @@ -7066,7 +10051,6 @@ typedef struct VkMultisamplePropertiesEXT { VkExtent2D maxSampleLocationGridSize; } VkMultisamplePropertiesEXT; - typedef void (VKAPI_PTR *PFN_vkCmdSetSampleLocationsEXT)(VkCommandBuffer commandBuffer, const VkSampleLocationsInfoEXT* pSampleLocationsInfo); typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT)(VkPhysicalDevice physicalDevice, VkSampleCountFlagBits samples, VkMultisamplePropertiesEXT* pMultisampleProperties); @@ -7081,21 +10065,17 @@ VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMultisamplePropertiesEXT( VkMultisamplePropertiesEXT* pMultisampleProperties); #endif + #define VK_EXT_blend_operation_advanced 1 #define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2 #define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced" - typedef enum VkBlendOverlapEXT { VK_BLEND_OVERLAP_UNCORRELATED_EXT = 0, VK_BLEND_OVERLAP_DISJOINT_EXT = 1, VK_BLEND_OVERLAP_CONJOINT_EXT = 2, - VK_BLEND_OVERLAP_BEGIN_RANGE_EXT = VK_BLEND_OVERLAP_UNCORRELATED_EXT, - VK_BLEND_OVERLAP_END_RANGE_EXT = VK_BLEND_OVERLAP_CONJOINT_EXT, - VK_BLEND_OVERLAP_RANGE_SIZE_EXT = (VK_BLEND_OVERLAP_CONJOINT_EXT - VK_BLEND_OVERLAP_UNCORRELATED_EXT + 1), VK_BLEND_OVERLAP_MAX_ENUM_EXT = 0x7FFFFFFF } VkBlendOverlapEXT; - typedef struct VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT { VkStructureType sType; void* pNext; @@ -7126,9 +10106,7 @@ typedef struct VkPipelineColorBlendAdvancedStateCreateInfoEXT { #define VK_NV_fragment_coverage_to_color 1 #define VK_NV_FRAGMENT_COVERAGE_TO_COLOR_SPEC_VERSION 1 #define VK_NV_FRAGMENT_COVERAGE_TO_COLOR_EXTENSION_NAME "VK_NV_fragment_coverage_to_color" - typedef VkFlags VkPipelineCoverageToColorStateCreateFlagsNV; - typedef struct VkPipelineCoverageToColorStateCreateInfoNV { VkStructureType sType; const void* pNext; @@ -7143,20 +10121,14 @@ typedef struct VkPipelineCoverageToColorStateCreateInfoNV { #define VK_NV_FRAMEBUFFER_MIXED_SAMPLES_SPEC_VERSION 1 #define VK_NV_FRAMEBUFFER_MIXED_SAMPLES_EXTENSION_NAME "VK_NV_framebuffer_mixed_samples" - typedef enum VkCoverageModulationModeNV { VK_COVERAGE_MODULATION_MODE_NONE_NV = 0, VK_COVERAGE_MODULATION_MODE_RGB_NV = 1, VK_COVERAGE_MODULATION_MODE_ALPHA_NV = 2, VK_COVERAGE_MODULATION_MODE_RGBA_NV = 3, - VK_COVERAGE_MODULATION_MODE_BEGIN_RANGE_NV = VK_COVERAGE_MODULATION_MODE_NONE_NV, - VK_COVERAGE_MODULATION_MODE_END_RANGE_NV = VK_COVERAGE_MODULATION_MODE_RGBA_NV, - VK_COVERAGE_MODULATION_MODE_RANGE_SIZE_NV = (VK_COVERAGE_MODULATION_MODE_RGBA_NV - VK_COVERAGE_MODULATION_MODE_NONE_NV + 1), VK_COVERAGE_MODULATION_MODE_MAX_ENUM_NV = 0x7FFFFFFF } VkCoverageModulationModeNV; - typedef VkFlags VkPipelineCoverageModulationStateCreateFlagsNV; - typedef struct VkPipelineCoverageModulationStateCreateInfoNV { VkStructureType sType; const void* pNext; @@ -7174,29 +10146,108 @@ typedef struct VkPipelineCoverageModulationStateCreateInfoNV { #define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle" +#define VK_NV_shader_sm_builtins 1 +#define VK_NV_SHADER_SM_BUILTINS_SPEC_VERSION 1 +#define VK_NV_SHADER_SM_BUILTINS_EXTENSION_NAME "VK_NV_shader_sm_builtins" +typedef struct VkPhysicalDeviceShaderSMBuiltinsPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t shaderSMCount; + uint32_t shaderWarpsPerSM; +} VkPhysicalDeviceShaderSMBuiltinsPropertiesNV; + +typedef struct VkPhysicalDeviceShaderSMBuiltinsFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 shaderSMBuiltins; +} VkPhysicalDeviceShaderSMBuiltinsFeaturesNV; + + + #define VK_EXT_post_depth_coverage 1 #define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1 #define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage" +#define VK_EXT_image_drm_format_modifier 1 +#define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION 2 +#define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME "VK_EXT_image_drm_format_modifier" +typedef struct VkDrmFormatModifierPropertiesEXT { + uint64_t drmFormatModifier; + uint32_t drmFormatModifierPlaneCount; + VkFormatFeatureFlags drmFormatModifierTilingFeatures; +} VkDrmFormatModifierPropertiesEXT; + +typedef struct VkDrmFormatModifierPropertiesListEXT { + VkStructureType sType; + void* pNext; + uint32_t drmFormatModifierCount; + VkDrmFormatModifierPropertiesEXT* pDrmFormatModifierProperties; +} VkDrmFormatModifierPropertiesListEXT; + +typedef struct VkPhysicalDeviceImageDrmFormatModifierInfoEXT { + VkStructureType sType; + const void* pNext; + uint64_t drmFormatModifier; + VkSharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; +} VkPhysicalDeviceImageDrmFormatModifierInfoEXT; + +typedef struct VkImageDrmFormatModifierListCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t drmFormatModifierCount; + const uint64_t* pDrmFormatModifiers; +} VkImageDrmFormatModifierListCreateInfoEXT; + +typedef struct VkImageDrmFormatModifierExplicitCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint64_t drmFormatModifier; + uint32_t drmFormatModifierPlaneCount; + const VkSubresourceLayout* pPlaneLayouts; +} VkImageDrmFormatModifierExplicitCreateInfoEXT; + +typedef struct VkImageDrmFormatModifierPropertiesEXT { + VkStructureType sType; + void* pNext; + uint64_t drmFormatModifier; +} VkImageDrmFormatModifierPropertiesEXT; + +typedef struct VkDrmFormatModifierProperties2EXT { + uint64_t drmFormatModifier; + uint32_t drmFormatModifierPlaneCount; + VkFormatFeatureFlags2KHR drmFormatModifierTilingFeatures; +} VkDrmFormatModifierProperties2EXT; + +typedef struct VkDrmFormatModifierPropertiesList2EXT { + VkStructureType sType; + void* pNext; + uint32_t drmFormatModifierCount; + VkDrmFormatModifierProperties2EXT* pDrmFormatModifierProperties; +} VkDrmFormatModifierPropertiesList2EXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetImageDrmFormatModifierPropertiesEXT)(VkDevice device, VkImage image, VkImageDrmFormatModifierPropertiesEXT* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetImageDrmFormatModifierPropertiesEXT( + VkDevice device, + VkImage image, + VkImageDrmFormatModifierPropertiesEXT* pProperties); +#endif + + #define VK_EXT_validation_cache 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkValidationCacheEXT) - #define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1 #define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache" -#define VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT - typedef enum VkValidationCacheHeaderVersionEXT { VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1, - VK_VALIDATION_CACHE_HEADER_VERSION_BEGIN_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT, - VK_VALIDATION_CACHE_HEADER_VERSION_END_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT, - VK_VALIDATION_CACHE_HEADER_VERSION_RANGE_SIZE_EXT = (VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT - VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT + 1), VK_VALIDATION_CACHE_HEADER_VERSION_MAX_ENUM_EXT = 0x7FFFFFFF } VkValidationCacheHeaderVersionEXT; - typedef VkFlags VkValidationCacheCreateFlagsEXT; - typedef struct VkValidationCacheCreateInfoEXT { VkStructureType sType; const void* pNext; @@ -7211,7 +10262,6 @@ typedef struct VkShaderModuleValidationCacheCreateInfoEXT { VkValidationCacheEXT validationCache; } VkShaderModuleValidationCacheCreateInfoEXT; - typedef VkResult (VKAPI_PTR *PFN_vkCreateValidationCacheEXT)(VkDevice device, const VkValidationCacheCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkValidationCacheEXT* pValidationCache); typedef void (VKAPI_PTR *PFN_vkDestroyValidationCacheEXT)(VkDevice device, VkValidationCacheEXT validationCache, const VkAllocationCallbacks* pAllocator); typedef VkResult (VKAPI_PTR *PFN_vkMergeValidationCachesEXT)(VkDevice device, VkValidationCacheEXT dstCache, uint32_t srcCacheCount, const VkValidationCacheEXT* pSrcCaches); @@ -7242,92 +10292,23 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetValidationCacheDataEXT( void* pData); #endif + #define VK_EXT_descriptor_indexing 1 #define VK_EXT_DESCRIPTOR_INDEXING_SPEC_VERSION 2 #define VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME "VK_EXT_descriptor_indexing" +typedef VkDescriptorBindingFlagBits VkDescriptorBindingFlagBitsEXT; +typedef VkDescriptorBindingFlags VkDescriptorBindingFlagsEXT; -typedef enum VkDescriptorBindingFlagBitsEXT { - VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT = 0x00000001, - VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT = 0x00000002, - VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT = 0x00000004, - VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT = 0x00000008, - VK_DESCRIPTOR_BINDING_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF -} VkDescriptorBindingFlagBitsEXT; -typedef VkFlags VkDescriptorBindingFlagsEXT; +typedef VkDescriptorSetLayoutBindingFlagsCreateInfo VkDescriptorSetLayoutBindingFlagsCreateInfoEXT; -typedef struct VkDescriptorSetLayoutBindingFlagsCreateInfoEXT { - VkStructureType sType; - const void* pNext; - uint32_t bindingCount; - const VkDescriptorBindingFlagsEXT* pBindingFlags; -} VkDescriptorSetLayoutBindingFlagsCreateInfoEXT; +typedef VkPhysicalDeviceDescriptorIndexingFeatures VkPhysicalDeviceDescriptorIndexingFeaturesEXT; -typedef struct VkPhysicalDeviceDescriptorIndexingFeaturesEXT { - VkStructureType sType; - void* pNext; - VkBool32 shaderInputAttachmentArrayDynamicIndexing; - VkBool32 shaderUniformTexelBufferArrayDynamicIndexing; - VkBool32 shaderStorageTexelBufferArrayDynamicIndexing; - VkBool32 shaderUniformBufferArrayNonUniformIndexing; - VkBool32 shaderSampledImageArrayNonUniformIndexing; - VkBool32 shaderStorageBufferArrayNonUniformIndexing; - VkBool32 shaderStorageImageArrayNonUniformIndexing; - VkBool32 shaderInputAttachmentArrayNonUniformIndexing; - VkBool32 shaderUniformTexelBufferArrayNonUniformIndexing; - VkBool32 shaderStorageTexelBufferArrayNonUniformIndexing; - VkBool32 descriptorBindingUniformBufferUpdateAfterBind; - VkBool32 descriptorBindingSampledImageUpdateAfterBind; - VkBool32 descriptorBindingStorageImageUpdateAfterBind; - VkBool32 descriptorBindingStorageBufferUpdateAfterBind; - VkBool32 descriptorBindingUniformTexelBufferUpdateAfterBind; - VkBool32 descriptorBindingStorageTexelBufferUpdateAfterBind; - VkBool32 descriptorBindingUpdateUnusedWhilePending; - VkBool32 descriptorBindingPartiallyBound; - VkBool32 descriptorBindingVariableDescriptorCount; - VkBool32 runtimeDescriptorArray; -} VkPhysicalDeviceDescriptorIndexingFeaturesEXT; +typedef VkPhysicalDeviceDescriptorIndexingProperties VkPhysicalDeviceDescriptorIndexingPropertiesEXT; -typedef struct VkPhysicalDeviceDescriptorIndexingPropertiesEXT { - VkStructureType sType; - void* pNext; - uint32_t maxUpdateAfterBindDescriptorsInAllPools; - VkBool32 shaderUniformBufferArrayNonUniformIndexingNative; - VkBool32 shaderSampledImageArrayNonUniformIndexingNative; - VkBool32 shaderStorageBufferArrayNonUniformIndexingNative; - VkBool32 shaderStorageImageArrayNonUniformIndexingNative; - VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative; - VkBool32 robustBufferAccessUpdateAfterBind; - VkBool32 quadDivergentImplicitLod; - uint32_t maxPerStageDescriptorUpdateAfterBindSamplers; - uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers; - uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers; - uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages; - uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages; - uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments; - uint32_t maxPerStageUpdateAfterBindResources; - uint32_t maxDescriptorSetUpdateAfterBindSamplers; - uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers; - uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic; - uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers; - uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic; - uint32_t maxDescriptorSetUpdateAfterBindSampledImages; - uint32_t maxDescriptorSetUpdateAfterBindStorageImages; - uint32_t maxDescriptorSetUpdateAfterBindInputAttachments; -} VkPhysicalDeviceDescriptorIndexingPropertiesEXT; +typedef VkDescriptorSetVariableDescriptorCountAllocateInfo VkDescriptorSetVariableDescriptorCountAllocateInfoEXT; -typedef struct VkDescriptorSetVariableDescriptorCountAllocateInfoEXT { - VkStructureType sType; - const void* pNext; - uint32_t descriptorSetCount; - const uint32_t* pDescriptorCounts; -} VkDescriptorSetVariableDescriptorCountAllocateInfoEXT; - -typedef struct VkDescriptorSetVariableDescriptorCountLayoutSupportEXT { - VkStructureType sType; - void* pNext; - uint32_t maxVariableDescriptorCount; -} VkDescriptorSetVariableDescriptorCountLayoutSupportEXT; +typedef VkDescriptorSetVariableDescriptorCountLayoutSupport VkDescriptorSetVariableDescriptorCountLayoutSupportEXT; @@ -7336,22 +10317,526 @@ typedef struct VkDescriptorSetVariableDescriptorCountLayoutSupportEXT { #define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer" +#define VK_NV_shading_rate_image 1 +#define VK_NV_SHADING_RATE_IMAGE_SPEC_VERSION 3 +#define VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME "VK_NV_shading_rate_image" + +typedef enum VkShadingRatePaletteEntryNV { + VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV = 0, + VK_SHADING_RATE_PALETTE_ENTRY_16_INVOCATIONS_PER_PIXEL_NV = 1, + VK_SHADING_RATE_PALETTE_ENTRY_8_INVOCATIONS_PER_PIXEL_NV = 2, + VK_SHADING_RATE_PALETTE_ENTRY_4_INVOCATIONS_PER_PIXEL_NV = 3, + VK_SHADING_RATE_PALETTE_ENTRY_2_INVOCATIONS_PER_PIXEL_NV = 4, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV = 5, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV = 6, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV = 7, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV = 8, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV = 9, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV = 10, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV = 11, + VK_SHADING_RATE_PALETTE_ENTRY_MAX_ENUM_NV = 0x7FFFFFFF +} VkShadingRatePaletteEntryNV; + +typedef enum VkCoarseSampleOrderTypeNV { + VK_COARSE_SAMPLE_ORDER_TYPE_DEFAULT_NV = 0, + VK_COARSE_SAMPLE_ORDER_TYPE_CUSTOM_NV = 1, + VK_COARSE_SAMPLE_ORDER_TYPE_PIXEL_MAJOR_NV = 2, + VK_COARSE_SAMPLE_ORDER_TYPE_SAMPLE_MAJOR_NV = 3, + VK_COARSE_SAMPLE_ORDER_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkCoarseSampleOrderTypeNV; +typedef struct VkShadingRatePaletteNV { + uint32_t shadingRatePaletteEntryCount; + const VkShadingRatePaletteEntryNV* pShadingRatePaletteEntries; +} VkShadingRatePaletteNV; + +typedef struct VkPipelineViewportShadingRateImageStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 shadingRateImageEnable; + uint32_t viewportCount; + const VkShadingRatePaletteNV* pShadingRatePalettes; +} VkPipelineViewportShadingRateImageStateCreateInfoNV; + +typedef struct VkPhysicalDeviceShadingRateImageFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 shadingRateImage; + VkBool32 shadingRateCoarseSampleOrder; +} VkPhysicalDeviceShadingRateImageFeaturesNV; + +typedef struct VkPhysicalDeviceShadingRateImagePropertiesNV { + VkStructureType sType; + void* pNext; + VkExtent2D shadingRateTexelSize; + uint32_t shadingRatePaletteSize; + uint32_t shadingRateMaxCoarseSamples; +} VkPhysicalDeviceShadingRateImagePropertiesNV; + +typedef struct VkCoarseSampleLocationNV { + uint32_t pixelX; + uint32_t pixelY; + uint32_t sample; +} VkCoarseSampleLocationNV; + +typedef struct VkCoarseSampleOrderCustomNV { + VkShadingRatePaletteEntryNV shadingRate; + uint32_t sampleCount; + uint32_t sampleLocationCount; + const VkCoarseSampleLocationNV* pSampleLocations; +} VkCoarseSampleOrderCustomNV; + +typedef struct VkPipelineViewportCoarseSampleOrderStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkCoarseSampleOrderTypeNV sampleOrderType; + uint32_t customSampleOrderCount; + const VkCoarseSampleOrderCustomNV* pCustomSampleOrders; +} VkPipelineViewportCoarseSampleOrderStateCreateInfoNV; + +typedef void (VKAPI_PTR *PFN_vkCmdBindShadingRateImageNV)(VkCommandBuffer commandBuffer, VkImageView imageView, VkImageLayout imageLayout); +typedef void (VKAPI_PTR *PFN_vkCmdSetViewportShadingRatePaletteNV)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkShadingRatePaletteNV* pShadingRatePalettes); +typedef void (VKAPI_PTR *PFN_vkCmdSetCoarseSampleOrderNV)(VkCommandBuffer commandBuffer, VkCoarseSampleOrderTypeNV sampleOrderType, uint32_t customSampleOrderCount, const VkCoarseSampleOrderCustomNV* pCustomSampleOrders); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdBindShadingRateImageNV( + VkCommandBuffer commandBuffer, + VkImageView imageView, + VkImageLayout imageLayout); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportShadingRatePaletteNV( + VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkShadingRatePaletteNV* pShadingRatePalettes); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetCoarseSampleOrderNV( + VkCommandBuffer commandBuffer, + VkCoarseSampleOrderTypeNV sampleOrderType, + uint32_t customSampleOrderCount, + const VkCoarseSampleOrderCustomNV* pCustomSampleOrders); +#endif + + +#define VK_NV_ray_tracing 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkAccelerationStructureNV) +#define VK_NV_RAY_TRACING_SPEC_VERSION 3 +#define VK_NV_RAY_TRACING_EXTENSION_NAME "VK_NV_ray_tracing" +#define VK_SHADER_UNUSED_KHR (~0U) +#define VK_SHADER_UNUSED_NV VK_SHADER_UNUSED_KHR + +typedef enum VkRayTracingShaderGroupTypeKHR { + VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR = 0, + VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR = 1, + VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR = 2, + VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR, + VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR, + VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR, + VK_RAY_TRACING_SHADER_GROUP_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkRayTracingShaderGroupTypeKHR; +typedef VkRayTracingShaderGroupTypeKHR VkRayTracingShaderGroupTypeNV; + + +typedef enum VkGeometryTypeKHR { + VK_GEOMETRY_TYPE_TRIANGLES_KHR = 0, + VK_GEOMETRY_TYPE_AABBS_KHR = 1, + VK_GEOMETRY_TYPE_INSTANCES_KHR = 2, + VK_GEOMETRY_TYPE_TRIANGLES_NV = VK_GEOMETRY_TYPE_TRIANGLES_KHR, + VK_GEOMETRY_TYPE_AABBS_NV = VK_GEOMETRY_TYPE_AABBS_KHR, + VK_GEOMETRY_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkGeometryTypeKHR; +typedef VkGeometryTypeKHR VkGeometryTypeNV; + + +typedef enum VkAccelerationStructureTypeKHR { + VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR = 0, + VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR = 1, + VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR = 2, + VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, + VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, + VK_ACCELERATION_STRUCTURE_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkAccelerationStructureTypeKHR; +typedef VkAccelerationStructureTypeKHR VkAccelerationStructureTypeNV; + + +typedef enum VkCopyAccelerationStructureModeKHR { + VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR = 0, + VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR = 1, + VK_COPY_ACCELERATION_STRUCTURE_MODE_SERIALIZE_KHR = 2, + VK_COPY_ACCELERATION_STRUCTURE_MODE_DESERIALIZE_KHR = 3, + VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_NV = VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR, + VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV = VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR, + VK_COPY_ACCELERATION_STRUCTURE_MODE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkCopyAccelerationStructureModeKHR; +typedef VkCopyAccelerationStructureModeKHR VkCopyAccelerationStructureModeNV; + + +typedef enum VkAccelerationStructureMemoryRequirementsTypeNV { + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV = 0, + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV = 1, + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV = 2, + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkAccelerationStructureMemoryRequirementsTypeNV; + +typedef enum VkGeometryFlagBitsKHR { + VK_GEOMETRY_OPAQUE_BIT_KHR = 0x00000001, + VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR = 0x00000002, + VK_GEOMETRY_OPAQUE_BIT_NV = VK_GEOMETRY_OPAQUE_BIT_KHR, + VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_NV = VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR, + VK_GEOMETRY_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkGeometryFlagBitsKHR; +typedef VkFlags VkGeometryFlagsKHR; +typedef VkGeometryFlagsKHR VkGeometryFlagsNV; + +typedef VkGeometryFlagBitsKHR VkGeometryFlagBitsNV; + + +typedef enum VkGeometryInstanceFlagBitsKHR { + VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR = 0x00000001, + VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR = 0x00000002, + VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR = 0x00000004, + VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR = 0x00000008, + VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR = VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR, + VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR, + VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_NV = VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR, + VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_NV = VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR, + VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_NV = VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR, + VK_GEOMETRY_INSTANCE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkGeometryInstanceFlagBitsKHR; +typedef VkFlags VkGeometryInstanceFlagsKHR; +typedef VkGeometryInstanceFlagsKHR VkGeometryInstanceFlagsNV; + +typedef VkGeometryInstanceFlagBitsKHR VkGeometryInstanceFlagBitsNV; + + +typedef enum VkBuildAccelerationStructureFlagBitsKHR { + VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR = 0x00000001, + VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR = 0x00000002, + VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR = 0x00000004, + VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR = 0x00000008, + VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_KHR = 0x00000010, + VK_BUILD_ACCELERATION_STRUCTURE_MOTION_BIT_NV = 0x00000020, + VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_NV = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR, + VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_NV = VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR, + VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_NV = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR, + VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_NV = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR, + VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_NV = VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_KHR, + VK_BUILD_ACCELERATION_STRUCTURE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkBuildAccelerationStructureFlagBitsKHR; +typedef VkFlags VkBuildAccelerationStructureFlagsKHR; +typedef VkBuildAccelerationStructureFlagsKHR VkBuildAccelerationStructureFlagsNV; + +typedef VkBuildAccelerationStructureFlagBitsKHR VkBuildAccelerationStructureFlagBitsNV; + +typedef struct VkRayTracingShaderGroupCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkRayTracingShaderGroupTypeKHR type; + uint32_t generalShader; + uint32_t closestHitShader; + uint32_t anyHitShader; + uint32_t intersectionShader; +} VkRayTracingShaderGroupCreateInfoNV; + +typedef struct VkRayTracingPipelineCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineCreateFlags flags; + uint32_t stageCount; + const VkPipelineShaderStageCreateInfo* pStages; + uint32_t groupCount; + const VkRayTracingShaderGroupCreateInfoNV* pGroups; + uint32_t maxRecursionDepth; + VkPipelineLayout layout; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkRayTracingPipelineCreateInfoNV; + +typedef struct VkGeometryTrianglesNV { + VkStructureType sType; + const void* pNext; + VkBuffer vertexData; + VkDeviceSize vertexOffset; + uint32_t vertexCount; + VkDeviceSize vertexStride; + VkFormat vertexFormat; + VkBuffer indexData; + VkDeviceSize indexOffset; + uint32_t indexCount; + VkIndexType indexType; + VkBuffer transformData; + VkDeviceSize transformOffset; +} VkGeometryTrianglesNV; + +typedef struct VkGeometryAABBNV { + VkStructureType sType; + const void* pNext; + VkBuffer aabbData; + uint32_t numAABBs; + uint32_t stride; + VkDeviceSize offset; +} VkGeometryAABBNV; + +typedef struct VkGeometryDataNV { + VkGeometryTrianglesNV triangles; + VkGeometryAABBNV aabbs; +} VkGeometryDataNV; + +typedef struct VkGeometryNV { + VkStructureType sType; + const void* pNext; + VkGeometryTypeKHR geometryType; + VkGeometryDataNV geometry; + VkGeometryFlagsKHR flags; +} VkGeometryNV; + +typedef struct VkAccelerationStructureInfoNV { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureTypeNV type; + VkBuildAccelerationStructureFlagsNV flags; + uint32_t instanceCount; + uint32_t geometryCount; + const VkGeometryNV* pGeometries; +} VkAccelerationStructureInfoNV; + +typedef struct VkAccelerationStructureCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkDeviceSize compactedSize; + VkAccelerationStructureInfoNV info; +} VkAccelerationStructureCreateInfoNV; + +typedef struct VkBindAccelerationStructureMemoryInfoNV { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureNV accelerationStructure; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + uint32_t deviceIndexCount; + const uint32_t* pDeviceIndices; +} VkBindAccelerationStructureMemoryInfoNV; + +typedef struct VkWriteDescriptorSetAccelerationStructureNV { + VkStructureType sType; + const void* pNext; + uint32_t accelerationStructureCount; + const VkAccelerationStructureNV* pAccelerationStructures; +} VkWriteDescriptorSetAccelerationStructureNV; + +typedef struct VkAccelerationStructureMemoryRequirementsInfoNV { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureMemoryRequirementsTypeNV type; + VkAccelerationStructureNV accelerationStructure; +} VkAccelerationStructureMemoryRequirementsInfoNV; + +typedef struct VkPhysicalDeviceRayTracingPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t shaderGroupHandleSize; + uint32_t maxRecursionDepth; + uint32_t maxShaderGroupStride; + uint32_t shaderGroupBaseAlignment; + uint64_t maxGeometryCount; + uint64_t maxInstanceCount; + uint64_t maxTriangleCount; + uint32_t maxDescriptorSetAccelerationStructures; +} VkPhysicalDeviceRayTracingPropertiesNV; + +typedef struct VkTransformMatrixKHR { + float matrix[3][4]; +} VkTransformMatrixKHR; + +typedef VkTransformMatrixKHR VkTransformMatrixNV; + +typedef struct VkAabbPositionsKHR { + float minX; + float minY; + float minZ; + float maxX; + float maxY; + float maxZ; +} VkAabbPositionsKHR; + +typedef VkAabbPositionsKHR VkAabbPositionsNV; + +typedef struct VkAccelerationStructureInstanceKHR { + VkTransformMatrixKHR transform; + uint32_t instanceCustomIndex:24; + uint32_t mask:8; + uint32_t instanceShaderBindingTableRecordOffset:24; + VkGeometryInstanceFlagsKHR flags:8; + uint64_t accelerationStructureReference; +} VkAccelerationStructureInstanceKHR; + +typedef VkAccelerationStructureInstanceKHR VkAccelerationStructureInstanceNV; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateAccelerationStructureNV)(VkDevice device, const VkAccelerationStructureCreateInfoNV* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkAccelerationStructureNV* pAccelerationStructure); +typedef void (VKAPI_PTR *PFN_vkDestroyAccelerationStructureNV)(VkDevice device, VkAccelerationStructureNV accelerationStructure, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkGetAccelerationStructureMemoryRequirementsNV)(VkDevice device, const VkAccelerationStructureMemoryRequirementsInfoNV* pInfo, VkMemoryRequirements2KHR* pMemoryRequirements); +typedef VkResult (VKAPI_PTR *PFN_vkBindAccelerationStructureMemoryNV)(VkDevice device, uint32_t bindInfoCount, const VkBindAccelerationStructureMemoryInfoNV* pBindInfos); +typedef void (VKAPI_PTR *PFN_vkCmdBuildAccelerationStructureNV)(VkCommandBuffer commandBuffer, const VkAccelerationStructureInfoNV* pInfo, VkBuffer instanceData, VkDeviceSize instanceOffset, VkBool32 update, VkAccelerationStructureNV dst, VkAccelerationStructureNV src, VkBuffer scratch, VkDeviceSize scratchOffset); +typedef void (VKAPI_PTR *PFN_vkCmdCopyAccelerationStructureNV)(VkCommandBuffer commandBuffer, VkAccelerationStructureNV dst, VkAccelerationStructureNV src, VkCopyAccelerationStructureModeKHR mode); +typedef void (VKAPI_PTR *PFN_vkCmdTraceRaysNV)(VkCommandBuffer commandBuffer, VkBuffer raygenShaderBindingTableBuffer, VkDeviceSize raygenShaderBindingOffset, VkBuffer missShaderBindingTableBuffer, VkDeviceSize missShaderBindingOffset, VkDeviceSize missShaderBindingStride, VkBuffer hitShaderBindingTableBuffer, VkDeviceSize hitShaderBindingOffset, VkDeviceSize hitShaderBindingStride, VkBuffer callableShaderBindingTableBuffer, VkDeviceSize callableShaderBindingOffset, VkDeviceSize callableShaderBindingStride, uint32_t width, uint32_t height, uint32_t depth); +typedef VkResult (VKAPI_PTR *PFN_vkCreateRayTracingPipelinesNV)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkRayTracingPipelineCreateInfoNV* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); +typedef VkResult (VKAPI_PTR *PFN_vkGetRayTracingShaderGroupHandlesKHR)(VkDevice device, VkPipeline pipeline, uint32_t firstGroup, uint32_t groupCount, size_t dataSize, void* pData); +typedef VkResult (VKAPI_PTR *PFN_vkGetRayTracingShaderGroupHandlesNV)(VkDevice device, VkPipeline pipeline, uint32_t firstGroup, uint32_t groupCount, size_t dataSize, void* pData); +typedef VkResult (VKAPI_PTR *PFN_vkGetAccelerationStructureHandleNV)(VkDevice device, VkAccelerationStructureNV accelerationStructure, size_t dataSize, void* pData); +typedef void (VKAPI_PTR *PFN_vkCmdWriteAccelerationStructuresPropertiesNV)(VkCommandBuffer commandBuffer, uint32_t accelerationStructureCount, const VkAccelerationStructureNV* pAccelerationStructures, VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery); +typedef VkResult (VKAPI_PTR *PFN_vkCompileDeferredNV)(VkDevice device, VkPipeline pipeline, uint32_t shader); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateAccelerationStructureNV( + VkDevice device, + const VkAccelerationStructureCreateInfoNV* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkAccelerationStructureNV* pAccelerationStructure); + +VKAPI_ATTR void VKAPI_CALL vkDestroyAccelerationStructureNV( + VkDevice device, + VkAccelerationStructureNV accelerationStructure, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkGetAccelerationStructureMemoryRequirementsNV( + VkDevice device, + const VkAccelerationStructureMemoryRequirementsInfoNV* pInfo, + VkMemoryRequirements2KHR* pMemoryRequirements); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindAccelerationStructureMemoryNV( + VkDevice device, + uint32_t bindInfoCount, + const VkBindAccelerationStructureMemoryInfoNV* pBindInfos); + +VKAPI_ATTR void VKAPI_CALL vkCmdBuildAccelerationStructureNV( + VkCommandBuffer commandBuffer, + const VkAccelerationStructureInfoNV* pInfo, + VkBuffer instanceData, + VkDeviceSize instanceOffset, + VkBool32 update, + VkAccelerationStructureNV dst, + VkAccelerationStructureNV src, + VkBuffer scratch, + VkDeviceSize scratchOffset); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyAccelerationStructureNV( + VkCommandBuffer commandBuffer, + VkAccelerationStructureNV dst, + VkAccelerationStructureNV src, + VkCopyAccelerationStructureModeKHR mode); + +VKAPI_ATTR void VKAPI_CALL vkCmdTraceRaysNV( + VkCommandBuffer commandBuffer, + VkBuffer raygenShaderBindingTableBuffer, + VkDeviceSize raygenShaderBindingOffset, + VkBuffer missShaderBindingTableBuffer, + VkDeviceSize missShaderBindingOffset, + VkDeviceSize missShaderBindingStride, + VkBuffer hitShaderBindingTableBuffer, + VkDeviceSize hitShaderBindingOffset, + VkDeviceSize hitShaderBindingStride, + VkBuffer callableShaderBindingTableBuffer, + VkDeviceSize callableShaderBindingOffset, + VkDeviceSize callableShaderBindingStride, + uint32_t width, + uint32_t height, + uint32_t depth); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateRayTracingPipelinesNV( + VkDevice device, + VkPipelineCache pipelineCache, + uint32_t createInfoCount, + const VkRayTracingPipelineCreateInfoNV* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetRayTracingShaderGroupHandlesKHR( + VkDevice device, + VkPipeline pipeline, + uint32_t firstGroup, + uint32_t groupCount, + size_t dataSize, + void* pData); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetRayTracingShaderGroupHandlesNV( + VkDevice device, + VkPipeline pipeline, + uint32_t firstGroup, + uint32_t groupCount, + size_t dataSize, + void* pData); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetAccelerationStructureHandleNV( + VkDevice device, + VkAccelerationStructureNV accelerationStructure, + size_t dataSize, + void* pData); + +VKAPI_ATTR void VKAPI_CALL vkCmdWriteAccelerationStructuresPropertiesNV( + VkCommandBuffer commandBuffer, + uint32_t accelerationStructureCount, + const VkAccelerationStructureNV* pAccelerationStructures, + VkQueryType queryType, + VkQueryPool queryPool, + uint32_t firstQuery); + +VKAPI_ATTR VkResult VKAPI_CALL vkCompileDeferredNV( + VkDevice device, + VkPipeline pipeline, + uint32_t shader); +#endif + + +#define VK_NV_representative_fragment_test 1 +#define VK_NV_REPRESENTATIVE_FRAGMENT_TEST_SPEC_VERSION 2 +#define VK_NV_REPRESENTATIVE_FRAGMENT_TEST_EXTENSION_NAME "VK_NV_representative_fragment_test" +typedef struct VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 representativeFragmentTest; +} VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV; + +typedef struct VkPipelineRepresentativeFragmentTestStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 representativeFragmentTestEnable; +} VkPipelineRepresentativeFragmentTestStateCreateInfoNV; + + + +#define VK_EXT_filter_cubic 1 +#define VK_EXT_FILTER_CUBIC_SPEC_VERSION 3 +#define VK_EXT_FILTER_CUBIC_EXTENSION_NAME "VK_EXT_filter_cubic" +typedef struct VkPhysicalDeviceImageViewImageFormatInfoEXT { + VkStructureType sType; + void* pNext; + VkImageViewType imageViewType; +} VkPhysicalDeviceImageViewImageFormatInfoEXT; + +typedef struct VkFilterCubicImageViewImageFormatPropertiesEXT { + VkStructureType sType; + void* pNext; + VkBool32 filterCubic; + VkBool32 filterCubicMinmax; +} VkFilterCubicImageViewImageFormatPropertiesEXT; + + + +#define VK_QCOM_render_pass_shader_resolve 1 +#define VK_QCOM_RENDER_PASS_SHADER_RESOLVE_SPEC_VERSION 4 +#define VK_QCOM_RENDER_PASS_SHADER_RESOLVE_EXTENSION_NAME "VK_QCOM_render_pass_shader_resolve" + + #define VK_EXT_global_priority 1 #define VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION 2 #define VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME "VK_EXT_global_priority" - typedef enum VkQueueGlobalPriorityEXT { VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT = 128, VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT = 256, VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT = 512, VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT = 1024, - VK_QUEUE_GLOBAL_PRIORITY_BEGIN_RANGE_EXT = VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT, - VK_QUEUE_GLOBAL_PRIORITY_END_RANGE_EXT = VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT, - VK_QUEUE_GLOBAL_PRIORITY_RANGE_SIZE_EXT = (VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT - VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT + 1), VK_QUEUE_GLOBAL_PRIORITY_MAX_ENUM_EXT = 0x7FFFFFFF } VkQueueGlobalPriorityEXT; - typedef struct VkDeviceQueueGlobalPriorityCreateInfoEXT { VkStructureType sType; const void* pNext; @@ -7363,7 +10848,6 @@ typedef struct VkDeviceQueueGlobalPriorityCreateInfoEXT { #define VK_EXT_external_memory_host 1 #define VK_EXT_EXTERNAL_MEMORY_HOST_SPEC_VERSION 1 #define VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME "VK_EXT_external_memory_host" - typedef struct VkImportMemoryHostPointerInfoEXT { VkStructureType sType; const void* pNext; @@ -7383,7 +10867,6 @@ typedef struct VkPhysicalDeviceExternalMemoryHostPropertiesEXT { VkDeviceSize minImportedHostPointerAlignment; } VkPhysicalDeviceExternalMemoryHostPropertiesEXT; - typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryHostPointerPropertiesEXT)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, const void* pHostPointer, VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties); #ifndef VK_NO_PROTOTYPES @@ -7394,10 +10877,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryHostPointerPropertiesEXT( VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties); #endif + #define VK_AMD_buffer_marker 1 #define VK_AMD_BUFFER_MARKER_SPEC_VERSION 1 #define VK_AMD_BUFFER_MARKER_EXTENSION_NAME "VK_AMD_buffer_marker" - typedef void (VKAPI_PTR *PFN_vkCmdWriteBufferMarkerAMD)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker); #ifndef VK_NO_PROTOTYPES @@ -7409,10 +10892,61 @@ VKAPI_ATTR void VKAPI_CALL vkCmdWriteBufferMarkerAMD( uint32_t marker); #endif -#define VK_AMD_shader_core_properties 1 -#define VK_AMD_SHADER_CORE_PROPERTIES_SPEC_VERSION 1 -#define VK_AMD_SHADER_CORE_PROPERTIES_EXTENSION_NAME "VK_AMD_shader_core_properties" +#define VK_AMD_pipeline_compiler_control 1 +#define VK_AMD_PIPELINE_COMPILER_CONTROL_SPEC_VERSION 1 +#define VK_AMD_PIPELINE_COMPILER_CONTROL_EXTENSION_NAME "VK_AMD_pipeline_compiler_control" + +typedef enum VkPipelineCompilerControlFlagBitsAMD { + VK_PIPELINE_COMPILER_CONTROL_FLAG_BITS_MAX_ENUM_AMD = 0x7FFFFFFF +} VkPipelineCompilerControlFlagBitsAMD; +typedef VkFlags VkPipelineCompilerControlFlagsAMD; +typedef struct VkPipelineCompilerControlCreateInfoAMD { + VkStructureType sType; + const void* pNext; + VkPipelineCompilerControlFlagsAMD compilerControlFlags; +} VkPipelineCompilerControlCreateInfoAMD; + + + +#define VK_EXT_calibrated_timestamps 1 +#define VK_EXT_CALIBRATED_TIMESTAMPS_SPEC_VERSION 2 +#define VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME "VK_EXT_calibrated_timestamps" + +typedef enum VkTimeDomainEXT { + VK_TIME_DOMAIN_DEVICE_EXT = 0, + VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT = 1, + VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT = 2, + VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT = 3, + VK_TIME_DOMAIN_MAX_ENUM_EXT = 0x7FFFFFFF +} VkTimeDomainEXT; +typedef struct VkCalibratedTimestampInfoEXT { + VkStructureType sType; + const void* pNext; + VkTimeDomainEXT timeDomain; +} VkCalibratedTimestampInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT)(VkPhysicalDevice physicalDevice, uint32_t* pTimeDomainCount, VkTimeDomainEXT* pTimeDomains); +typedef VkResult (VKAPI_PTR *PFN_vkGetCalibratedTimestampsEXT)(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT* pTimestampInfos, uint64_t* pTimestamps, uint64_t* pMaxDeviation); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceCalibrateableTimeDomainsEXT( + VkPhysicalDevice physicalDevice, + uint32_t* pTimeDomainCount, + VkTimeDomainEXT* pTimeDomains); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetCalibratedTimestampsEXT( + VkDevice device, + uint32_t timestampCount, + const VkCalibratedTimestampInfoEXT* pTimestampInfos, + uint64_t* pTimestamps, + uint64_t* pMaxDeviation); +#endif + + +#define VK_AMD_shader_core_properties 1 +#define VK_AMD_SHADER_CORE_PROPERTIES_SPEC_VERSION 2 +#define VK_AMD_SHADER_CORE_PROPERTIES_EXTENSION_NAME "VK_AMD_shader_core_properties" typedef struct VkPhysicalDeviceShaderCorePropertiesAMD { VkStructureType sType; void* pNext; @@ -7434,10 +10968,27 @@ typedef struct VkPhysicalDeviceShaderCorePropertiesAMD { -#define VK_EXT_vertex_attribute_divisor 1 -#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 1 -#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor" +#define VK_AMD_memory_overallocation_behavior 1 +#define VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_SPEC_VERSION 1 +#define VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_EXTENSION_NAME "VK_AMD_memory_overallocation_behavior" +typedef enum VkMemoryOverallocationBehaviorAMD { + VK_MEMORY_OVERALLOCATION_BEHAVIOR_DEFAULT_AMD = 0, + VK_MEMORY_OVERALLOCATION_BEHAVIOR_ALLOWED_AMD = 1, + VK_MEMORY_OVERALLOCATION_BEHAVIOR_DISALLOWED_AMD = 2, + VK_MEMORY_OVERALLOCATION_BEHAVIOR_MAX_ENUM_AMD = 0x7FFFFFFF +} VkMemoryOverallocationBehaviorAMD; +typedef struct VkDeviceMemoryOverallocationCreateInfoAMD { + VkStructureType sType; + const void* pNext; + VkMemoryOverallocationBehaviorAMD overallocationBehavior; +} VkDeviceMemoryOverallocationCreateInfoAMD; + + + +#define VK_EXT_vertex_attribute_divisor 1 +#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 3 +#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor" typedef struct VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT { VkStructureType sType; void* pNext; @@ -7456,6 +11007,39 @@ typedef struct VkPipelineVertexInputDivisorStateCreateInfoEXT { const VkVertexInputBindingDivisorDescriptionEXT* pVertexBindingDivisors; } VkPipelineVertexInputDivisorStateCreateInfoEXT; +typedef struct VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 vertexAttributeInstanceRateDivisor; + VkBool32 vertexAttributeInstanceRateZeroDivisor; +} VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT; + + + +#define VK_EXT_pipeline_creation_feedback 1 +#define VK_EXT_PIPELINE_CREATION_FEEDBACK_SPEC_VERSION 1 +#define VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME "VK_EXT_pipeline_creation_feedback" + +typedef enum VkPipelineCreationFeedbackFlagBitsEXT { + VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT = 0x00000001, + VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT = 0x00000002, + VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT = 0x00000004, + VK_PIPELINE_CREATION_FEEDBACK_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkPipelineCreationFeedbackFlagBitsEXT; +typedef VkFlags VkPipelineCreationFeedbackFlagsEXT; +typedef struct VkPipelineCreationFeedbackEXT { + VkPipelineCreationFeedbackFlagsEXT flags; + uint64_t duration; +} VkPipelineCreationFeedbackEXT; + +typedef struct VkPipelineCreationFeedbackCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPipelineCreationFeedbackEXT* pPipelineCreationFeedback; + uint32_t pipelineStageCreationFeedbackCount; + VkPipelineCreationFeedbackEXT* pPipelineStageCreationFeedbacks; +} VkPipelineCreationFeedbackCreateInfoEXT; + #define VK_NV_shader_subgroup_partitioned 1 @@ -7463,6 +11047,2492 @@ typedef struct VkPipelineVertexInputDivisorStateCreateInfoEXT { #define VK_NV_SHADER_SUBGROUP_PARTITIONED_EXTENSION_NAME "VK_NV_shader_subgroup_partitioned" +#define VK_NV_compute_shader_derivatives 1 +#define VK_NV_COMPUTE_SHADER_DERIVATIVES_SPEC_VERSION 1 +#define VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME "VK_NV_compute_shader_derivatives" +typedef struct VkPhysicalDeviceComputeShaderDerivativesFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 computeDerivativeGroupQuads; + VkBool32 computeDerivativeGroupLinear; +} VkPhysicalDeviceComputeShaderDerivativesFeaturesNV; + + + +#define VK_NV_mesh_shader 1 +#define VK_NV_MESH_SHADER_SPEC_VERSION 1 +#define VK_NV_MESH_SHADER_EXTENSION_NAME "VK_NV_mesh_shader" +typedef struct VkPhysicalDeviceMeshShaderFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 taskShader; + VkBool32 meshShader; +} VkPhysicalDeviceMeshShaderFeaturesNV; + +typedef struct VkPhysicalDeviceMeshShaderPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t maxDrawMeshTasksCount; + uint32_t maxTaskWorkGroupInvocations; + uint32_t maxTaskWorkGroupSize[3]; + uint32_t maxTaskTotalMemorySize; + uint32_t maxTaskOutputCount; + uint32_t maxMeshWorkGroupInvocations; + uint32_t maxMeshWorkGroupSize[3]; + uint32_t maxMeshTotalMemorySize; + uint32_t maxMeshOutputVertices; + uint32_t maxMeshOutputPrimitives; + uint32_t maxMeshMultiviewViewCount; + uint32_t meshOutputPerVertexGranularity; + uint32_t meshOutputPerPrimitiveGranularity; +} VkPhysicalDeviceMeshShaderPropertiesNV; + +typedef struct VkDrawMeshTasksIndirectCommandNV { + uint32_t taskCount; + uint32_t firstTask; +} VkDrawMeshTasksIndirectCommandNV; + +typedef void (VKAPI_PTR *PFN_vkCmdDrawMeshTasksNV)(VkCommandBuffer commandBuffer, uint32_t taskCount, uint32_t firstTask); +typedef void (VKAPI_PTR *PFN_vkCmdDrawMeshTasksIndirectNV)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawMeshTasksIndirectCountNV)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawMeshTasksNV( + VkCommandBuffer commandBuffer, + uint32_t taskCount, + uint32_t firstTask); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawMeshTasksIndirectNV( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t drawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawMeshTasksIndirectCountNV( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); +#endif + + +#define VK_NV_fragment_shader_barycentric 1 +#define VK_NV_FRAGMENT_SHADER_BARYCENTRIC_SPEC_VERSION 1 +#define VK_NV_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME "VK_NV_fragment_shader_barycentric" +typedef struct VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 fragmentShaderBarycentric; +} VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV; + + + +#define VK_NV_shader_image_footprint 1 +#define VK_NV_SHADER_IMAGE_FOOTPRINT_SPEC_VERSION 2 +#define VK_NV_SHADER_IMAGE_FOOTPRINT_EXTENSION_NAME "VK_NV_shader_image_footprint" +typedef struct VkPhysicalDeviceShaderImageFootprintFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 imageFootprint; +} VkPhysicalDeviceShaderImageFootprintFeaturesNV; + + + +#define VK_NV_scissor_exclusive 1 +#define VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION 1 +#define VK_NV_SCISSOR_EXCLUSIVE_EXTENSION_NAME "VK_NV_scissor_exclusive" +typedef struct VkPipelineViewportExclusiveScissorStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + uint32_t exclusiveScissorCount; + const VkRect2D* pExclusiveScissors; +} VkPipelineViewportExclusiveScissorStateCreateInfoNV; + +typedef struct VkPhysicalDeviceExclusiveScissorFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 exclusiveScissor; +} VkPhysicalDeviceExclusiveScissorFeaturesNV; + +typedef void (VKAPI_PTR *PFN_vkCmdSetExclusiveScissorNV)(VkCommandBuffer commandBuffer, uint32_t firstExclusiveScissor, uint32_t exclusiveScissorCount, const VkRect2D* pExclusiveScissors); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetExclusiveScissorNV( + VkCommandBuffer commandBuffer, + uint32_t firstExclusiveScissor, + uint32_t exclusiveScissorCount, + const VkRect2D* pExclusiveScissors); +#endif + + +#define VK_NV_device_diagnostic_checkpoints 1 +#define VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_SPEC_VERSION 2 +#define VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME "VK_NV_device_diagnostic_checkpoints" +typedef struct VkQueueFamilyCheckpointPropertiesNV { + VkStructureType sType; + void* pNext; + VkPipelineStageFlags checkpointExecutionStageMask; +} VkQueueFamilyCheckpointPropertiesNV; + +typedef struct VkCheckpointDataNV { + VkStructureType sType; + void* pNext; + VkPipelineStageFlagBits stage; + void* pCheckpointMarker; +} VkCheckpointDataNV; + +typedef void (VKAPI_PTR *PFN_vkCmdSetCheckpointNV)(VkCommandBuffer commandBuffer, const void* pCheckpointMarker); +typedef void (VKAPI_PTR *PFN_vkGetQueueCheckpointDataNV)(VkQueue queue, uint32_t* pCheckpointDataCount, VkCheckpointDataNV* pCheckpointData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetCheckpointNV( + VkCommandBuffer commandBuffer, + const void* pCheckpointMarker); + +VKAPI_ATTR void VKAPI_CALL vkGetQueueCheckpointDataNV( + VkQueue queue, + uint32_t* pCheckpointDataCount, + VkCheckpointDataNV* pCheckpointData); +#endif + + +#define VK_INTEL_shader_integer_functions2 1 +#define VK_INTEL_SHADER_INTEGER_FUNCTIONS_2_SPEC_VERSION 1 +#define VK_INTEL_SHADER_INTEGER_FUNCTIONS_2_EXTENSION_NAME "VK_INTEL_shader_integer_functions2" +typedef struct VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL { + VkStructureType sType; + void* pNext; + VkBool32 shaderIntegerFunctions2; +} VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL; + + + +#define VK_INTEL_performance_query 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPerformanceConfigurationINTEL) +#define VK_INTEL_PERFORMANCE_QUERY_SPEC_VERSION 2 +#define VK_INTEL_PERFORMANCE_QUERY_EXTENSION_NAME "VK_INTEL_performance_query" + +typedef enum VkPerformanceConfigurationTypeINTEL { + VK_PERFORMANCE_CONFIGURATION_TYPE_COMMAND_QUEUE_METRICS_DISCOVERY_ACTIVATED_INTEL = 0, + VK_PERFORMANCE_CONFIGURATION_TYPE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkPerformanceConfigurationTypeINTEL; + +typedef enum VkQueryPoolSamplingModeINTEL { + VK_QUERY_POOL_SAMPLING_MODE_MANUAL_INTEL = 0, + VK_QUERY_POOL_SAMPLING_MODE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkQueryPoolSamplingModeINTEL; + +typedef enum VkPerformanceOverrideTypeINTEL { + VK_PERFORMANCE_OVERRIDE_TYPE_NULL_HARDWARE_INTEL = 0, + VK_PERFORMANCE_OVERRIDE_TYPE_FLUSH_GPU_CACHES_INTEL = 1, + VK_PERFORMANCE_OVERRIDE_TYPE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkPerformanceOverrideTypeINTEL; + +typedef enum VkPerformanceParameterTypeINTEL { + VK_PERFORMANCE_PARAMETER_TYPE_HW_COUNTERS_SUPPORTED_INTEL = 0, + VK_PERFORMANCE_PARAMETER_TYPE_STREAM_MARKER_VALID_BITS_INTEL = 1, + VK_PERFORMANCE_PARAMETER_TYPE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkPerformanceParameterTypeINTEL; + +typedef enum VkPerformanceValueTypeINTEL { + VK_PERFORMANCE_VALUE_TYPE_UINT32_INTEL = 0, + VK_PERFORMANCE_VALUE_TYPE_UINT64_INTEL = 1, + VK_PERFORMANCE_VALUE_TYPE_FLOAT_INTEL = 2, + VK_PERFORMANCE_VALUE_TYPE_BOOL_INTEL = 3, + VK_PERFORMANCE_VALUE_TYPE_STRING_INTEL = 4, + VK_PERFORMANCE_VALUE_TYPE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkPerformanceValueTypeINTEL; +typedef union VkPerformanceValueDataINTEL { + uint32_t value32; + uint64_t value64; + float valueFloat; + VkBool32 valueBool; + const char* valueString; +} VkPerformanceValueDataINTEL; + +typedef struct VkPerformanceValueINTEL { + VkPerformanceValueTypeINTEL type; + VkPerformanceValueDataINTEL data; +} VkPerformanceValueINTEL; + +typedef struct VkInitializePerformanceApiInfoINTEL { + VkStructureType sType; + const void* pNext; + void* pUserData; +} VkInitializePerformanceApiInfoINTEL; + +typedef struct VkQueryPoolPerformanceQueryCreateInfoINTEL { + VkStructureType sType; + const void* pNext; + VkQueryPoolSamplingModeINTEL performanceCountersSampling; +} VkQueryPoolPerformanceQueryCreateInfoINTEL; + +typedef VkQueryPoolPerformanceQueryCreateInfoINTEL VkQueryPoolCreateInfoINTEL; + +typedef struct VkPerformanceMarkerInfoINTEL { + VkStructureType sType; + const void* pNext; + uint64_t marker; +} VkPerformanceMarkerInfoINTEL; + +typedef struct VkPerformanceStreamMarkerInfoINTEL { + VkStructureType sType; + const void* pNext; + uint32_t marker; +} VkPerformanceStreamMarkerInfoINTEL; + +typedef struct VkPerformanceOverrideInfoINTEL { + VkStructureType sType; + const void* pNext; + VkPerformanceOverrideTypeINTEL type; + VkBool32 enable; + uint64_t parameter; +} VkPerformanceOverrideInfoINTEL; + +typedef struct VkPerformanceConfigurationAcquireInfoINTEL { + VkStructureType sType; + const void* pNext; + VkPerformanceConfigurationTypeINTEL type; +} VkPerformanceConfigurationAcquireInfoINTEL; + +typedef VkResult (VKAPI_PTR *PFN_vkInitializePerformanceApiINTEL)(VkDevice device, const VkInitializePerformanceApiInfoINTEL* pInitializeInfo); +typedef void (VKAPI_PTR *PFN_vkUninitializePerformanceApiINTEL)(VkDevice device); +typedef VkResult (VKAPI_PTR *PFN_vkCmdSetPerformanceMarkerINTEL)(VkCommandBuffer commandBuffer, const VkPerformanceMarkerInfoINTEL* pMarkerInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCmdSetPerformanceStreamMarkerINTEL)(VkCommandBuffer commandBuffer, const VkPerformanceStreamMarkerInfoINTEL* pMarkerInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCmdSetPerformanceOverrideINTEL)(VkCommandBuffer commandBuffer, const VkPerformanceOverrideInfoINTEL* pOverrideInfo); +typedef VkResult (VKAPI_PTR *PFN_vkAcquirePerformanceConfigurationINTEL)(VkDevice device, const VkPerformanceConfigurationAcquireInfoINTEL* pAcquireInfo, VkPerformanceConfigurationINTEL* pConfiguration); +typedef VkResult (VKAPI_PTR *PFN_vkReleasePerformanceConfigurationINTEL)(VkDevice device, VkPerformanceConfigurationINTEL configuration); +typedef VkResult (VKAPI_PTR *PFN_vkQueueSetPerformanceConfigurationINTEL)(VkQueue queue, VkPerformanceConfigurationINTEL configuration); +typedef VkResult (VKAPI_PTR *PFN_vkGetPerformanceParameterINTEL)(VkDevice device, VkPerformanceParameterTypeINTEL parameter, VkPerformanceValueINTEL* pValue); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkInitializePerformanceApiINTEL( + VkDevice device, + const VkInitializePerformanceApiInfoINTEL* pInitializeInfo); + +VKAPI_ATTR void VKAPI_CALL vkUninitializePerformanceApiINTEL( + VkDevice device); + +VKAPI_ATTR VkResult VKAPI_CALL vkCmdSetPerformanceMarkerINTEL( + VkCommandBuffer commandBuffer, + const VkPerformanceMarkerInfoINTEL* pMarkerInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCmdSetPerformanceStreamMarkerINTEL( + VkCommandBuffer commandBuffer, + const VkPerformanceStreamMarkerInfoINTEL* pMarkerInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCmdSetPerformanceOverrideINTEL( + VkCommandBuffer commandBuffer, + const VkPerformanceOverrideInfoINTEL* pOverrideInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquirePerformanceConfigurationINTEL( + VkDevice device, + const VkPerformanceConfigurationAcquireInfoINTEL* pAcquireInfo, + VkPerformanceConfigurationINTEL* pConfiguration); + +VKAPI_ATTR VkResult VKAPI_CALL vkReleasePerformanceConfigurationINTEL( + VkDevice device, + VkPerformanceConfigurationINTEL configuration); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueSetPerformanceConfigurationINTEL( + VkQueue queue, + VkPerformanceConfigurationINTEL configuration); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPerformanceParameterINTEL( + VkDevice device, + VkPerformanceParameterTypeINTEL parameter, + VkPerformanceValueINTEL* pValue); +#endif + + +#define VK_EXT_pci_bus_info 1 +#define VK_EXT_PCI_BUS_INFO_SPEC_VERSION 2 +#define VK_EXT_PCI_BUS_INFO_EXTENSION_NAME "VK_EXT_pci_bus_info" +typedef struct VkPhysicalDevicePCIBusInfoPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t pciDomain; + uint32_t pciBus; + uint32_t pciDevice; + uint32_t pciFunction; +} VkPhysicalDevicePCIBusInfoPropertiesEXT; + + + +#define VK_AMD_display_native_hdr 1 +#define VK_AMD_DISPLAY_NATIVE_HDR_SPEC_VERSION 1 +#define VK_AMD_DISPLAY_NATIVE_HDR_EXTENSION_NAME "VK_AMD_display_native_hdr" +typedef struct VkDisplayNativeHdrSurfaceCapabilitiesAMD { + VkStructureType sType; + void* pNext; + VkBool32 localDimmingSupport; +} VkDisplayNativeHdrSurfaceCapabilitiesAMD; + +typedef struct VkSwapchainDisplayNativeHdrCreateInfoAMD { + VkStructureType sType; + const void* pNext; + VkBool32 localDimmingEnable; +} VkSwapchainDisplayNativeHdrCreateInfoAMD; + +typedef void (VKAPI_PTR *PFN_vkSetLocalDimmingAMD)(VkDevice device, VkSwapchainKHR swapChain, VkBool32 localDimmingEnable); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkSetLocalDimmingAMD( + VkDevice device, + VkSwapchainKHR swapChain, + VkBool32 localDimmingEnable); +#endif + + +#define VK_EXT_fragment_density_map 1 +#define VK_EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION 2 +#define VK_EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME "VK_EXT_fragment_density_map" +typedef struct VkPhysicalDeviceFragmentDensityMapFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 fragmentDensityMap; + VkBool32 fragmentDensityMapDynamic; + VkBool32 fragmentDensityMapNonSubsampledImages; +} VkPhysicalDeviceFragmentDensityMapFeaturesEXT; + +typedef struct VkPhysicalDeviceFragmentDensityMapPropertiesEXT { + VkStructureType sType; + void* pNext; + VkExtent2D minFragmentDensityTexelSize; + VkExtent2D maxFragmentDensityTexelSize; + VkBool32 fragmentDensityInvocations; +} VkPhysicalDeviceFragmentDensityMapPropertiesEXT; + +typedef struct VkRenderPassFragmentDensityMapCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkAttachmentReference fragmentDensityMapAttachment; +} VkRenderPassFragmentDensityMapCreateInfoEXT; + + + +#define VK_EXT_scalar_block_layout 1 +#define VK_EXT_SCALAR_BLOCK_LAYOUT_SPEC_VERSION 1 +#define VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME "VK_EXT_scalar_block_layout" +typedef VkPhysicalDeviceScalarBlockLayoutFeatures VkPhysicalDeviceScalarBlockLayoutFeaturesEXT; + + + +#define VK_GOOGLE_hlsl_functionality1 1 +#define VK_GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION 1 +#define VK_GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME "VK_GOOGLE_hlsl_functionality1" +#define VK_GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION VK_GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION +#define VK_GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME VK_GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME + + +#define VK_GOOGLE_decorate_string 1 +#define VK_GOOGLE_DECORATE_STRING_SPEC_VERSION 1 +#define VK_GOOGLE_DECORATE_STRING_EXTENSION_NAME "VK_GOOGLE_decorate_string" + + +#define VK_EXT_subgroup_size_control 1 +#define VK_EXT_SUBGROUP_SIZE_CONTROL_SPEC_VERSION 2 +#define VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME "VK_EXT_subgroup_size_control" +typedef struct VkPhysicalDeviceSubgroupSizeControlFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 subgroupSizeControl; + VkBool32 computeFullSubgroups; +} VkPhysicalDeviceSubgroupSizeControlFeaturesEXT; + +typedef struct VkPhysicalDeviceSubgroupSizeControlPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t minSubgroupSize; + uint32_t maxSubgroupSize; + uint32_t maxComputeWorkgroupSubgroups; + VkShaderStageFlags requiredSubgroupSizeStages; +} VkPhysicalDeviceSubgroupSizeControlPropertiesEXT; + +typedef struct VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT { + VkStructureType sType; + void* pNext; + uint32_t requiredSubgroupSize; +} VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT; + + + +#define VK_AMD_shader_core_properties2 1 +#define VK_AMD_SHADER_CORE_PROPERTIES_2_SPEC_VERSION 1 +#define VK_AMD_SHADER_CORE_PROPERTIES_2_EXTENSION_NAME "VK_AMD_shader_core_properties2" + +typedef enum VkShaderCorePropertiesFlagBitsAMD { + VK_SHADER_CORE_PROPERTIES_FLAG_BITS_MAX_ENUM_AMD = 0x7FFFFFFF +} VkShaderCorePropertiesFlagBitsAMD; +typedef VkFlags VkShaderCorePropertiesFlagsAMD; +typedef struct VkPhysicalDeviceShaderCoreProperties2AMD { + VkStructureType sType; + void* pNext; + VkShaderCorePropertiesFlagsAMD shaderCoreFeatures; + uint32_t activeComputeUnitCount; +} VkPhysicalDeviceShaderCoreProperties2AMD; + + + +#define VK_AMD_device_coherent_memory 1 +#define VK_AMD_DEVICE_COHERENT_MEMORY_SPEC_VERSION 1 +#define VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME "VK_AMD_device_coherent_memory" +typedef struct VkPhysicalDeviceCoherentMemoryFeaturesAMD { + VkStructureType sType; + void* pNext; + VkBool32 deviceCoherentMemory; +} VkPhysicalDeviceCoherentMemoryFeaturesAMD; + + + +#define VK_EXT_shader_image_atomic_int64 1 +#define VK_EXT_SHADER_IMAGE_ATOMIC_INT64_SPEC_VERSION 1 +#define VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME "VK_EXT_shader_image_atomic_int64" +typedef struct VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 shaderImageInt64Atomics; + VkBool32 sparseImageInt64Atomics; +} VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT; + + + +#define VK_EXT_memory_budget 1 +#define VK_EXT_MEMORY_BUDGET_SPEC_VERSION 1 +#define VK_EXT_MEMORY_BUDGET_EXTENSION_NAME "VK_EXT_memory_budget" +typedef struct VkPhysicalDeviceMemoryBudgetPropertiesEXT { + VkStructureType sType; + void* pNext; + VkDeviceSize heapBudget[VK_MAX_MEMORY_HEAPS]; + VkDeviceSize heapUsage[VK_MAX_MEMORY_HEAPS]; +} VkPhysicalDeviceMemoryBudgetPropertiesEXT; + + + +#define VK_EXT_memory_priority 1 +#define VK_EXT_MEMORY_PRIORITY_SPEC_VERSION 1 +#define VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME "VK_EXT_memory_priority" +typedef struct VkPhysicalDeviceMemoryPriorityFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 memoryPriority; +} VkPhysicalDeviceMemoryPriorityFeaturesEXT; + +typedef struct VkMemoryPriorityAllocateInfoEXT { + VkStructureType sType; + const void* pNext; + float priority; +} VkMemoryPriorityAllocateInfoEXT; + + + +#define VK_NV_dedicated_allocation_image_aliasing 1 +#define VK_NV_DEDICATED_ALLOCATION_IMAGE_ALIASING_SPEC_VERSION 1 +#define VK_NV_DEDICATED_ALLOCATION_IMAGE_ALIASING_EXTENSION_NAME "VK_NV_dedicated_allocation_image_aliasing" +typedef struct VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 dedicatedAllocationImageAliasing; +} VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV; + + + +#define VK_EXT_buffer_device_address 1 +#define VK_EXT_BUFFER_DEVICE_ADDRESS_SPEC_VERSION 2 +#define VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME "VK_EXT_buffer_device_address" +typedef struct VkPhysicalDeviceBufferDeviceAddressFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 bufferDeviceAddress; + VkBool32 bufferDeviceAddressCaptureReplay; + VkBool32 bufferDeviceAddressMultiDevice; +} VkPhysicalDeviceBufferDeviceAddressFeaturesEXT; + +typedef VkPhysicalDeviceBufferDeviceAddressFeaturesEXT VkPhysicalDeviceBufferAddressFeaturesEXT; + +typedef VkBufferDeviceAddressInfo VkBufferDeviceAddressInfoEXT; + +typedef struct VkBufferDeviceAddressCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDeviceAddress deviceAddress; +} VkBufferDeviceAddressCreateInfoEXT; + +typedef VkDeviceAddress (VKAPI_PTR *PFN_vkGetBufferDeviceAddressEXT)(VkDevice device, const VkBufferDeviceAddressInfo* pInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkDeviceAddress VKAPI_CALL vkGetBufferDeviceAddressEXT( + VkDevice device, + const VkBufferDeviceAddressInfo* pInfo); +#endif + + +#define VK_EXT_tooling_info 1 +#define VK_EXT_TOOLING_INFO_SPEC_VERSION 1 +#define VK_EXT_TOOLING_INFO_EXTENSION_NAME "VK_EXT_tooling_info" + +typedef enum VkToolPurposeFlagBitsEXT { + VK_TOOL_PURPOSE_VALIDATION_BIT_EXT = 0x00000001, + VK_TOOL_PURPOSE_PROFILING_BIT_EXT = 0x00000002, + VK_TOOL_PURPOSE_TRACING_BIT_EXT = 0x00000004, + VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT_EXT = 0x00000008, + VK_TOOL_PURPOSE_MODIFYING_FEATURES_BIT_EXT = 0x00000010, + VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT = 0x00000020, + VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT = 0x00000040, + VK_TOOL_PURPOSE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkToolPurposeFlagBitsEXT; +typedef VkFlags VkToolPurposeFlagsEXT; +typedef struct VkPhysicalDeviceToolPropertiesEXT { + VkStructureType sType; + void* pNext; + char name[VK_MAX_EXTENSION_NAME_SIZE]; + char version[VK_MAX_EXTENSION_NAME_SIZE]; + VkToolPurposeFlagsEXT purposes; + char description[VK_MAX_DESCRIPTION_SIZE]; + char layer[VK_MAX_EXTENSION_NAME_SIZE]; +} VkPhysicalDeviceToolPropertiesEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceToolPropertiesEXT)(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolPropertiesEXT* pToolProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceToolPropertiesEXT( + VkPhysicalDevice physicalDevice, + uint32_t* pToolCount, + VkPhysicalDeviceToolPropertiesEXT* pToolProperties); +#endif + + +#define VK_EXT_separate_stencil_usage 1 +#define VK_EXT_SEPARATE_STENCIL_USAGE_SPEC_VERSION 1 +#define VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME "VK_EXT_separate_stencil_usage" +typedef VkImageStencilUsageCreateInfo VkImageStencilUsageCreateInfoEXT; + + + +#define VK_EXT_validation_features 1 +#define VK_EXT_VALIDATION_FEATURES_SPEC_VERSION 5 +#define VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME "VK_EXT_validation_features" + +typedef enum VkValidationFeatureEnableEXT { + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT = 0, + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT = 1, + VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT = 2, + VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT = 3, + VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT = 4, + VK_VALIDATION_FEATURE_ENABLE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationFeatureEnableEXT; + +typedef enum VkValidationFeatureDisableEXT { + VK_VALIDATION_FEATURE_DISABLE_ALL_EXT = 0, + VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT = 1, + VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT = 2, + VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT = 3, + VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT = 4, + VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT = 5, + VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT = 6, + VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT = 7, + VK_VALIDATION_FEATURE_DISABLE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationFeatureDisableEXT; +typedef struct VkValidationFeaturesEXT { + VkStructureType sType; + const void* pNext; + uint32_t enabledValidationFeatureCount; + const VkValidationFeatureEnableEXT* pEnabledValidationFeatures; + uint32_t disabledValidationFeatureCount; + const VkValidationFeatureDisableEXT* pDisabledValidationFeatures; +} VkValidationFeaturesEXT; + + + +#define VK_NV_cooperative_matrix 1 +#define VK_NV_COOPERATIVE_MATRIX_SPEC_VERSION 1 +#define VK_NV_COOPERATIVE_MATRIX_EXTENSION_NAME "VK_NV_cooperative_matrix" + +typedef enum VkComponentTypeNV { + VK_COMPONENT_TYPE_FLOAT16_NV = 0, + VK_COMPONENT_TYPE_FLOAT32_NV = 1, + VK_COMPONENT_TYPE_FLOAT64_NV = 2, + VK_COMPONENT_TYPE_SINT8_NV = 3, + VK_COMPONENT_TYPE_SINT16_NV = 4, + VK_COMPONENT_TYPE_SINT32_NV = 5, + VK_COMPONENT_TYPE_SINT64_NV = 6, + VK_COMPONENT_TYPE_UINT8_NV = 7, + VK_COMPONENT_TYPE_UINT16_NV = 8, + VK_COMPONENT_TYPE_UINT32_NV = 9, + VK_COMPONENT_TYPE_UINT64_NV = 10, + VK_COMPONENT_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkComponentTypeNV; + +typedef enum VkScopeNV { + VK_SCOPE_DEVICE_NV = 1, + VK_SCOPE_WORKGROUP_NV = 2, + VK_SCOPE_SUBGROUP_NV = 3, + VK_SCOPE_QUEUE_FAMILY_NV = 5, + VK_SCOPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkScopeNV; +typedef struct VkCooperativeMatrixPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t MSize; + uint32_t NSize; + uint32_t KSize; + VkComponentTypeNV AType; + VkComponentTypeNV BType; + VkComponentTypeNV CType; + VkComponentTypeNV DType; + VkScopeNV scope; +} VkCooperativeMatrixPropertiesNV; + +typedef struct VkPhysicalDeviceCooperativeMatrixFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 cooperativeMatrix; + VkBool32 cooperativeMatrixRobustBufferAccess; +} VkPhysicalDeviceCooperativeMatrixFeaturesNV; + +typedef struct VkPhysicalDeviceCooperativeMatrixPropertiesNV { + VkStructureType sType; + void* pNext; + VkShaderStageFlags cooperativeMatrixSupportedStages; +} VkPhysicalDeviceCooperativeMatrixPropertiesNV; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkCooperativeMatrixPropertiesNV* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceCooperativeMatrixPropertiesNV( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkCooperativeMatrixPropertiesNV* pProperties); +#endif + + +#define VK_NV_coverage_reduction_mode 1 +#define VK_NV_COVERAGE_REDUCTION_MODE_SPEC_VERSION 1 +#define VK_NV_COVERAGE_REDUCTION_MODE_EXTENSION_NAME "VK_NV_coverage_reduction_mode" + +typedef enum VkCoverageReductionModeNV { + VK_COVERAGE_REDUCTION_MODE_MERGE_NV = 0, + VK_COVERAGE_REDUCTION_MODE_TRUNCATE_NV = 1, + VK_COVERAGE_REDUCTION_MODE_MAX_ENUM_NV = 0x7FFFFFFF +} VkCoverageReductionModeNV; +typedef VkFlags VkPipelineCoverageReductionStateCreateFlagsNV; +typedef struct VkPhysicalDeviceCoverageReductionModeFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 coverageReductionMode; +} VkPhysicalDeviceCoverageReductionModeFeaturesNV; + +typedef struct VkPipelineCoverageReductionStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineCoverageReductionStateCreateFlagsNV flags; + VkCoverageReductionModeNV coverageReductionMode; +} VkPipelineCoverageReductionStateCreateInfoNV; + +typedef struct VkFramebufferMixedSamplesCombinationNV { + VkStructureType sType; + void* pNext; + VkCoverageReductionModeNV coverageReductionMode; + VkSampleCountFlagBits rasterizationSamples; + VkSampleCountFlags depthStencilSamples; + VkSampleCountFlags colorSamples; +} VkFramebufferMixedSamplesCombinationNV; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV)(VkPhysicalDevice physicalDevice, uint32_t* pCombinationCount, VkFramebufferMixedSamplesCombinationNV* pCombinations); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV( + VkPhysicalDevice physicalDevice, + uint32_t* pCombinationCount, + VkFramebufferMixedSamplesCombinationNV* pCombinations); +#endif + + +#define VK_EXT_fragment_shader_interlock 1 +#define VK_EXT_FRAGMENT_SHADER_INTERLOCK_SPEC_VERSION 1 +#define VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME "VK_EXT_fragment_shader_interlock" +typedef struct VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 fragmentShaderSampleInterlock; + VkBool32 fragmentShaderPixelInterlock; + VkBool32 fragmentShaderShadingRateInterlock; +} VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT; + + + +#define VK_EXT_ycbcr_image_arrays 1 +#define VK_EXT_YCBCR_IMAGE_ARRAYS_SPEC_VERSION 1 +#define VK_EXT_YCBCR_IMAGE_ARRAYS_EXTENSION_NAME "VK_EXT_ycbcr_image_arrays" +typedef struct VkPhysicalDeviceYcbcrImageArraysFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 ycbcrImageArrays; +} VkPhysicalDeviceYcbcrImageArraysFeaturesEXT; + + + +#define VK_EXT_provoking_vertex 1 +#define VK_EXT_PROVOKING_VERTEX_SPEC_VERSION 1 +#define VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME "VK_EXT_provoking_vertex" + +typedef enum VkProvokingVertexModeEXT { + VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT = 0, + VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT = 1, + VK_PROVOKING_VERTEX_MODE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkProvokingVertexModeEXT; +typedef struct VkPhysicalDeviceProvokingVertexFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 provokingVertexLast; + VkBool32 transformFeedbackPreservesProvokingVertex; +} VkPhysicalDeviceProvokingVertexFeaturesEXT; + +typedef struct VkPhysicalDeviceProvokingVertexPropertiesEXT { + VkStructureType sType; + void* pNext; + VkBool32 provokingVertexModePerPipeline; + VkBool32 transformFeedbackPreservesTriangleFanProvokingVertex; +} VkPhysicalDeviceProvokingVertexPropertiesEXT; + +typedef struct VkPipelineRasterizationProvokingVertexStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkProvokingVertexModeEXT provokingVertexMode; +} VkPipelineRasterizationProvokingVertexStateCreateInfoEXT; + + + +#define VK_EXT_headless_surface 1 +#define VK_EXT_HEADLESS_SURFACE_SPEC_VERSION 1 +#define VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME "VK_EXT_headless_surface" +typedef VkFlags VkHeadlessSurfaceCreateFlagsEXT; +typedef struct VkHeadlessSurfaceCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkHeadlessSurfaceCreateFlagsEXT flags; +} VkHeadlessSurfaceCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateHeadlessSurfaceEXT)(VkInstance instance, const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateHeadlessSurfaceEXT( + VkInstance instance, + const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + + +#define VK_EXT_line_rasterization 1 +#define VK_EXT_LINE_RASTERIZATION_SPEC_VERSION 1 +#define VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME "VK_EXT_line_rasterization" + +typedef enum VkLineRasterizationModeEXT { + VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT = 0, + VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT = 1, + VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT = 2, + VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT = 3, + VK_LINE_RASTERIZATION_MODE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkLineRasterizationModeEXT; +typedef struct VkPhysicalDeviceLineRasterizationFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 rectangularLines; + VkBool32 bresenhamLines; + VkBool32 smoothLines; + VkBool32 stippledRectangularLines; + VkBool32 stippledBresenhamLines; + VkBool32 stippledSmoothLines; +} VkPhysicalDeviceLineRasterizationFeaturesEXT; + +typedef struct VkPhysicalDeviceLineRasterizationPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t lineSubPixelPrecisionBits; +} VkPhysicalDeviceLineRasterizationPropertiesEXT; + +typedef struct VkPipelineRasterizationLineStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkLineRasterizationModeEXT lineRasterizationMode; + VkBool32 stippledLineEnable; + uint32_t lineStippleFactor; + uint16_t lineStipplePattern; +} VkPipelineRasterizationLineStateCreateInfoEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdSetLineStippleEXT)(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetLineStippleEXT( + VkCommandBuffer commandBuffer, + uint32_t lineStippleFactor, + uint16_t lineStipplePattern); +#endif + + +#define VK_EXT_shader_atomic_float 1 +#define VK_EXT_SHADER_ATOMIC_FLOAT_SPEC_VERSION 1 +#define VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME "VK_EXT_shader_atomic_float" +typedef struct VkPhysicalDeviceShaderAtomicFloatFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 shaderBufferFloat32Atomics; + VkBool32 shaderBufferFloat32AtomicAdd; + VkBool32 shaderBufferFloat64Atomics; + VkBool32 shaderBufferFloat64AtomicAdd; + VkBool32 shaderSharedFloat32Atomics; + VkBool32 shaderSharedFloat32AtomicAdd; + VkBool32 shaderSharedFloat64Atomics; + VkBool32 shaderSharedFloat64AtomicAdd; + VkBool32 shaderImageFloat32Atomics; + VkBool32 shaderImageFloat32AtomicAdd; + VkBool32 sparseImageFloat32Atomics; + VkBool32 sparseImageFloat32AtomicAdd; +} VkPhysicalDeviceShaderAtomicFloatFeaturesEXT; + + + +#define VK_EXT_host_query_reset 1 +#define VK_EXT_HOST_QUERY_RESET_SPEC_VERSION 1 +#define VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME "VK_EXT_host_query_reset" +typedef VkPhysicalDeviceHostQueryResetFeatures VkPhysicalDeviceHostQueryResetFeaturesEXT; + +typedef void (VKAPI_PTR *PFN_vkResetQueryPoolEXT)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkResetQueryPoolEXT( + VkDevice device, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount); +#endif + + +#define VK_EXT_index_type_uint8 1 +#define VK_EXT_INDEX_TYPE_UINT8_SPEC_VERSION 1 +#define VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME "VK_EXT_index_type_uint8" +typedef struct VkPhysicalDeviceIndexTypeUint8FeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 indexTypeUint8; +} VkPhysicalDeviceIndexTypeUint8FeaturesEXT; + + + +#define VK_EXT_extended_dynamic_state 1 +#define VK_EXT_EXTENDED_DYNAMIC_STATE_SPEC_VERSION 1 +#define VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME "VK_EXT_extended_dynamic_state" +typedef struct VkPhysicalDeviceExtendedDynamicStateFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 extendedDynamicState; +} VkPhysicalDeviceExtendedDynamicStateFeaturesEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdSetCullModeEXT)(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode); +typedef void (VKAPI_PTR *PFN_vkCmdSetFrontFaceEXT)(VkCommandBuffer commandBuffer, VkFrontFace frontFace); +typedef void (VKAPI_PTR *PFN_vkCmdSetPrimitiveTopologyEXT)(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology); +typedef void (VKAPI_PTR *PFN_vkCmdSetViewportWithCountEXT)(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports); +typedef void (VKAPI_PTR *PFN_vkCmdSetScissorWithCountEXT)(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors); +typedef void (VKAPI_PTR *PFN_vkCmdBindVertexBuffers2EXT)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthTestEnableEXT)(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthWriteEnableEXT)(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthCompareOpEXT)(VkCommandBuffer commandBuffer, VkCompareOp depthCompareOp); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBoundsTestEnableEXT)(VkCommandBuffer commandBuffer, VkBool32 depthBoundsTestEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilTestEnableEXT)(VkCommandBuffer commandBuffer, VkBool32 stencilTestEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilOpEXT)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetCullModeEXT( + VkCommandBuffer commandBuffer, + VkCullModeFlags cullMode); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetFrontFaceEXT( + VkCommandBuffer commandBuffer, + VkFrontFace frontFace); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetPrimitiveTopologyEXT( + VkCommandBuffer commandBuffer, + VkPrimitiveTopology primitiveTopology); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWithCountEXT( + VkCommandBuffer commandBuffer, + uint32_t viewportCount, + const VkViewport* pViewports); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetScissorWithCountEXT( + VkCommandBuffer commandBuffer, + uint32_t scissorCount, + const VkRect2D* pScissors); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindVertexBuffers2EXT( + VkCommandBuffer commandBuffer, + uint32_t firstBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets, + const VkDeviceSize* pSizes, + const VkDeviceSize* pStrides); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthTestEnableEXT( + VkCommandBuffer commandBuffer, + VkBool32 depthTestEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthWriteEnableEXT( + VkCommandBuffer commandBuffer, + VkBool32 depthWriteEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthCompareOpEXT( + VkCommandBuffer commandBuffer, + VkCompareOp depthCompareOp); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBoundsTestEnableEXT( + VkCommandBuffer commandBuffer, + VkBool32 depthBoundsTestEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilTestEnableEXT( + VkCommandBuffer commandBuffer, + VkBool32 stencilTestEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilOpEXT( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + VkStencilOp failOp, + VkStencilOp passOp, + VkStencilOp depthFailOp, + VkCompareOp compareOp); +#endif + + +#define VK_EXT_shader_atomic_float2 1 +#define VK_EXT_SHADER_ATOMIC_FLOAT_2_SPEC_VERSION 1 +#define VK_EXT_SHADER_ATOMIC_FLOAT_2_EXTENSION_NAME "VK_EXT_shader_atomic_float2" +typedef struct VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 shaderBufferFloat16Atomics; + VkBool32 shaderBufferFloat16AtomicAdd; + VkBool32 shaderBufferFloat16AtomicMinMax; + VkBool32 shaderBufferFloat32AtomicMinMax; + VkBool32 shaderBufferFloat64AtomicMinMax; + VkBool32 shaderSharedFloat16Atomics; + VkBool32 shaderSharedFloat16AtomicAdd; + VkBool32 shaderSharedFloat16AtomicMinMax; + VkBool32 shaderSharedFloat32AtomicMinMax; + VkBool32 shaderSharedFloat64AtomicMinMax; + VkBool32 shaderImageFloat32AtomicMinMax; + VkBool32 sparseImageFloat32AtomicMinMax; +} VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT; + + + +#define VK_EXT_shader_demote_to_helper_invocation 1 +#define VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_SPEC_VERSION 1 +#define VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME "VK_EXT_shader_demote_to_helper_invocation" +typedef struct VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 shaderDemoteToHelperInvocation; +} VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT; + + + +#define VK_NV_device_generated_commands 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNV) +#define VK_NV_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3 +#define VK_NV_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NV_device_generated_commands" + +typedef enum VkIndirectCommandsTokenTypeNV { + VK_INDIRECT_COMMANDS_TOKEN_TYPE_SHADER_GROUP_NV = 0, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_STATE_FLAGS_NV = 1, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_NV = 2, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_NV = 3, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_NV = 4, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_NV = 5, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_NV = 6, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_TASKS_NV = 7, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkIndirectCommandsTokenTypeNV; + +typedef enum VkIndirectStateFlagBitsNV { + VK_INDIRECT_STATE_FLAG_FRONTFACE_BIT_NV = 0x00000001, + VK_INDIRECT_STATE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkIndirectStateFlagBitsNV; +typedef VkFlags VkIndirectStateFlagsNV; + +typedef enum VkIndirectCommandsLayoutUsageFlagBitsNV { + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_NV = 0x00000001, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NV = 0x00000002, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NV = 0x00000004, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkIndirectCommandsLayoutUsageFlagBitsNV; +typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNV; +typedef struct VkPhysicalDeviceDeviceGeneratedCommandsPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t maxGraphicsShaderGroupCount; + uint32_t maxIndirectSequenceCount; + uint32_t maxIndirectCommandsTokenCount; + uint32_t maxIndirectCommandsStreamCount; + uint32_t maxIndirectCommandsTokenOffset; + uint32_t maxIndirectCommandsStreamStride; + uint32_t minSequencesCountBufferOffsetAlignment; + uint32_t minSequencesIndexBufferOffsetAlignment; + uint32_t minIndirectCommandsBufferOffsetAlignment; +} VkPhysicalDeviceDeviceGeneratedCommandsPropertiesNV; + +typedef struct VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 deviceGeneratedCommands; +} VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV; + +typedef struct VkGraphicsShaderGroupCreateInfoNV { + VkStructureType sType; + const void* pNext; + uint32_t stageCount; + const VkPipelineShaderStageCreateInfo* pStages; + const VkPipelineVertexInputStateCreateInfo* pVertexInputState; + const VkPipelineTessellationStateCreateInfo* pTessellationState; +} VkGraphicsShaderGroupCreateInfoNV; + +typedef struct VkGraphicsPipelineShaderGroupsCreateInfoNV { + VkStructureType sType; + const void* pNext; + uint32_t groupCount; + const VkGraphicsShaderGroupCreateInfoNV* pGroups; + uint32_t pipelineCount; + const VkPipeline* pPipelines; +} VkGraphicsPipelineShaderGroupsCreateInfoNV; + +typedef struct VkBindShaderGroupIndirectCommandNV { + uint32_t groupIndex; +} VkBindShaderGroupIndirectCommandNV; + +typedef struct VkBindIndexBufferIndirectCommandNV { + VkDeviceAddress bufferAddress; + uint32_t size; + VkIndexType indexType; +} VkBindIndexBufferIndirectCommandNV; + +typedef struct VkBindVertexBufferIndirectCommandNV { + VkDeviceAddress bufferAddress; + uint32_t size; + uint32_t stride; +} VkBindVertexBufferIndirectCommandNV; + +typedef struct VkSetStateFlagsIndirectCommandNV { + uint32_t data; +} VkSetStateFlagsIndirectCommandNV; + +typedef struct VkIndirectCommandsStreamNV { + VkBuffer buffer; + VkDeviceSize offset; +} VkIndirectCommandsStreamNV; + +typedef struct VkIndirectCommandsLayoutTokenNV { + VkStructureType sType; + const void* pNext; + VkIndirectCommandsTokenTypeNV tokenType; + uint32_t stream; + uint32_t offset; + uint32_t vertexBindingUnit; + VkBool32 vertexDynamicStride; + VkPipelineLayout pushconstantPipelineLayout; + VkShaderStageFlags pushconstantShaderStageFlags; + uint32_t pushconstantOffset; + uint32_t pushconstantSize; + VkIndirectStateFlagsNV indirectStateFlags; + uint32_t indexTypeCount; + const VkIndexType* pIndexTypes; + const uint32_t* pIndexTypeValues; +} VkIndirectCommandsLayoutTokenNV; + +typedef struct VkIndirectCommandsLayoutCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkIndirectCommandsLayoutUsageFlagsNV flags; + VkPipelineBindPoint pipelineBindPoint; + uint32_t tokenCount; + const VkIndirectCommandsLayoutTokenNV* pTokens; + uint32_t streamCount; + const uint32_t* pStreamStrides; +} VkIndirectCommandsLayoutCreateInfoNV; + +typedef struct VkGeneratedCommandsInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineBindPoint pipelineBindPoint; + VkPipeline pipeline; + VkIndirectCommandsLayoutNV indirectCommandsLayout; + uint32_t streamCount; + const VkIndirectCommandsStreamNV* pStreams; + uint32_t sequencesCount; + VkBuffer preprocessBuffer; + VkDeviceSize preprocessOffset; + VkDeviceSize preprocessSize; + VkBuffer sequencesCountBuffer; + VkDeviceSize sequencesCountOffset; + VkBuffer sequencesIndexBuffer; + VkDeviceSize sequencesIndexOffset; +} VkGeneratedCommandsInfoNV; + +typedef struct VkGeneratedCommandsMemoryRequirementsInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineBindPoint pipelineBindPoint; + VkPipeline pipeline; + VkIndirectCommandsLayoutNV indirectCommandsLayout; + uint32_t maxSequencesCount; +} VkGeneratedCommandsMemoryRequirementsInfoNV; + +typedef void (VKAPI_PTR *PFN_vkGetGeneratedCommandsMemoryRequirementsNV)(VkDevice device, const VkGeneratedCommandsMemoryRequirementsInfoNV* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkCmdPreprocessGeneratedCommandsNV)(VkCommandBuffer commandBuffer, const VkGeneratedCommandsInfoNV* pGeneratedCommandsInfo); +typedef void (VKAPI_PTR *PFN_vkCmdExecuteGeneratedCommandsNV)(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, const VkGeneratedCommandsInfoNV* pGeneratedCommandsInfo); +typedef void (VKAPI_PTR *PFN_vkCmdBindPipelineShaderGroupNV)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline, uint32_t groupIndex); +typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNV)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNV* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNV* pIndirectCommandsLayout); +typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNV)(VkDevice device, VkIndirectCommandsLayoutNV indirectCommandsLayout, const VkAllocationCallbacks* pAllocator); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetGeneratedCommandsMemoryRequirementsNV( + VkDevice device, + const VkGeneratedCommandsMemoryRequirementsInfoNV* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkCmdPreprocessGeneratedCommandsNV( + VkCommandBuffer commandBuffer, + const VkGeneratedCommandsInfoNV* pGeneratedCommandsInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdExecuteGeneratedCommandsNV( + VkCommandBuffer commandBuffer, + VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoNV* pGeneratedCommandsInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindPipelineShaderGroupNV( + VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipeline pipeline, + uint32_t groupIndex); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNV( + VkDevice device, + const VkIndirectCommandsLayoutCreateInfoNV* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutNV* pIndirectCommandsLayout); + +VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNV( + VkDevice device, + VkIndirectCommandsLayoutNV indirectCommandsLayout, + const VkAllocationCallbacks* pAllocator); +#endif + + +#define VK_NV_inherited_viewport_scissor 1 +#define VK_NV_INHERITED_VIEWPORT_SCISSOR_SPEC_VERSION 1 +#define VK_NV_INHERITED_VIEWPORT_SCISSOR_EXTENSION_NAME "VK_NV_inherited_viewport_scissor" +typedef struct VkPhysicalDeviceInheritedViewportScissorFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 inheritedViewportScissor2D; +} VkPhysicalDeviceInheritedViewportScissorFeaturesNV; + +typedef struct VkCommandBufferInheritanceViewportScissorInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 viewportScissor2D; + uint32_t viewportDepthCount; + const VkViewport* pViewportDepths; +} VkCommandBufferInheritanceViewportScissorInfoNV; + + + +#define VK_EXT_texel_buffer_alignment 1 +#define VK_EXT_TEXEL_BUFFER_ALIGNMENT_SPEC_VERSION 1 +#define VK_EXT_TEXEL_BUFFER_ALIGNMENT_EXTENSION_NAME "VK_EXT_texel_buffer_alignment" +typedef struct VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 texelBufferAlignment; +} VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT; + +typedef struct VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT { + VkStructureType sType; + void* pNext; + VkDeviceSize storageTexelBufferOffsetAlignmentBytes; + VkBool32 storageTexelBufferOffsetSingleTexelAlignment; + VkDeviceSize uniformTexelBufferOffsetAlignmentBytes; + VkBool32 uniformTexelBufferOffsetSingleTexelAlignment; +} VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT; + + + +#define VK_QCOM_render_pass_transform 1 +#define VK_QCOM_RENDER_PASS_TRANSFORM_SPEC_VERSION 2 +#define VK_QCOM_RENDER_PASS_TRANSFORM_EXTENSION_NAME "VK_QCOM_render_pass_transform" +typedef struct VkRenderPassTransformBeginInfoQCOM { + VkStructureType sType; + void* pNext; + VkSurfaceTransformFlagBitsKHR transform; +} VkRenderPassTransformBeginInfoQCOM; + +typedef struct VkCommandBufferInheritanceRenderPassTransformInfoQCOM { + VkStructureType sType; + void* pNext; + VkSurfaceTransformFlagBitsKHR transform; + VkRect2D renderArea; +} VkCommandBufferInheritanceRenderPassTransformInfoQCOM; + + + +#define VK_EXT_device_memory_report 1 +#define VK_EXT_DEVICE_MEMORY_REPORT_SPEC_VERSION 2 +#define VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME "VK_EXT_device_memory_report" + +typedef enum VkDeviceMemoryReportEventTypeEXT { + VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT = 0, + VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT = 1, + VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_IMPORT_EXT = 2, + VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_UNIMPORT_EXT = 3, + VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATION_FAILED_EXT = 4, + VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDeviceMemoryReportEventTypeEXT; +typedef VkFlags VkDeviceMemoryReportFlagsEXT; +typedef struct VkPhysicalDeviceDeviceMemoryReportFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 deviceMemoryReport; +} VkPhysicalDeviceDeviceMemoryReportFeaturesEXT; + +typedef struct VkDeviceMemoryReportCallbackDataEXT { + VkStructureType sType; + void* pNext; + VkDeviceMemoryReportFlagsEXT flags; + VkDeviceMemoryReportEventTypeEXT type; + uint64_t memoryObjectId; + VkDeviceSize size; + VkObjectType objectType; + uint64_t objectHandle; + uint32_t heapIndex; +} VkDeviceMemoryReportCallbackDataEXT; + +typedef void (VKAPI_PTR *PFN_vkDeviceMemoryReportCallbackEXT)( + const VkDeviceMemoryReportCallbackDataEXT* pCallbackData, + void* pUserData); + +typedef struct VkDeviceDeviceMemoryReportCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDeviceMemoryReportFlagsEXT flags; + PFN_vkDeviceMemoryReportCallbackEXT pfnUserCallback; + void* pUserData; +} VkDeviceDeviceMemoryReportCreateInfoEXT; + + + +#define VK_EXT_acquire_drm_display 1 +#define VK_EXT_ACQUIRE_DRM_DISPLAY_SPEC_VERSION 1 +#define VK_EXT_ACQUIRE_DRM_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_drm_display" +typedef VkResult (VKAPI_PTR *PFN_vkAcquireDrmDisplayEXT)(VkPhysicalDevice physicalDevice, int32_t drmFd, VkDisplayKHR display); +typedef VkResult (VKAPI_PTR *PFN_vkGetDrmDisplayEXT)(VkPhysicalDevice physicalDevice, int32_t drmFd, uint32_t connectorId, VkDisplayKHR* display); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireDrmDisplayEXT( + VkPhysicalDevice physicalDevice, + int32_t drmFd, + VkDisplayKHR display); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDrmDisplayEXT( + VkPhysicalDevice physicalDevice, + int32_t drmFd, + uint32_t connectorId, + VkDisplayKHR* display); +#endif + + +#define VK_EXT_robustness2 1 +#define VK_EXT_ROBUSTNESS_2_SPEC_VERSION 1 +#define VK_EXT_ROBUSTNESS_2_EXTENSION_NAME "VK_EXT_robustness2" +typedef struct VkPhysicalDeviceRobustness2FeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 robustBufferAccess2; + VkBool32 robustImageAccess2; + VkBool32 nullDescriptor; +} VkPhysicalDeviceRobustness2FeaturesEXT; + +typedef struct VkPhysicalDeviceRobustness2PropertiesEXT { + VkStructureType sType; + void* pNext; + VkDeviceSize robustStorageBufferAccessSizeAlignment; + VkDeviceSize robustUniformBufferAccessSizeAlignment; +} VkPhysicalDeviceRobustness2PropertiesEXT; + + + +#define VK_EXT_custom_border_color 1 +#define VK_EXT_CUSTOM_BORDER_COLOR_SPEC_VERSION 12 +#define VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME "VK_EXT_custom_border_color" +typedef struct VkSamplerCustomBorderColorCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkClearColorValue customBorderColor; + VkFormat format; +} VkSamplerCustomBorderColorCreateInfoEXT; + +typedef struct VkPhysicalDeviceCustomBorderColorPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxCustomBorderColorSamplers; +} VkPhysicalDeviceCustomBorderColorPropertiesEXT; + +typedef struct VkPhysicalDeviceCustomBorderColorFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 customBorderColors; + VkBool32 customBorderColorWithoutFormat; +} VkPhysicalDeviceCustomBorderColorFeaturesEXT; + + + +#define VK_GOOGLE_user_type 1 +#define VK_GOOGLE_USER_TYPE_SPEC_VERSION 1 +#define VK_GOOGLE_USER_TYPE_EXTENSION_NAME "VK_GOOGLE_user_type" + + +#define VK_EXT_private_data 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPrivateDataSlotEXT) +#define VK_EXT_PRIVATE_DATA_SPEC_VERSION 1 +#define VK_EXT_PRIVATE_DATA_EXTENSION_NAME "VK_EXT_private_data" + +typedef enum VkPrivateDataSlotCreateFlagBitsEXT { + VK_PRIVATE_DATA_SLOT_CREATE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkPrivateDataSlotCreateFlagBitsEXT; +typedef VkFlags VkPrivateDataSlotCreateFlagsEXT; +typedef struct VkPhysicalDevicePrivateDataFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 privateData; +} VkPhysicalDevicePrivateDataFeaturesEXT; + +typedef struct VkDevicePrivateDataCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t privateDataSlotRequestCount; +} VkDevicePrivateDataCreateInfoEXT; + +typedef struct VkPrivateDataSlotCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPrivateDataSlotCreateFlagsEXT flags; +} VkPrivateDataSlotCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkCreatePrivateDataSlotEXT)(VkDevice device, const VkPrivateDataSlotCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPrivateDataSlotEXT* pPrivateDataSlot); +typedef void (VKAPI_PTR *PFN_vkDestroyPrivateDataSlotEXT)(VkDevice device, VkPrivateDataSlotEXT privateDataSlot, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkSetPrivateDataEXT)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlotEXT privateDataSlot, uint64_t data); +typedef void (VKAPI_PTR *PFN_vkGetPrivateDataEXT)(VkDevice device, VkObjectType objectType, uint64_t objectHandle, VkPrivateDataSlotEXT privateDataSlot, uint64_t* pData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreatePrivateDataSlotEXT( + VkDevice device, + const VkPrivateDataSlotCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPrivateDataSlotEXT* pPrivateDataSlot); + +VKAPI_ATTR void VKAPI_CALL vkDestroyPrivateDataSlotEXT( + VkDevice device, + VkPrivateDataSlotEXT privateDataSlot, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetPrivateDataEXT( + VkDevice device, + VkObjectType objectType, + uint64_t objectHandle, + VkPrivateDataSlotEXT privateDataSlot, + uint64_t data); + +VKAPI_ATTR void VKAPI_CALL vkGetPrivateDataEXT( + VkDevice device, + VkObjectType objectType, + uint64_t objectHandle, + VkPrivateDataSlotEXT privateDataSlot, + uint64_t* pData); +#endif + + +#define VK_EXT_pipeline_creation_cache_control 1 +#define VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_SPEC_VERSION 3 +#define VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME "VK_EXT_pipeline_creation_cache_control" +typedef struct VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 pipelineCreationCacheControl; +} VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT; + + + +#define VK_NV_device_diagnostics_config 1 +#define VK_NV_DEVICE_DIAGNOSTICS_CONFIG_SPEC_VERSION 1 +#define VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME "VK_NV_device_diagnostics_config" + +typedef enum VkDeviceDiagnosticsConfigFlagBitsNV { + VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV = 0x00000001, + VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV = 0x00000002, + VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV = 0x00000004, + VK_DEVICE_DIAGNOSTICS_CONFIG_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkDeviceDiagnosticsConfigFlagBitsNV; +typedef VkFlags VkDeviceDiagnosticsConfigFlagsNV; +typedef struct VkPhysicalDeviceDiagnosticsConfigFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 diagnosticsConfig; +} VkPhysicalDeviceDiagnosticsConfigFeaturesNV; + +typedef struct VkDeviceDiagnosticsConfigCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkDeviceDiagnosticsConfigFlagsNV flags; +} VkDeviceDiagnosticsConfigCreateInfoNV; + + + +#define VK_QCOM_render_pass_store_ops 1 +#define VK_QCOM_RENDER_PASS_STORE_OPS_SPEC_VERSION 2 +#define VK_QCOM_RENDER_PASS_STORE_OPS_EXTENSION_NAME "VK_QCOM_render_pass_store_ops" + + +#define VK_NV_fragment_shading_rate_enums 1 +#define VK_NV_FRAGMENT_SHADING_RATE_ENUMS_SPEC_VERSION 1 +#define VK_NV_FRAGMENT_SHADING_RATE_ENUMS_EXTENSION_NAME "VK_NV_fragment_shading_rate_enums" + +typedef enum VkFragmentShadingRateTypeNV { + VK_FRAGMENT_SHADING_RATE_TYPE_FRAGMENT_SIZE_NV = 0, + VK_FRAGMENT_SHADING_RATE_TYPE_ENUMS_NV = 1, + VK_FRAGMENT_SHADING_RATE_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkFragmentShadingRateTypeNV; + +typedef enum VkFragmentShadingRateNV { + VK_FRAGMENT_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV = 0, + VK_FRAGMENT_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV = 1, + VK_FRAGMENT_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV = 4, + VK_FRAGMENT_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV = 5, + VK_FRAGMENT_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV = 6, + VK_FRAGMENT_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV = 9, + VK_FRAGMENT_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV = 10, + VK_FRAGMENT_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV = 11, + VK_FRAGMENT_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV = 12, + VK_FRAGMENT_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV = 13, + VK_FRAGMENT_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV = 14, + VK_FRAGMENT_SHADING_RATE_NO_INVOCATIONS_NV = 15, + VK_FRAGMENT_SHADING_RATE_MAX_ENUM_NV = 0x7FFFFFFF +} VkFragmentShadingRateNV; +typedef struct VkPhysicalDeviceFragmentShadingRateEnumsFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 fragmentShadingRateEnums; + VkBool32 supersampleFragmentShadingRates; + VkBool32 noInvocationFragmentShadingRates; +} VkPhysicalDeviceFragmentShadingRateEnumsFeaturesNV; + +typedef struct VkPhysicalDeviceFragmentShadingRateEnumsPropertiesNV { + VkStructureType sType; + void* pNext; + VkSampleCountFlagBits maxFragmentShadingRateInvocationCount; +} VkPhysicalDeviceFragmentShadingRateEnumsPropertiesNV; + +typedef struct VkPipelineFragmentShadingRateEnumStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkFragmentShadingRateTypeNV shadingRateType; + VkFragmentShadingRateNV shadingRate; + VkFragmentShadingRateCombinerOpKHR combinerOps[2]; +} VkPipelineFragmentShadingRateEnumStateCreateInfoNV; + +typedef void (VKAPI_PTR *PFN_vkCmdSetFragmentShadingRateEnumNV)(VkCommandBuffer commandBuffer, VkFragmentShadingRateNV shadingRate, const VkFragmentShadingRateCombinerOpKHR combinerOps[2]); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetFragmentShadingRateEnumNV( + VkCommandBuffer commandBuffer, + VkFragmentShadingRateNV shadingRate, + const VkFragmentShadingRateCombinerOpKHR combinerOps[2]); +#endif + + +#define VK_NV_ray_tracing_motion_blur 1 +#define VK_NV_RAY_TRACING_MOTION_BLUR_SPEC_VERSION 1 +#define VK_NV_RAY_TRACING_MOTION_BLUR_EXTENSION_NAME "VK_NV_ray_tracing_motion_blur" + +typedef enum VkAccelerationStructureMotionInstanceTypeNV { + VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_STATIC_NV = 0, + VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_MATRIX_MOTION_NV = 1, + VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_SRT_MOTION_NV = 2, + VK_ACCELERATION_STRUCTURE_MOTION_INSTANCE_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkAccelerationStructureMotionInstanceTypeNV; +typedef VkFlags VkAccelerationStructureMotionInfoFlagsNV; +typedef VkFlags VkAccelerationStructureMotionInstanceFlagsNV; +typedef union VkDeviceOrHostAddressConstKHR { + VkDeviceAddress deviceAddress; + const void* hostAddress; +} VkDeviceOrHostAddressConstKHR; + +typedef struct VkAccelerationStructureGeometryMotionTrianglesDataNV { + VkStructureType sType; + const void* pNext; + VkDeviceOrHostAddressConstKHR vertexData; +} VkAccelerationStructureGeometryMotionTrianglesDataNV; + +typedef struct VkAccelerationStructureMotionInfoNV { + VkStructureType sType; + const void* pNext; + uint32_t maxInstances; + VkAccelerationStructureMotionInfoFlagsNV flags; +} VkAccelerationStructureMotionInfoNV; + +typedef struct VkAccelerationStructureMatrixMotionInstanceNV { + VkTransformMatrixKHR transformT0; + VkTransformMatrixKHR transformT1; + uint32_t instanceCustomIndex:24; + uint32_t mask:8; + uint32_t instanceShaderBindingTableRecordOffset:24; + VkGeometryInstanceFlagsKHR flags:8; + uint64_t accelerationStructureReference; +} VkAccelerationStructureMatrixMotionInstanceNV; + +typedef struct VkSRTDataNV { + float sx; + float a; + float b; + float pvx; + float sy; + float c; + float pvy; + float sz; + float pvz; + float qx; + float qy; + float qz; + float qw; + float tx; + float ty; + float tz; +} VkSRTDataNV; + +typedef struct VkAccelerationStructureSRTMotionInstanceNV { + VkSRTDataNV transformT0; + VkSRTDataNV transformT1; + uint32_t instanceCustomIndex:24; + uint32_t mask:8; + uint32_t instanceShaderBindingTableRecordOffset:24; + VkGeometryInstanceFlagsKHR flags:8; + uint64_t accelerationStructureReference; +} VkAccelerationStructureSRTMotionInstanceNV; + +typedef union VkAccelerationStructureMotionInstanceDataNV { + VkAccelerationStructureInstanceKHR staticInstance; + VkAccelerationStructureMatrixMotionInstanceNV matrixMotionInstance; + VkAccelerationStructureSRTMotionInstanceNV srtMotionInstance; +} VkAccelerationStructureMotionInstanceDataNV; + +typedef struct VkAccelerationStructureMotionInstanceNV { + VkAccelerationStructureMotionInstanceTypeNV type; + VkAccelerationStructureMotionInstanceFlagsNV flags; + VkAccelerationStructureMotionInstanceDataNV data; +} VkAccelerationStructureMotionInstanceNV; + +typedef struct VkPhysicalDeviceRayTracingMotionBlurFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 rayTracingMotionBlur; + VkBool32 rayTracingMotionBlurPipelineTraceRaysIndirect; +} VkPhysicalDeviceRayTracingMotionBlurFeaturesNV; + + + +#define VK_EXT_ycbcr_2plane_444_formats 1 +#define VK_EXT_YCBCR_2PLANE_444_FORMATS_SPEC_VERSION 1 +#define VK_EXT_YCBCR_2PLANE_444_FORMATS_EXTENSION_NAME "VK_EXT_ycbcr_2plane_444_formats" +typedef struct VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 ycbcr2plane444Formats; +} VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT; + + + +#define VK_EXT_fragment_density_map2 1 +#define VK_EXT_FRAGMENT_DENSITY_MAP_2_SPEC_VERSION 1 +#define VK_EXT_FRAGMENT_DENSITY_MAP_2_EXTENSION_NAME "VK_EXT_fragment_density_map2" +typedef struct VkPhysicalDeviceFragmentDensityMap2FeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 fragmentDensityMapDeferred; +} VkPhysicalDeviceFragmentDensityMap2FeaturesEXT; + +typedef struct VkPhysicalDeviceFragmentDensityMap2PropertiesEXT { + VkStructureType sType; + void* pNext; + VkBool32 subsampledLoads; + VkBool32 subsampledCoarseReconstructionEarlyAccess; + uint32_t maxSubsampledArrayLayers; + uint32_t maxDescriptorSetSubsampledSamplers; +} VkPhysicalDeviceFragmentDensityMap2PropertiesEXT; + + + +#define VK_QCOM_rotated_copy_commands 1 +#define VK_QCOM_ROTATED_COPY_COMMANDS_SPEC_VERSION 1 +#define VK_QCOM_ROTATED_COPY_COMMANDS_EXTENSION_NAME "VK_QCOM_rotated_copy_commands" +typedef struct VkCopyCommandTransformInfoQCOM { + VkStructureType sType; + const void* pNext; + VkSurfaceTransformFlagBitsKHR transform; +} VkCopyCommandTransformInfoQCOM; + + + +#define VK_EXT_image_robustness 1 +#define VK_EXT_IMAGE_ROBUSTNESS_SPEC_VERSION 1 +#define VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME "VK_EXT_image_robustness" +typedef struct VkPhysicalDeviceImageRobustnessFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 robustImageAccess; +} VkPhysicalDeviceImageRobustnessFeaturesEXT; + + + +#define VK_EXT_4444_formats 1 +#define VK_EXT_4444_FORMATS_SPEC_VERSION 1 +#define VK_EXT_4444_FORMATS_EXTENSION_NAME "VK_EXT_4444_formats" +typedef struct VkPhysicalDevice4444FormatsFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 formatA4R4G4B4; + VkBool32 formatA4B4G4R4; +} VkPhysicalDevice4444FormatsFeaturesEXT; + + + +#define VK_EXT_rgba10x6_formats 1 +#define VK_EXT_RGBA10X6_FORMATS_SPEC_VERSION 1 +#define VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME "VK_EXT_rgba10x6_formats" +typedef struct VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 formatRgba10x6WithoutYCbCrSampler; +} VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT; + + + +#define VK_NV_acquire_winrt_display 1 +#define VK_NV_ACQUIRE_WINRT_DISPLAY_SPEC_VERSION 1 +#define VK_NV_ACQUIRE_WINRT_DISPLAY_EXTENSION_NAME "VK_NV_acquire_winrt_display" +typedef VkResult (VKAPI_PTR *PFN_vkAcquireWinrtDisplayNV)(VkPhysicalDevice physicalDevice, VkDisplayKHR display); +typedef VkResult (VKAPI_PTR *PFN_vkGetWinrtDisplayNV)(VkPhysicalDevice physicalDevice, uint32_t deviceRelativeId, VkDisplayKHR* pDisplay); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireWinrtDisplayNV( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetWinrtDisplayNV( + VkPhysicalDevice physicalDevice, + uint32_t deviceRelativeId, + VkDisplayKHR* pDisplay); +#endif + + +#define VK_VALVE_mutable_descriptor_type 1 +#define VK_VALVE_MUTABLE_DESCRIPTOR_TYPE_SPEC_VERSION 1 +#define VK_VALVE_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME "VK_VALVE_mutable_descriptor_type" +typedef struct VkPhysicalDeviceMutableDescriptorTypeFeaturesVALVE { + VkStructureType sType; + void* pNext; + VkBool32 mutableDescriptorType; +} VkPhysicalDeviceMutableDescriptorTypeFeaturesVALVE; + +typedef struct VkMutableDescriptorTypeListVALVE { + uint32_t descriptorTypeCount; + const VkDescriptorType* pDescriptorTypes; +} VkMutableDescriptorTypeListVALVE; + +typedef struct VkMutableDescriptorTypeCreateInfoVALVE { + VkStructureType sType; + const void* pNext; + uint32_t mutableDescriptorTypeListCount; + const VkMutableDescriptorTypeListVALVE* pMutableDescriptorTypeLists; +} VkMutableDescriptorTypeCreateInfoVALVE; + + + +#define VK_EXT_vertex_input_dynamic_state 1 +#define VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_SPEC_VERSION 2 +#define VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME "VK_EXT_vertex_input_dynamic_state" +typedef struct VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 vertexInputDynamicState; +} VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT; + +typedef struct VkVertexInputBindingDescription2EXT { + VkStructureType sType; + void* pNext; + uint32_t binding; + uint32_t stride; + VkVertexInputRate inputRate; + uint32_t divisor; +} VkVertexInputBindingDescription2EXT; + +typedef struct VkVertexInputAttributeDescription2EXT { + VkStructureType sType; + void* pNext; + uint32_t location; + uint32_t binding; + VkFormat format; + uint32_t offset; +} VkVertexInputAttributeDescription2EXT; + +typedef void (VKAPI_PTR *PFN_vkCmdSetVertexInputEXT)(VkCommandBuffer commandBuffer, uint32_t vertexBindingDescriptionCount, const VkVertexInputBindingDescription2EXT* pVertexBindingDescriptions, uint32_t vertexAttributeDescriptionCount, const VkVertexInputAttributeDescription2EXT* pVertexAttributeDescriptions); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetVertexInputEXT( + VkCommandBuffer commandBuffer, + uint32_t vertexBindingDescriptionCount, + const VkVertexInputBindingDescription2EXT* pVertexBindingDescriptions, + uint32_t vertexAttributeDescriptionCount, + const VkVertexInputAttributeDescription2EXT* pVertexAttributeDescriptions); +#endif + + +#define VK_EXT_physical_device_drm 1 +#define VK_EXT_PHYSICAL_DEVICE_DRM_SPEC_VERSION 1 +#define VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME "VK_EXT_physical_device_drm" +typedef struct VkPhysicalDeviceDrmPropertiesEXT { + VkStructureType sType; + void* pNext; + VkBool32 hasPrimary; + VkBool32 hasRender; + int64_t primaryMajor; + int64_t primaryMinor; + int64_t renderMajor; + int64_t renderMinor; +} VkPhysicalDeviceDrmPropertiesEXT; + + + +#define VK_EXT_primitive_topology_list_restart 1 +#define VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_SPEC_VERSION 1 +#define VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME "VK_EXT_primitive_topology_list_restart" +typedef struct VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 primitiveTopologyListRestart; + VkBool32 primitiveTopologyPatchListRestart; +} VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT; + + + +#define VK_HUAWEI_subpass_shading 1 +#define VK_HUAWEI_SUBPASS_SHADING_SPEC_VERSION 2 +#define VK_HUAWEI_SUBPASS_SHADING_EXTENSION_NAME "VK_HUAWEI_subpass_shading" +typedef struct VkSubpassShadingPipelineCreateInfoHUAWEI { + VkStructureType sType; + void* pNext; + VkRenderPass renderPass; + uint32_t subpass; +} VkSubpassShadingPipelineCreateInfoHUAWEI; + +typedef struct VkPhysicalDeviceSubpassShadingFeaturesHUAWEI { + VkStructureType sType; + void* pNext; + VkBool32 subpassShading; +} VkPhysicalDeviceSubpassShadingFeaturesHUAWEI; + +typedef struct VkPhysicalDeviceSubpassShadingPropertiesHUAWEI { + VkStructureType sType; + void* pNext; + uint32_t maxSubpassShadingWorkgroupSizeAspectRatio; +} VkPhysicalDeviceSubpassShadingPropertiesHUAWEI; + +typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI)(VkDevice device, VkRenderPass renderpass, VkExtent2D* pMaxWorkgroupSize); +typedef void (VKAPI_PTR *PFN_vkCmdSubpassShadingHUAWEI)(VkCommandBuffer commandBuffer); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI( + VkDevice device, + VkRenderPass renderpass, + VkExtent2D* pMaxWorkgroupSize); + +VKAPI_ATTR void VKAPI_CALL vkCmdSubpassShadingHUAWEI( + VkCommandBuffer commandBuffer); +#endif + + +#define VK_HUAWEI_invocation_mask 1 +#define VK_HUAWEI_INVOCATION_MASK_SPEC_VERSION 1 +#define VK_HUAWEI_INVOCATION_MASK_EXTENSION_NAME "VK_HUAWEI_invocation_mask" +typedef struct VkPhysicalDeviceInvocationMaskFeaturesHUAWEI { + VkStructureType sType; + void* pNext; + VkBool32 invocationMask; +} VkPhysicalDeviceInvocationMaskFeaturesHUAWEI; + +typedef void (VKAPI_PTR *PFN_vkCmdBindInvocationMaskHUAWEI)(VkCommandBuffer commandBuffer, VkImageView imageView, VkImageLayout imageLayout); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdBindInvocationMaskHUAWEI( + VkCommandBuffer commandBuffer, + VkImageView imageView, + VkImageLayout imageLayout); +#endif + + +#define VK_NV_external_memory_rdma 1 +typedef void* VkRemoteAddressNV; +#define VK_NV_EXTERNAL_MEMORY_RDMA_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_RDMA_EXTENSION_NAME "VK_NV_external_memory_rdma" +typedef struct VkMemoryGetRemoteAddressInfoNV { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; + VkExternalMemoryHandleTypeFlagBits handleType; +} VkMemoryGetRemoteAddressInfoNV; + +typedef struct VkPhysicalDeviceExternalMemoryRDMAFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 externalMemoryRDMA; +} VkPhysicalDeviceExternalMemoryRDMAFeaturesNV; + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryRemoteAddressNV)(VkDevice device, const VkMemoryGetRemoteAddressInfoNV* pMemoryGetRemoteAddressInfo, VkRemoteAddressNV* pAddress); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryRemoteAddressNV( + VkDevice device, + const VkMemoryGetRemoteAddressInfoNV* pMemoryGetRemoteAddressInfo, + VkRemoteAddressNV* pAddress); +#endif + + +#define VK_EXT_extended_dynamic_state2 1 +#define VK_EXT_EXTENDED_DYNAMIC_STATE_2_SPEC_VERSION 1 +#define VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME "VK_EXT_extended_dynamic_state2" +typedef struct VkPhysicalDeviceExtendedDynamicState2FeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 extendedDynamicState2; + VkBool32 extendedDynamicState2LogicOp; + VkBool32 extendedDynamicState2PatchControlPoints; +} VkPhysicalDeviceExtendedDynamicState2FeaturesEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdSetPatchControlPointsEXT)(VkCommandBuffer commandBuffer, uint32_t patchControlPoints); +typedef void (VKAPI_PTR *PFN_vkCmdSetRasterizerDiscardEnableEXT)(VkCommandBuffer commandBuffer, VkBool32 rasterizerDiscardEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBiasEnableEXT)(VkCommandBuffer commandBuffer, VkBool32 depthBiasEnable); +typedef void (VKAPI_PTR *PFN_vkCmdSetLogicOpEXT)(VkCommandBuffer commandBuffer, VkLogicOp logicOp); +typedef void (VKAPI_PTR *PFN_vkCmdSetPrimitiveRestartEnableEXT)(VkCommandBuffer commandBuffer, VkBool32 primitiveRestartEnable); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetPatchControlPointsEXT( + VkCommandBuffer commandBuffer, + uint32_t patchControlPoints); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetRasterizerDiscardEnableEXT( + VkCommandBuffer commandBuffer, + VkBool32 rasterizerDiscardEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBiasEnableEXT( + VkCommandBuffer commandBuffer, + VkBool32 depthBiasEnable); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetLogicOpEXT( + VkCommandBuffer commandBuffer, + VkLogicOp logicOp); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetPrimitiveRestartEnableEXT( + VkCommandBuffer commandBuffer, + VkBool32 primitiveRestartEnable); +#endif + + +#define VK_EXT_color_write_enable 1 +#define VK_EXT_COLOR_WRITE_ENABLE_SPEC_VERSION 1 +#define VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME "VK_EXT_color_write_enable" +typedef struct VkPhysicalDeviceColorWriteEnableFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 colorWriteEnable; +} VkPhysicalDeviceColorWriteEnableFeaturesEXT; + +typedef struct VkPipelineColorWriteCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t attachmentCount; + const VkBool32* pColorWriteEnables; +} VkPipelineColorWriteCreateInfoEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdSetColorWriteEnableEXT)(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkBool32* pColorWriteEnables); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetColorWriteEnableEXT( + VkCommandBuffer commandBuffer, + uint32_t attachmentCount, + const VkBool32* pColorWriteEnables); +#endif + + +#define VK_EXT_global_priority_query 1 +#define VK_MAX_GLOBAL_PRIORITY_SIZE_EXT 16U +#define VK_EXT_GLOBAL_PRIORITY_QUERY_SPEC_VERSION 1 +#define VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME "VK_EXT_global_priority_query" +typedef struct VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 globalPriorityQuery; +} VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT; + +typedef struct VkQueueFamilyGlobalPriorityPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t priorityCount; + VkQueueGlobalPriorityEXT priorities[VK_MAX_GLOBAL_PRIORITY_SIZE_EXT]; +} VkQueueFamilyGlobalPriorityPropertiesEXT; + + + +#define VK_EXT_multi_draw 1 +#define VK_EXT_MULTI_DRAW_SPEC_VERSION 1 +#define VK_EXT_MULTI_DRAW_EXTENSION_NAME "VK_EXT_multi_draw" +typedef struct VkPhysicalDeviceMultiDrawFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 multiDraw; +} VkPhysicalDeviceMultiDrawFeaturesEXT; + +typedef struct VkPhysicalDeviceMultiDrawPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxMultiDrawCount; +} VkPhysicalDeviceMultiDrawPropertiesEXT; + +typedef struct VkMultiDrawInfoEXT { + uint32_t firstVertex; + uint32_t vertexCount; +} VkMultiDrawInfoEXT; + +typedef struct VkMultiDrawIndexedInfoEXT { + uint32_t firstIndex; + uint32_t indexCount; + int32_t vertexOffset; +} VkMultiDrawIndexedInfoEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdDrawMultiEXT)(VkCommandBuffer commandBuffer, uint32_t drawCount, const VkMultiDrawInfoEXT* pVertexInfo, uint32_t instanceCount, uint32_t firstInstance, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawMultiIndexedEXT)(VkCommandBuffer commandBuffer, uint32_t drawCount, const VkMultiDrawIndexedInfoEXT* pIndexInfo, uint32_t instanceCount, uint32_t firstInstance, uint32_t stride, const int32_t* pVertexOffset); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawMultiEXT( + VkCommandBuffer commandBuffer, + uint32_t drawCount, + const VkMultiDrawInfoEXT* pVertexInfo, + uint32_t instanceCount, + uint32_t firstInstance, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawMultiIndexedEXT( + VkCommandBuffer commandBuffer, + uint32_t drawCount, + const VkMultiDrawIndexedInfoEXT* pIndexInfo, + uint32_t instanceCount, + uint32_t firstInstance, + uint32_t stride, + const int32_t* pVertexOffset); +#endif + + +#define VK_EXT_load_store_op_none 1 +#define VK_EXT_LOAD_STORE_OP_NONE_SPEC_VERSION 1 +#define VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME "VK_EXT_load_store_op_none" + + +#define VK_EXT_border_color_swizzle 1 +#define VK_EXT_BORDER_COLOR_SWIZZLE_SPEC_VERSION 1 +#define VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME "VK_EXT_border_color_swizzle" +typedef struct VkPhysicalDeviceBorderColorSwizzleFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 borderColorSwizzle; + VkBool32 borderColorSwizzleFromImage; +} VkPhysicalDeviceBorderColorSwizzleFeaturesEXT; + +typedef struct VkSamplerBorderColorComponentMappingCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkComponentMapping components; + VkBool32 srgb; +} VkSamplerBorderColorComponentMappingCreateInfoEXT; + + + +#define VK_EXT_pageable_device_local_memory 1 +#define VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_SPEC_VERSION 1 +#define VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME "VK_EXT_pageable_device_local_memory" +typedef struct VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 pageableDeviceLocalMemory; +} VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT; + +typedef void (VKAPI_PTR *PFN_vkSetDeviceMemoryPriorityEXT)(VkDevice device, VkDeviceMemory memory, float priority); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkSetDeviceMemoryPriorityEXT( + VkDevice device, + VkDeviceMemory memory, + float priority); +#endif + + +#define VK_KHR_acceleration_structure 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkAccelerationStructureKHR) +#define VK_KHR_ACCELERATION_STRUCTURE_SPEC_VERSION 13 +#define VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME "VK_KHR_acceleration_structure" + +typedef enum VkBuildAccelerationStructureModeKHR { + VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR = 0, + VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR = 1, + VK_BUILD_ACCELERATION_STRUCTURE_MODE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkBuildAccelerationStructureModeKHR; + +typedef enum VkAccelerationStructureBuildTypeKHR { + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_KHR = 0, + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR = 1, + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_OR_DEVICE_KHR = 2, + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkAccelerationStructureBuildTypeKHR; + +typedef enum VkAccelerationStructureCompatibilityKHR { + VK_ACCELERATION_STRUCTURE_COMPATIBILITY_COMPATIBLE_KHR = 0, + VK_ACCELERATION_STRUCTURE_COMPATIBILITY_INCOMPATIBLE_KHR = 1, + VK_ACCELERATION_STRUCTURE_COMPATIBILITY_MAX_ENUM_KHR = 0x7FFFFFFF +} VkAccelerationStructureCompatibilityKHR; + +typedef enum VkAccelerationStructureCreateFlagBitsKHR { + VK_ACCELERATION_STRUCTURE_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_KHR = 0x00000001, + VK_ACCELERATION_STRUCTURE_CREATE_MOTION_BIT_NV = 0x00000004, + VK_ACCELERATION_STRUCTURE_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkAccelerationStructureCreateFlagBitsKHR; +typedef VkFlags VkAccelerationStructureCreateFlagsKHR; +typedef union VkDeviceOrHostAddressKHR { + VkDeviceAddress deviceAddress; + void* hostAddress; +} VkDeviceOrHostAddressKHR; + +typedef struct VkAccelerationStructureBuildRangeInfoKHR { + uint32_t primitiveCount; + uint32_t primitiveOffset; + uint32_t firstVertex; + uint32_t transformOffset; +} VkAccelerationStructureBuildRangeInfoKHR; + +typedef struct VkAccelerationStructureGeometryTrianglesDataKHR { + VkStructureType sType; + const void* pNext; + VkFormat vertexFormat; + VkDeviceOrHostAddressConstKHR vertexData; + VkDeviceSize vertexStride; + uint32_t maxVertex; + VkIndexType indexType; + VkDeviceOrHostAddressConstKHR indexData; + VkDeviceOrHostAddressConstKHR transformData; +} VkAccelerationStructureGeometryTrianglesDataKHR; + +typedef struct VkAccelerationStructureGeometryAabbsDataKHR { + VkStructureType sType; + const void* pNext; + VkDeviceOrHostAddressConstKHR data; + VkDeviceSize stride; +} VkAccelerationStructureGeometryAabbsDataKHR; + +typedef struct VkAccelerationStructureGeometryInstancesDataKHR { + VkStructureType sType; + const void* pNext; + VkBool32 arrayOfPointers; + VkDeviceOrHostAddressConstKHR data; +} VkAccelerationStructureGeometryInstancesDataKHR; + +typedef union VkAccelerationStructureGeometryDataKHR { + VkAccelerationStructureGeometryTrianglesDataKHR triangles; + VkAccelerationStructureGeometryAabbsDataKHR aabbs; + VkAccelerationStructureGeometryInstancesDataKHR instances; +} VkAccelerationStructureGeometryDataKHR; + +typedef struct VkAccelerationStructureGeometryKHR { + VkStructureType sType; + const void* pNext; + VkGeometryTypeKHR geometryType; + VkAccelerationStructureGeometryDataKHR geometry; + VkGeometryFlagsKHR flags; +} VkAccelerationStructureGeometryKHR; + +typedef struct VkAccelerationStructureBuildGeometryInfoKHR { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureTypeKHR type; + VkBuildAccelerationStructureFlagsKHR flags; + VkBuildAccelerationStructureModeKHR mode; + VkAccelerationStructureKHR srcAccelerationStructure; + VkAccelerationStructureKHR dstAccelerationStructure; + uint32_t geometryCount; + const VkAccelerationStructureGeometryKHR* pGeometries; + const VkAccelerationStructureGeometryKHR* const* ppGeometries; + VkDeviceOrHostAddressKHR scratchData; +} VkAccelerationStructureBuildGeometryInfoKHR; + +typedef struct VkAccelerationStructureCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureCreateFlagsKHR createFlags; + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize size; + VkAccelerationStructureTypeKHR type; + VkDeviceAddress deviceAddress; +} VkAccelerationStructureCreateInfoKHR; + +typedef struct VkWriteDescriptorSetAccelerationStructureKHR { + VkStructureType sType; + const void* pNext; + uint32_t accelerationStructureCount; + const VkAccelerationStructureKHR* pAccelerationStructures; +} VkWriteDescriptorSetAccelerationStructureKHR; + +typedef struct VkPhysicalDeviceAccelerationStructureFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 accelerationStructure; + VkBool32 accelerationStructureCaptureReplay; + VkBool32 accelerationStructureIndirectBuild; + VkBool32 accelerationStructureHostCommands; + VkBool32 descriptorBindingAccelerationStructureUpdateAfterBind; +} VkPhysicalDeviceAccelerationStructureFeaturesKHR; + +typedef struct VkPhysicalDeviceAccelerationStructurePropertiesKHR { + VkStructureType sType; + void* pNext; + uint64_t maxGeometryCount; + uint64_t maxInstanceCount; + uint64_t maxPrimitiveCount; + uint32_t maxPerStageDescriptorAccelerationStructures; + uint32_t maxPerStageDescriptorUpdateAfterBindAccelerationStructures; + uint32_t maxDescriptorSetAccelerationStructures; + uint32_t maxDescriptorSetUpdateAfterBindAccelerationStructures; + uint32_t minAccelerationStructureScratchOffsetAlignment; +} VkPhysicalDeviceAccelerationStructurePropertiesKHR; + +typedef struct VkAccelerationStructureDeviceAddressInfoKHR { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureKHR accelerationStructure; +} VkAccelerationStructureDeviceAddressInfoKHR; + +typedef struct VkAccelerationStructureVersionInfoKHR { + VkStructureType sType; + const void* pNext; + const uint8_t* pVersionData; +} VkAccelerationStructureVersionInfoKHR; + +typedef struct VkCopyAccelerationStructureToMemoryInfoKHR { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureKHR src; + VkDeviceOrHostAddressKHR dst; + VkCopyAccelerationStructureModeKHR mode; +} VkCopyAccelerationStructureToMemoryInfoKHR; + +typedef struct VkCopyMemoryToAccelerationStructureInfoKHR { + VkStructureType sType; + const void* pNext; + VkDeviceOrHostAddressConstKHR src; + VkAccelerationStructureKHR dst; + VkCopyAccelerationStructureModeKHR mode; +} VkCopyMemoryToAccelerationStructureInfoKHR; + +typedef struct VkCopyAccelerationStructureInfoKHR { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureKHR src; + VkAccelerationStructureKHR dst; + VkCopyAccelerationStructureModeKHR mode; +} VkCopyAccelerationStructureInfoKHR; + +typedef struct VkAccelerationStructureBuildSizesInfoKHR { + VkStructureType sType; + const void* pNext; + VkDeviceSize accelerationStructureSize; + VkDeviceSize updateScratchSize; + VkDeviceSize buildScratchSize; +} VkAccelerationStructureBuildSizesInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateAccelerationStructureKHR)(VkDevice device, const VkAccelerationStructureCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkAccelerationStructureKHR* pAccelerationStructure); +typedef void (VKAPI_PTR *PFN_vkDestroyAccelerationStructureKHR)(VkDevice device, VkAccelerationStructureKHR accelerationStructure, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkCmdBuildAccelerationStructuresKHR)(VkCommandBuffer commandBuffer, uint32_t infoCount, const VkAccelerationStructureBuildGeometryInfoKHR* pInfos, const VkAccelerationStructureBuildRangeInfoKHR* const* ppBuildRangeInfos); +typedef void (VKAPI_PTR *PFN_vkCmdBuildAccelerationStructuresIndirectKHR)(VkCommandBuffer commandBuffer, uint32_t infoCount, const VkAccelerationStructureBuildGeometryInfoKHR* pInfos, const VkDeviceAddress* pIndirectDeviceAddresses, const uint32_t* pIndirectStrides, const uint32_t* const* ppMaxPrimitiveCounts); +typedef VkResult (VKAPI_PTR *PFN_vkBuildAccelerationStructuresKHR)(VkDevice device, VkDeferredOperationKHR deferredOperation, uint32_t infoCount, const VkAccelerationStructureBuildGeometryInfoKHR* pInfos, const VkAccelerationStructureBuildRangeInfoKHR* const* ppBuildRangeInfos); +typedef VkResult (VKAPI_PTR *PFN_vkCopyAccelerationStructureKHR)(VkDevice device, VkDeferredOperationKHR deferredOperation, const VkCopyAccelerationStructureInfoKHR* pInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCopyAccelerationStructureToMemoryKHR)(VkDevice device, VkDeferredOperationKHR deferredOperation, const VkCopyAccelerationStructureToMemoryInfoKHR* pInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCopyMemoryToAccelerationStructureKHR)(VkDevice device, VkDeferredOperationKHR deferredOperation, const VkCopyMemoryToAccelerationStructureInfoKHR* pInfo); +typedef VkResult (VKAPI_PTR *PFN_vkWriteAccelerationStructuresPropertiesKHR)(VkDevice device, uint32_t accelerationStructureCount, const VkAccelerationStructureKHR* pAccelerationStructures, VkQueryType queryType, size_t dataSize, void* pData, size_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdCopyAccelerationStructureKHR)(VkCommandBuffer commandBuffer, const VkCopyAccelerationStructureInfoKHR* pInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyAccelerationStructureToMemoryKHR)(VkCommandBuffer commandBuffer, const VkCopyAccelerationStructureToMemoryInfoKHR* pInfo); +typedef void (VKAPI_PTR *PFN_vkCmdCopyMemoryToAccelerationStructureKHR)(VkCommandBuffer commandBuffer, const VkCopyMemoryToAccelerationStructureInfoKHR* pInfo); +typedef VkDeviceAddress (VKAPI_PTR *PFN_vkGetAccelerationStructureDeviceAddressKHR)(VkDevice device, const VkAccelerationStructureDeviceAddressInfoKHR* pInfo); +typedef void (VKAPI_PTR *PFN_vkCmdWriteAccelerationStructuresPropertiesKHR)(VkCommandBuffer commandBuffer, uint32_t accelerationStructureCount, const VkAccelerationStructureKHR* pAccelerationStructures, VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery); +typedef void (VKAPI_PTR *PFN_vkGetDeviceAccelerationStructureCompatibilityKHR)(VkDevice device, const VkAccelerationStructureVersionInfoKHR* pVersionInfo, VkAccelerationStructureCompatibilityKHR* pCompatibility); +typedef void (VKAPI_PTR *PFN_vkGetAccelerationStructureBuildSizesKHR)(VkDevice device, VkAccelerationStructureBuildTypeKHR buildType, const VkAccelerationStructureBuildGeometryInfoKHR* pBuildInfo, const uint32_t* pMaxPrimitiveCounts, VkAccelerationStructureBuildSizesInfoKHR* pSizeInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateAccelerationStructureKHR( + VkDevice device, + const VkAccelerationStructureCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkAccelerationStructureKHR* pAccelerationStructure); + +VKAPI_ATTR void VKAPI_CALL vkDestroyAccelerationStructureKHR( + VkDevice device, + VkAccelerationStructureKHR accelerationStructure, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkCmdBuildAccelerationStructuresKHR( + VkCommandBuffer commandBuffer, + uint32_t infoCount, + const VkAccelerationStructureBuildGeometryInfoKHR* pInfos, + const VkAccelerationStructureBuildRangeInfoKHR* const* ppBuildRangeInfos); + +VKAPI_ATTR void VKAPI_CALL vkCmdBuildAccelerationStructuresIndirectKHR( + VkCommandBuffer commandBuffer, + uint32_t infoCount, + const VkAccelerationStructureBuildGeometryInfoKHR* pInfos, + const VkDeviceAddress* pIndirectDeviceAddresses, + const uint32_t* pIndirectStrides, + const uint32_t* const* ppMaxPrimitiveCounts); + +VKAPI_ATTR VkResult VKAPI_CALL vkBuildAccelerationStructuresKHR( + VkDevice device, + VkDeferredOperationKHR deferredOperation, + uint32_t infoCount, + const VkAccelerationStructureBuildGeometryInfoKHR* pInfos, + const VkAccelerationStructureBuildRangeInfoKHR* const* ppBuildRangeInfos); + +VKAPI_ATTR VkResult VKAPI_CALL vkCopyAccelerationStructureKHR( + VkDevice device, + VkDeferredOperationKHR deferredOperation, + const VkCopyAccelerationStructureInfoKHR* pInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCopyAccelerationStructureToMemoryKHR( + VkDevice device, + VkDeferredOperationKHR deferredOperation, + const VkCopyAccelerationStructureToMemoryInfoKHR* pInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCopyMemoryToAccelerationStructureKHR( + VkDevice device, + VkDeferredOperationKHR deferredOperation, + const VkCopyMemoryToAccelerationStructureInfoKHR* pInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkWriteAccelerationStructuresPropertiesKHR( + VkDevice device, + uint32_t accelerationStructureCount, + const VkAccelerationStructureKHR* pAccelerationStructures, + VkQueryType queryType, + size_t dataSize, + void* pData, + size_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyAccelerationStructureKHR( + VkCommandBuffer commandBuffer, + const VkCopyAccelerationStructureInfoKHR* pInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyAccelerationStructureToMemoryKHR( + VkCommandBuffer commandBuffer, + const VkCopyAccelerationStructureToMemoryInfoKHR* pInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyMemoryToAccelerationStructureKHR( + VkCommandBuffer commandBuffer, + const VkCopyMemoryToAccelerationStructureInfoKHR* pInfo); + +VKAPI_ATTR VkDeviceAddress VKAPI_CALL vkGetAccelerationStructureDeviceAddressKHR( + VkDevice device, + const VkAccelerationStructureDeviceAddressInfoKHR* pInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdWriteAccelerationStructuresPropertiesKHR( + VkCommandBuffer commandBuffer, + uint32_t accelerationStructureCount, + const VkAccelerationStructureKHR* pAccelerationStructures, + VkQueryType queryType, + VkQueryPool queryPool, + uint32_t firstQuery); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceAccelerationStructureCompatibilityKHR( + VkDevice device, + const VkAccelerationStructureVersionInfoKHR* pVersionInfo, + VkAccelerationStructureCompatibilityKHR* pCompatibility); + +VKAPI_ATTR void VKAPI_CALL vkGetAccelerationStructureBuildSizesKHR( + VkDevice device, + VkAccelerationStructureBuildTypeKHR buildType, + const VkAccelerationStructureBuildGeometryInfoKHR* pBuildInfo, + const uint32_t* pMaxPrimitiveCounts, + VkAccelerationStructureBuildSizesInfoKHR* pSizeInfo); +#endif + + +#define VK_KHR_ray_tracing_pipeline 1 +#define VK_KHR_RAY_TRACING_PIPELINE_SPEC_VERSION 1 +#define VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME "VK_KHR_ray_tracing_pipeline" + +typedef enum VkShaderGroupShaderKHR { + VK_SHADER_GROUP_SHADER_GENERAL_KHR = 0, + VK_SHADER_GROUP_SHADER_CLOSEST_HIT_KHR = 1, + VK_SHADER_GROUP_SHADER_ANY_HIT_KHR = 2, + VK_SHADER_GROUP_SHADER_INTERSECTION_KHR = 3, + VK_SHADER_GROUP_SHADER_MAX_ENUM_KHR = 0x7FFFFFFF +} VkShaderGroupShaderKHR; +typedef struct VkRayTracingShaderGroupCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkRayTracingShaderGroupTypeKHR type; + uint32_t generalShader; + uint32_t closestHitShader; + uint32_t anyHitShader; + uint32_t intersectionShader; + const void* pShaderGroupCaptureReplayHandle; +} VkRayTracingShaderGroupCreateInfoKHR; + +typedef struct VkRayTracingPipelineInterfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t maxPipelineRayPayloadSize; + uint32_t maxPipelineRayHitAttributeSize; +} VkRayTracingPipelineInterfaceCreateInfoKHR; + +typedef struct VkRayTracingPipelineCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkPipelineCreateFlags flags; + uint32_t stageCount; + const VkPipelineShaderStageCreateInfo* pStages; + uint32_t groupCount; + const VkRayTracingShaderGroupCreateInfoKHR* pGroups; + uint32_t maxPipelineRayRecursionDepth; + const VkPipelineLibraryCreateInfoKHR* pLibraryInfo; + const VkRayTracingPipelineInterfaceCreateInfoKHR* pLibraryInterface; + const VkPipelineDynamicStateCreateInfo* pDynamicState; + VkPipelineLayout layout; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkRayTracingPipelineCreateInfoKHR; + +typedef struct VkPhysicalDeviceRayTracingPipelineFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 rayTracingPipeline; + VkBool32 rayTracingPipelineShaderGroupHandleCaptureReplay; + VkBool32 rayTracingPipelineShaderGroupHandleCaptureReplayMixed; + VkBool32 rayTracingPipelineTraceRaysIndirect; + VkBool32 rayTraversalPrimitiveCulling; +} VkPhysicalDeviceRayTracingPipelineFeaturesKHR; + +typedef struct VkPhysicalDeviceRayTracingPipelinePropertiesKHR { + VkStructureType sType; + void* pNext; + uint32_t shaderGroupHandleSize; + uint32_t maxRayRecursionDepth; + uint32_t maxShaderGroupStride; + uint32_t shaderGroupBaseAlignment; + uint32_t shaderGroupHandleCaptureReplaySize; + uint32_t maxRayDispatchInvocationCount; + uint32_t shaderGroupHandleAlignment; + uint32_t maxRayHitAttributeSize; +} VkPhysicalDeviceRayTracingPipelinePropertiesKHR; + +typedef struct VkStridedDeviceAddressRegionKHR { + VkDeviceAddress deviceAddress; + VkDeviceSize stride; + VkDeviceSize size; +} VkStridedDeviceAddressRegionKHR; + +typedef struct VkTraceRaysIndirectCommandKHR { + uint32_t width; + uint32_t height; + uint32_t depth; +} VkTraceRaysIndirectCommandKHR; + +typedef void (VKAPI_PTR *PFN_vkCmdTraceRaysKHR)(VkCommandBuffer commandBuffer, const VkStridedDeviceAddressRegionKHR* pRaygenShaderBindingTable, const VkStridedDeviceAddressRegionKHR* pMissShaderBindingTable, const VkStridedDeviceAddressRegionKHR* pHitShaderBindingTable, const VkStridedDeviceAddressRegionKHR* pCallableShaderBindingTable, uint32_t width, uint32_t height, uint32_t depth); +typedef VkResult (VKAPI_PTR *PFN_vkCreateRayTracingPipelinesKHR)(VkDevice device, VkDeferredOperationKHR deferredOperation, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkRayTracingPipelineCreateInfoKHR* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); +typedef VkResult (VKAPI_PTR *PFN_vkGetRayTracingCaptureReplayShaderGroupHandlesKHR)(VkDevice device, VkPipeline pipeline, uint32_t firstGroup, uint32_t groupCount, size_t dataSize, void* pData); +typedef void (VKAPI_PTR *PFN_vkCmdTraceRaysIndirectKHR)(VkCommandBuffer commandBuffer, const VkStridedDeviceAddressRegionKHR* pRaygenShaderBindingTable, const VkStridedDeviceAddressRegionKHR* pMissShaderBindingTable, const VkStridedDeviceAddressRegionKHR* pHitShaderBindingTable, const VkStridedDeviceAddressRegionKHR* pCallableShaderBindingTable, VkDeviceAddress indirectDeviceAddress); +typedef VkDeviceSize (VKAPI_PTR *PFN_vkGetRayTracingShaderGroupStackSizeKHR)(VkDevice device, VkPipeline pipeline, uint32_t group, VkShaderGroupShaderKHR groupShader); +typedef void (VKAPI_PTR *PFN_vkCmdSetRayTracingPipelineStackSizeKHR)(VkCommandBuffer commandBuffer, uint32_t pipelineStackSize); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdTraceRaysKHR( + VkCommandBuffer commandBuffer, + const VkStridedDeviceAddressRegionKHR* pRaygenShaderBindingTable, + const VkStridedDeviceAddressRegionKHR* pMissShaderBindingTable, + const VkStridedDeviceAddressRegionKHR* pHitShaderBindingTable, + const VkStridedDeviceAddressRegionKHR* pCallableShaderBindingTable, + uint32_t width, + uint32_t height, + uint32_t depth); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateRayTracingPipelinesKHR( + VkDevice device, + VkDeferredOperationKHR deferredOperation, + VkPipelineCache pipelineCache, + uint32_t createInfoCount, + const VkRayTracingPipelineCreateInfoKHR* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetRayTracingCaptureReplayShaderGroupHandlesKHR( + VkDevice device, + VkPipeline pipeline, + uint32_t firstGroup, + uint32_t groupCount, + size_t dataSize, + void* pData); + +VKAPI_ATTR void VKAPI_CALL vkCmdTraceRaysIndirectKHR( + VkCommandBuffer commandBuffer, + const VkStridedDeviceAddressRegionKHR* pRaygenShaderBindingTable, + const VkStridedDeviceAddressRegionKHR* pMissShaderBindingTable, + const VkStridedDeviceAddressRegionKHR* pHitShaderBindingTable, + const VkStridedDeviceAddressRegionKHR* pCallableShaderBindingTable, + VkDeviceAddress indirectDeviceAddress); + +VKAPI_ATTR VkDeviceSize VKAPI_CALL vkGetRayTracingShaderGroupStackSizeKHR( + VkDevice device, + VkPipeline pipeline, + uint32_t group, + VkShaderGroupShaderKHR groupShader); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetRayTracingPipelineStackSizeKHR( + VkCommandBuffer commandBuffer, + uint32_t pipelineStackSize); +#endif + + +#define VK_KHR_ray_query 1 +#define VK_KHR_RAY_QUERY_SPEC_VERSION 1 +#define VK_KHR_RAY_QUERY_EXTENSION_NAME "VK_KHR_ray_query" +typedef struct VkPhysicalDeviceRayQueryFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 rayQuery; +} VkPhysicalDeviceRayQueryFeaturesKHR; + + #ifdef __cplusplus } #endif diff --git a/third_party/vulkan/vulkan_directfb.h b/third_party/vulkan/vulkan_directfb.h new file mode 100644 index 000000000..8eaac6e48 --- /dev/null +++ b/third_party/vulkan/vulkan_directfb.h @@ -0,0 +1,54 @@ +#ifndef VULKAN_DIRECTFB_H_ +#define VULKAN_DIRECTFB_H_ 1 + +/* +** Copyright 2015-2021 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_EXT_directfb_surface 1 +#define VK_EXT_DIRECTFB_SURFACE_SPEC_VERSION 1 +#define VK_EXT_DIRECTFB_SURFACE_EXTENSION_NAME "VK_EXT_directfb_surface" +typedef VkFlags VkDirectFBSurfaceCreateFlagsEXT; +typedef struct VkDirectFBSurfaceCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDirectFBSurfaceCreateFlagsEXT flags; + IDirectFB* dfb; + IDirectFBSurface* surface; +} VkDirectFBSurfaceCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateDirectFBSurfaceEXT)(VkInstance instance, const VkDirectFBSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceDirectFBPresentationSupportEXT)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, IDirectFB* dfb); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDirectFBSurfaceEXT( + VkInstance instance, + const VkDirectFBSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceDirectFBPresentationSupportEXT( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + IDirectFB* dfb); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/vulkan/vulkan_fuchsia.h b/third_party/vulkan/vulkan_fuchsia.h new file mode 100644 index 000000000..44b4ace3e --- /dev/null +++ b/third_party/vulkan/vulkan_fuchsia.h @@ -0,0 +1,258 @@ +#ifndef VULKAN_FUCHSIA_H_ +#define VULKAN_FUCHSIA_H_ 1 + +/* +** Copyright 2015-2021 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_FUCHSIA_imagepipe_surface 1 +#define VK_FUCHSIA_IMAGEPIPE_SURFACE_SPEC_VERSION 1 +#define VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME "VK_FUCHSIA_imagepipe_surface" +typedef VkFlags VkImagePipeSurfaceCreateFlagsFUCHSIA; +typedef struct VkImagePipeSurfaceCreateInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkImagePipeSurfaceCreateFlagsFUCHSIA flags; + zx_handle_t imagePipeHandle; +} VkImagePipeSurfaceCreateInfoFUCHSIA; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateImagePipeSurfaceFUCHSIA)(VkInstance instance, const VkImagePipeSurfaceCreateInfoFUCHSIA* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateImagePipeSurfaceFUCHSIA( + VkInstance instance, + const VkImagePipeSurfaceCreateInfoFUCHSIA* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + + +#define VK_FUCHSIA_external_memory 1 +#define VK_FUCHSIA_EXTERNAL_MEMORY_SPEC_VERSION 1 +#define VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME "VK_FUCHSIA_external_memory" +typedef struct VkImportMemoryZirconHandleInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagBits handleType; + zx_handle_t handle; +} VkImportMemoryZirconHandleInfoFUCHSIA; + +typedef struct VkMemoryZirconHandlePropertiesFUCHSIA { + VkStructureType sType; + void* pNext; + uint32_t memoryTypeBits; +} VkMemoryZirconHandlePropertiesFUCHSIA; + +typedef struct VkMemoryGetZirconHandleInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; + VkExternalMemoryHandleTypeFlagBits handleType; +} VkMemoryGetZirconHandleInfoFUCHSIA; + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryZirconHandleFUCHSIA)(VkDevice device, const VkMemoryGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, zx_handle_t* pZirconHandle); +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, zx_handle_t zirconHandle, VkMemoryZirconHandlePropertiesFUCHSIA* pMemoryZirconHandleProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryZirconHandleFUCHSIA( + VkDevice device, + const VkMemoryGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, + zx_handle_t* pZirconHandle); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryZirconHandlePropertiesFUCHSIA( + VkDevice device, + VkExternalMemoryHandleTypeFlagBits handleType, + zx_handle_t zirconHandle, + VkMemoryZirconHandlePropertiesFUCHSIA* pMemoryZirconHandleProperties); +#endif + + +#define VK_FUCHSIA_external_semaphore 1 +#define VK_FUCHSIA_EXTERNAL_SEMAPHORE_SPEC_VERSION 1 +#define VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_FUCHSIA_external_semaphore" +typedef struct VkImportSemaphoreZirconHandleInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkSemaphoreImportFlags flags; + VkExternalSemaphoreHandleTypeFlagBits handleType; + zx_handle_t zirconHandle; +} VkImportSemaphoreZirconHandleInfoFUCHSIA; + +typedef struct VkSemaphoreGetZirconHandleInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkExternalSemaphoreHandleTypeFlagBits handleType; +} VkSemaphoreGetZirconHandleInfoFUCHSIA; + +typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreZirconHandleFUCHSIA)(VkDevice device, const VkImportSemaphoreZirconHandleInfoFUCHSIA* pImportSemaphoreZirconHandleInfo); +typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreZirconHandleFUCHSIA)(VkDevice device, const VkSemaphoreGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, zx_handle_t* pZirconHandle); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreZirconHandleFUCHSIA( + VkDevice device, + const VkImportSemaphoreZirconHandleInfoFUCHSIA* pImportSemaphoreZirconHandleInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreZirconHandleFUCHSIA( + VkDevice device, + const VkSemaphoreGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, + zx_handle_t* pZirconHandle); +#endif + + +#define VK_FUCHSIA_buffer_collection 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferCollectionFUCHSIA) +#define VK_FUCHSIA_BUFFER_COLLECTION_SPEC_VERSION 2 +#define VK_FUCHSIA_BUFFER_COLLECTION_EXTENSION_NAME "VK_FUCHSIA_buffer_collection" +typedef VkFlags VkImageFormatConstraintsFlagsFUCHSIA; + +typedef enum VkImageConstraintsInfoFlagBitsFUCHSIA { + VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_RARELY_FUCHSIA = 0x00000001, + VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_OFTEN_FUCHSIA = 0x00000002, + VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_RARELY_FUCHSIA = 0x00000004, + VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_OFTEN_FUCHSIA = 0x00000008, + VK_IMAGE_CONSTRAINTS_INFO_PROTECTED_OPTIONAL_FUCHSIA = 0x00000010, + VK_IMAGE_CONSTRAINTS_INFO_FLAG_BITS_MAX_ENUM_FUCHSIA = 0x7FFFFFFF +} VkImageConstraintsInfoFlagBitsFUCHSIA; +typedef VkFlags VkImageConstraintsInfoFlagsFUCHSIA; +typedef struct VkBufferCollectionCreateInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + zx_handle_t collectionToken; +} VkBufferCollectionCreateInfoFUCHSIA; + +typedef struct VkImportMemoryBufferCollectionFUCHSIA { + VkStructureType sType; + const void* pNext; + VkBufferCollectionFUCHSIA collection; + uint32_t index; +} VkImportMemoryBufferCollectionFUCHSIA; + +typedef struct VkBufferCollectionImageCreateInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkBufferCollectionFUCHSIA collection; + uint32_t index; +} VkBufferCollectionImageCreateInfoFUCHSIA; + +typedef struct VkBufferCollectionConstraintsInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + uint32_t minBufferCount; + uint32_t maxBufferCount; + uint32_t minBufferCountForCamping; + uint32_t minBufferCountForDedicatedSlack; + uint32_t minBufferCountForSharedSlack; +} VkBufferCollectionConstraintsInfoFUCHSIA; + +typedef struct VkBufferConstraintsInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkBufferCreateInfo createInfo; + VkFormatFeatureFlags requiredFormatFeatures; + VkBufferCollectionConstraintsInfoFUCHSIA bufferCollectionConstraints; +} VkBufferConstraintsInfoFUCHSIA; + +typedef struct VkBufferCollectionBufferCreateInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkBufferCollectionFUCHSIA collection; + uint32_t index; +} VkBufferCollectionBufferCreateInfoFUCHSIA; + +typedef struct VkSysmemColorSpaceFUCHSIA { + VkStructureType sType; + const void* pNext; + uint32_t colorSpace; +} VkSysmemColorSpaceFUCHSIA; + +typedef struct VkBufferCollectionPropertiesFUCHSIA { + VkStructureType sType; + void* pNext; + uint32_t memoryTypeBits; + uint32_t bufferCount; + uint32_t createInfoIndex; + uint64_t sysmemPixelFormat; + VkFormatFeatureFlags formatFeatures; + VkSysmemColorSpaceFUCHSIA sysmemColorSpaceIndex; + VkComponentMapping samplerYcbcrConversionComponents; + VkSamplerYcbcrModelConversion suggestedYcbcrModel; + VkSamplerYcbcrRange suggestedYcbcrRange; + VkChromaLocation suggestedXChromaOffset; + VkChromaLocation suggestedYChromaOffset; +} VkBufferCollectionPropertiesFUCHSIA; + +typedef struct VkImageFormatConstraintsInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkImageCreateInfo imageCreateInfo; + VkFormatFeatureFlags requiredFormatFeatures; + VkImageFormatConstraintsFlagsFUCHSIA flags; + uint64_t sysmemPixelFormat; + uint32_t colorSpaceCount; + const VkSysmemColorSpaceFUCHSIA* pColorSpaces; +} VkImageFormatConstraintsInfoFUCHSIA; + +typedef struct VkImageConstraintsInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + uint32_t formatConstraintsCount; + const VkImageFormatConstraintsInfoFUCHSIA* pFormatConstraints; + VkBufferCollectionConstraintsInfoFUCHSIA bufferCollectionConstraints; + VkImageConstraintsInfoFlagsFUCHSIA flags; +} VkImageConstraintsInfoFUCHSIA; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateBufferCollectionFUCHSIA)(VkDevice device, const VkBufferCollectionCreateInfoFUCHSIA* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferCollectionFUCHSIA* pCollection); +typedef VkResult (VKAPI_PTR *PFN_vkSetBufferCollectionImageConstraintsFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo); +typedef VkResult (VKAPI_PTR *PFN_vkSetBufferCollectionBufferConstraintsFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo); +typedef void (VKAPI_PTR *PFN_vkDestroyBufferCollectionFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetBufferCollectionPropertiesFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, VkBufferCollectionPropertiesFUCHSIA* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferCollectionFUCHSIA( + VkDevice device, + const VkBufferCollectionCreateInfoFUCHSIA* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBufferCollectionFUCHSIA* pCollection); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetBufferCollectionImageConstraintsFUCHSIA( + VkDevice device, + VkBufferCollectionFUCHSIA collection, + const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetBufferCollectionBufferConstraintsFUCHSIA( + VkDevice device, + VkBufferCollectionFUCHSIA collection, + const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo); + +VKAPI_ATTR void VKAPI_CALL vkDestroyBufferCollectionFUCHSIA( + VkDevice device, + VkBufferCollectionFUCHSIA collection, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetBufferCollectionPropertiesFUCHSIA( + VkDevice device, + VkBufferCollectionFUCHSIA collection, + VkBufferCollectionPropertiesFUCHSIA* pProperties); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/vulkan/vulkan_ggp.h b/third_party/vulkan/vulkan_ggp.h new file mode 100644 index 000000000..9a6a582c5 --- /dev/null +++ b/third_party/vulkan/vulkan_ggp.h @@ -0,0 +1,58 @@ +#ifndef VULKAN_GGP_H_ +#define VULKAN_GGP_H_ 1 + +/* +** Copyright 2015-2021 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_GGP_stream_descriptor_surface 1 +#define VK_GGP_STREAM_DESCRIPTOR_SURFACE_SPEC_VERSION 1 +#define VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME "VK_GGP_stream_descriptor_surface" +typedef VkFlags VkStreamDescriptorSurfaceCreateFlagsGGP; +typedef struct VkStreamDescriptorSurfaceCreateInfoGGP { + VkStructureType sType; + const void* pNext; + VkStreamDescriptorSurfaceCreateFlagsGGP flags; + GgpStreamDescriptor streamDescriptor; +} VkStreamDescriptorSurfaceCreateInfoGGP; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateStreamDescriptorSurfaceGGP)(VkInstance instance, const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateStreamDescriptorSurfaceGGP( + VkInstance instance, + const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + + +#define VK_GGP_frame_token 1 +#define VK_GGP_FRAME_TOKEN_SPEC_VERSION 1 +#define VK_GGP_FRAME_TOKEN_EXTENSION_NAME "VK_GGP_frame_token" +typedef struct VkPresentFrameTokenGGP { + VkStructureType sType; + const void* pNext; + GgpFrameToken frameToken; +} VkPresentFrameTokenGGP; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/vulkan/vulkan_ios.h b/third_party/vulkan/vulkan_ios.h index a0924816d..6e7e6afea 100644 --- a/third_party/vulkan/vulkan_ios.h +++ b/third_party/vulkan/vulkan_ios.h @@ -1,24 +1,10 @@ #ifndef VULKAN_IOS_H_ #define VULKAN_IOS_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,12 +13,16 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_MVK_ios_surface 1 -#define VK_MVK_IOS_SURFACE_SPEC_VERSION 2 +#define VK_MVK_IOS_SURFACE_SPEC_VERSION 3 #define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface" - typedef VkFlags VkIOSSurfaceCreateFlagsMVK; - typedef struct VkIOSSurfaceCreateInfoMVK { VkStructureType sType; const void* pNext; @@ -40,7 +30,6 @@ typedef struct VkIOSSurfaceCreateInfoMVK { const void* pView; } VkIOSSurfaceCreateInfoMVK; - typedef VkResult (VKAPI_PTR *PFN_vkCreateIOSSurfaceMVK)(VkInstance instance, const VkIOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES diff --git a/third_party/vulkan/vulkan_macos.h b/third_party/vulkan/vulkan_macos.h index ff0b70180..c49b123d0 100644 --- a/third_party/vulkan/vulkan_macos.h +++ b/third_party/vulkan/vulkan_macos.h @@ -1,24 +1,10 @@ #ifndef VULKAN_MACOS_H_ #define VULKAN_MACOS_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,12 +13,16 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_MVK_macos_surface 1 -#define VK_MVK_MACOS_SURFACE_SPEC_VERSION 2 +#define VK_MVK_MACOS_SURFACE_SPEC_VERSION 3 #define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface" - typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; - typedef struct VkMacOSSurfaceCreateInfoMVK { VkStructureType sType; const void* pNext; @@ -40,7 +30,6 @@ typedef struct VkMacOSSurfaceCreateInfoMVK { const void* pView; } VkMacOSSurfaceCreateInfoMVK; - typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES diff --git a/third_party/vulkan/vulkan_metal.h b/third_party/vulkan/vulkan_metal.h new file mode 100644 index 000000000..5cf4a703a --- /dev/null +++ b/third_party/vulkan/vulkan_metal.h @@ -0,0 +1,54 @@ +#ifndef VULKAN_METAL_H_ +#define VULKAN_METAL_H_ 1 + +/* +** Copyright 2015-2021 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_EXT_metal_surface 1 + +#ifdef __OBJC__ +@class CAMetalLayer; +#else +typedef void CAMetalLayer; +#endif + +#define VK_EXT_METAL_SURFACE_SPEC_VERSION 1 +#define VK_EXT_METAL_SURFACE_EXTENSION_NAME "VK_EXT_metal_surface" +typedef VkFlags VkMetalSurfaceCreateFlagsEXT; +typedef struct VkMetalSurfaceCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkMetalSurfaceCreateFlagsEXT flags; + const CAMetalLayer* pLayer; +} VkMetalSurfaceCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateMetalSurfaceEXT)(VkInstance instance, const VkMetalSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateMetalSurfaceEXT( + VkInstance instance, + const VkMetalSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/vulkan/vulkan_mir.h b/third_party/vulkan/vulkan_mir.h deleted file mode 100644 index 7d24ed27a..000000000 --- a/third_party/vulkan/vulkan_mir.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef VULKAN_MIR_H_ -#define VULKAN_MIR_H_ 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* -** Copyright (c) 2015-2018 The Khronos Group Inc. -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -/* -** This header is generated from the Khronos Vulkan XML API Registry. -** -*/ - - -#define VK_KHR_mir_surface 1 -#define VK_KHR_MIR_SURFACE_SPEC_VERSION 4 -#define VK_KHR_MIR_SURFACE_EXTENSION_NAME "VK_KHR_mir_surface" - -typedef VkFlags VkMirSurfaceCreateFlagsKHR; - -typedef struct VkMirSurfaceCreateInfoKHR { - VkStructureType sType; - const void* pNext; - VkMirSurfaceCreateFlagsKHR flags; - MirConnection* connection; - MirSurface* mirSurface; -} VkMirSurfaceCreateInfoKHR; - - -typedef VkResult (VKAPI_PTR *PFN_vkCreateMirSurfaceKHR)(VkInstance instance, const VkMirSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); -typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, MirConnection* connection); - -#ifndef VK_NO_PROTOTYPES -VKAPI_ATTR VkResult VKAPI_CALL vkCreateMirSurfaceKHR( - VkInstance instance, - const VkMirSurfaceCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface); - -VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceMirPresentationSupportKHR( - VkPhysicalDevice physicalDevice, - uint32_t queueFamilyIndex, - MirConnection* connection); -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/third_party/vulkan/vulkan_screen.h b/third_party/vulkan/vulkan_screen.h new file mode 100644 index 000000000..92ad9bfab --- /dev/null +++ b/third_party/vulkan/vulkan_screen.h @@ -0,0 +1,54 @@ +#ifndef VULKAN_SCREEN_H_ +#define VULKAN_SCREEN_H_ 1 + +/* +** Copyright 2015-2021 The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_QNX_screen_surface 1 +#define VK_QNX_SCREEN_SURFACE_SPEC_VERSION 1 +#define VK_QNX_SCREEN_SURFACE_EXTENSION_NAME "VK_QNX_screen_surface" +typedef VkFlags VkScreenSurfaceCreateFlagsQNX; +typedef struct VkScreenSurfaceCreateInfoQNX { + VkStructureType sType; + const void* pNext; + VkScreenSurfaceCreateFlagsQNX flags; + struct _screen_context* context; + struct _screen_window* window; +} VkScreenSurfaceCreateInfoQNX; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateScreenSurfaceQNX)(VkInstance instance, const VkScreenSurfaceCreateInfoQNX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceScreenPresentationSupportQNX)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct _screen_window* window); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateScreenSurfaceQNX( + VkInstance instance, + const VkScreenSurfaceCreateInfoQNX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceScreenPresentationSupportQNX( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + struct _screen_window* window); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/vulkan/vulkan_vi.h b/third_party/vulkan/vulkan_vi.h index 015166bfc..9e0dcca20 100644 --- a/third_party/vulkan/vulkan_vi.h +++ b/third_party/vulkan/vulkan_vi.h @@ -1,24 +1,10 @@ #ifndef VULKAN_VI_H_ #define VULKAN_VI_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,12 +13,16 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_NN_vi_surface 1 #define VK_NN_VI_SURFACE_SPEC_VERSION 1 #define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface" - typedef VkFlags VkViSurfaceCreateFlagsNN; - typedef struct VkViSurfaceCreateInfoNN { VkStructureType sType; const void* pNext; @@ -40,7 +30,6 @@ typedef struct VkViSurfaceCreateInfoNN { void* window; } VkViSurfaceCreateInfoNN; - typedef VkResult (VKAPI_PTR *PFN_vkCreateViSurfaceNN)(VkInstance instance, const VkViSurfaceCreateInfoNN* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #ifndef VK_NO_PROTOTYPES diff --git a/third_party/vulkan/vulkan_wayland.h b/third_party/vulkan/vulkan_wayland.h index 5ba0827aa..2a329be9d 100644 --- a/third_party/vulkan/vulkan_wayland.h +++ b/third_party/vulkan/vulkan_wayland.h @@ -1,24 +1,10 @@ #ifndef VULKAN_WAYLAND_H_ #define VULKAN_WAYLAND_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,12 +13,16 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_KHR_wayland_surface 1 #define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6 #define VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME "VK_KHR_wayland_surface" - typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; - typedef struct VkWaylandSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; @@ -41,7 +31,6 @@ typedef struct VkWaylandSurfaceCreateInfoKHR { struct wl_surface* surface; } VkWaylandSurfaceCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateWaylandSurfaceKHR)(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct wl_display* display); diff --git a/third_party/vulkan/vulkan_win32.h b/third_party/vulkan/vulkan_win32.h index 6a85409eb..1b680f0b1 100644 --- a/third_party/vulkan/vulkan_win32.h +++ b/third_party/vulkan/vulkan_win32.h @@ -1,24 +1,10 @@ #ifndef VULKAN_WIN32_H_ #define VULKAN_WIN32_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,12 +13,16 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_KHR_win32_surface 1 #define VK_KHR_WIN32_SURFACE_SPEC_VERSION 6 #define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface" - typedef VkFlags VkWin32SurfaceCreateFlagsKHR; - typedef struct VkWin32SurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; @@ -41,7 +31,6 @@ typedef struct VkWin32SurfaceCreateInfoKHR { HWND hwnd; } VkWin32SurfaceCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex); @@ -57,10 +46,10 @@ VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR( uint32_t queueFamilyIndex); #endif + #define VK_KHR_external_memory_win32 1 #define VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHR_external_memory_win32" - typedef struct VkImportMemoryWin32HandleInfoKHR { VkStructureType sType; const void* pNext; @@ -90,7 +79,6 @@ typedef struct VkMemoryGetWin32HandleInfoKHR { VkExternalMemoryHandleTypeFlagBits handleType; } VkMemoryGetWin32HandleInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleKHR)(VkDevice device, const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle); typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandlePropertiesKHR)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties); @@ -107,10 +95,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHR( VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties); #endif + #define VK_KHR_win32_keyed_mutex 1 #define VK_KHR_WIN32_KEYED_MUTEX_SPEC_VERSION 1 #define VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHR_win32_keyed_mutex" - typedef struct VkWin32KeyedMutexAcquireReleaseInfoKHR { VkStructureType sType; const void* pNext; @@ -128,7 +116,6 @@ typedef struct VkWin32KeyedMutexAcquireReleaseInfoKHR { #define VK_KHR_external_semaphore_win32 1 #define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHR_external_semaphore_win32" - typedef struct VkImportSemaphoreWin32HandleInfoKHR { VkStructureType sType; const void* pNext; @@ -163,7 +150,6 @@ typedef struct VkSemaphoreGetWin32HandleInfoKHR { VkExternalSemaphoreHandleTypeFlagBits handleType; } VkSemaphoreGetWin32HandleInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreWin32HandleKHR)(VkDevice device, const VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo); typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreWin32HandleKHR)(VkDevice device, const VkSemaphoreGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle); @@ -178,10 +164,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreWin32HandleKHR( HANDLE* pHandle); #endif + #define VK_KHR_external_fence_win32 1 #define VK_KHR_EXTERNAL_FENCE_WIN32_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME "VK_KHR_external_fence_win32" - typedef struct VkImportFenceWin32HandleInfoKHR { VkStructureType sType; const void* pNext; @@ -207,7 +193,6 @@ typedef struct VkFenceGetWin32HandleInfoKHR { VkExternalFenceHandleTypeFlagBits handleType; } VkFenceGetWin32HandleInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkImportFenceWin32HandleKHR)(VkDevice device, const VkImportFenceWin32HandleInfoKHR* pImportFenceWin32HandleInfo); typedef VkResult (VKAPI_PTR *PFN_vkGetFenceWin32HandleKHR)(VkDevice device, const VkFenceGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle); @@ -222,10 +207,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceWin32HandleKHR( HANDLE* pHandle); #endif + #define VK_NV_external_memory_win32 1 #define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 #define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32" - typedef struct VkImportMemoryWin32HandleInfoNV { VkStructureType sType; const void* pNext; @@ -240,7 +225,6 @@ typedef struct VkExportMemoryWin32HandleInfoNV { DWORD dwAccess; } VkExportMemoryWin32HandleInfoNV; - typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle); #ifndef VK_NO_PROTOTYPES @@ -251,10 +235,10 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV( HANDLE* pHandle); #endif -#define VK_NV_win32_keyed_mutex 1 -#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1 -#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex" +#define VK_NV_win32_keyed_mutex 1 +#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 2 +#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex" typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV { VkStructureType sType; const void* pNext; @@ -269,6 +253,61 @@ typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV { +#define VK_EXT_full_screen_exclusive 1 +#define VK_EXT_FULL_SCREEN_EXCLUSIVE_SPEC_VERSION 4 +#define VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME "VK_EXT_full_screen_exclusive" + +typedef enum VkFullScreenExclusiveEXT { + VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT = 0, + VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT = 1, + VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT = 2, + VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT = 3, + VK_FULL_SCREEN_EXCLUSIVE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkFullScreenExclusiveEXT; +typedef struct VkSurfaceFullScreenExclusiveInfoEXT { + VkStructureType sType; + void* pNext; + VkFullScreenExclusiveEXT fullScreenExclusive; +} VkSurfaceFullScreenExclusiveInfoEXT; + +typedef struct VkSurfaceCapabilitiesFullScreenExclusiveEXT { + VkStructureType sType; + void* pNext; + VkBool32 fullScreenExclusiveSupported; +} VkSurfaceCapabilitiesFullScreenExclusiveEXT; + +typedef struct VkSurfaceFullScreenExclusiveWin32InfoEXT { + VkStructureType sType; + const void* pNext; + HMONITOR hmonitor; +} VkSurfaceFullScreenExclusiveWin32InfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModes2EXT)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes); +typedef VkResult (VKAPI_PTR *PFN_vkAcquireFullScreenExclusiveModeEXT)(VkDevice device, VkSwapchainKHR swapchain); +typedef VkResult (VKAPI_PTR *PFN_vkReleaseFullScreenExclusiveModeEXT)(VkDevice device, VkSwapchainKHR swapchain); +typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModes2EXT)(VkDevice device, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkDeviceGroupPresentModeFlagsKHR* pModes); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModes2EXT( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, + uint32_t* pPresentModeCount, + VkPresentModeKHR* pPresentModes); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireFullScreenExclusiveModeEXT( + VkDevice device, + VkSwapchainKHR swapchain); + +VKAPI_ATTR VkResult VKAPI_CALL vkReleaseFullScreenExclusiveModeEXT( + VkDevice device, + VkSwapchainKHR swapchain); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModes2EXT( + VkDevice device, + const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, + VkDeviceGroupPresentModeFlagsKHR* pModes); +#endif + #ifdef __cplusplus } #endif diff --git a/third_party/vulkan/vulkan_xcb.h b/third_party/vulkan/vulkan_xcb.h index ba0360060..5ba2ad850 100644 --- a/third_party/vulkan/vulkan_xcb.h +++ b/third_party/vulkan/vulkan_xcb.h @@ -1,24 +1,10 @@ #ifndef VULKAN_XCB_H_ #define VULKAN_XCB_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,12 +13,16 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_KHR_xcb_surface 1 #define VK_KHR_XCB_SURFACE_SPEC_VERSION 6 #define VK_KHR_XCB_SURFACE_EXTENSION_NAME "VK_KHR_xcb_surface" - typedef VkFlags VkXcbSurfaceCreateFlagsKHR; - typedef struct VkXcbSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; @@ -41,7 +31,6 @@ typedef struct VkXcbSurfaceCreateInfoKHR { xcb_window_t window; } VkXcbSurfaceCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateXcbSurfaceKHR)(VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id); diff --git a/third_party/vulkan/vulkan_xlib.h b/third_party/vulkan/vulkan_xlib.h index e1d967e01..75c75dc2e 100644 --- a/third_party/vulkan/vulkan_xlib.h +++ b/third_party/vulkan/vulkan_xlib.h @@ -1,24 +1,10 @@ #ifndef VULKAN_XLIB_H_ #define VULKAN_XLIB_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,12 +13,16 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_KHR_xlib_surface 1 #define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6 #define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface" - typedef VkFlags VkXlibSurfaceCreateFlagsKHR; - typedef struct VkXlibSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; @@ -41,7 +31,6 @@ typedef struct VkXlibSurfaceCreateInfoKHR { Window window; } VkXlibSurfaceCreateInfoKHR; - typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display* dpy, VisualID visualID); diff --git a/third_party/vulkan/vulkan_xlib_xrandr.h b/third_party/vulkan/vulkan_xlib_xrandr.h index 117d01799..fa2749342 100644 --- a/third_party/vulkan/vulkan_xlib_xrandr.h +++ b/third_party/vulkan/vulkan_xlib_xrandr.h @@ -1,24 +1,10 @@ #ifndef VULKAN_XLIB_XRANDR_H_ #define VULKAN_XLIB_XRANDR_H_ 1 -#ifdef __cplusplus -extern "C" { -#endif - /* -** Copyright (c) 2015-2018 The Khronos Group Inc. +** Copyright 2015-2021 The Khronos Group Inc. ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. +** SPDX-License-Identifier: Apache-2.0 */ /* @@ -27,10 +13,15 @@ extern "C" { */ +#ifdef __cplusplus +extern "C" { +#endif + + + #define VK_EXT_acquire_xlib_display 1 #define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1 #define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display" - typedef VkResult (VKAPI_PTR *PFN_vkAcquireXlibDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, VkDisplayKHR display); typedef VkResult (VKAPI_PTR *PFN_vkGetRandROutputDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, RROutput rrOutput, VkDisplayKHR* pDisplay); diff --git a/xenia-build b/xenia-build index 2efb2400e..a37f76d8e 100755 --- a/xenia-build +++ b/xenia-build @@ -902,7 +902,8 @@ class BuildShadersCommand(Command): if sys.platform == 'win32': print('Building Direct3D 12 Shader Model 5.1 DXBC shaders...') windows_sdk_bin_path = os.path.join( - os.environ['ProgramFiles(x86)'], 'Windows Kits/10/bin/x64') + os.environ['ProgramFiles(x86)'], + 'Windows Kits/10/bin/10.0.19041.0/x64') fxc = os.path.join(windows_sdk_bin_path, 'fxc') # Ensure we have the tools. if not os.path.exists(windows_sdk_bin_path): @@ -921,12 +922,16 @@ class BuildShadersCommand(Command): os.makedirs(dxbc_dir_path, exist_ok=True) dxbc_file_path_base = os.path.join(dxbc_dir_path, dxbc_identifier) + # Not enabling treating warnings as errors (/WX) because it + # overrides #pragma warning, and the FXAA shader triggers a + # bug in FXC causing an uninitialized variable warning if + # early exit from a function is done. if subprocess.call([ fxc, + '/D', 'XESL_LANGUAGE_HLSL=1', '/Fh', dxbc_file_path_base + '.h', '/T', dxbc_identifier[-2:] + '_5_1', '/Vn', dxbc_identifier, - '/WX', '/nologo', src_path ], stdout=subprocess.DEVNULL): @@ -966,7 +971,6 @@ class GenSpirvCommand(Command): vulkan_bin_path = os.path.join(vulkan_sdk_path, 'bin') glslang = os.path.join(vulkan_bin_path, 'glslangValidator') spirv_dis = os.path.join(vulkan_bin_path, 'spirv-dis') - spirv_remap = os.path.join(vulkan_bin_path, 'spirv-remap') # Ensure we have the tools. if not os.path.exists(vulkan_sdk_path): @@ -978,9 +982,6 @@ class GenSpirvCommand(Command): elif not has_bin(spirv_dis): print('ERROR: could not find spirv-dis') return 1 - elif not has_bin(spirv_remap): - print('ERROR: could not find spirv-remap') - return 1 src_files = [os.path.join(root, name) for root, dirs, files in os.walk('src') @@ -994,7 +995,8 @@ class GenSpirvCommand(Command): src_name = os.path.splitext(os.path.basename(src_file))[0] identifier = os.path.basename(src_file).replace('.', '_') - bin_path = os.path.join(os.path.dirname(src_file), 'bin') + bin_path = os.path.join(os.path.dirname(src_file), + 'bytecode/vulkan_spirv') spv_file = os.path.join(bin_path, identifier) + '.spv' txt_file = os.path.join(bin_path, identifier) + '.txt' h_file = os.path.join(bin_path, identifier) + '.h' @@ -1002,6 +1004,7 @@ class GenSpirvCommand(Command): # GLSL source -> .spv binary shell_call([ glslang, + '-DXESL_LANGUAGE_GLSL=1', '-Os', '-V', src_file, '-o', spv_file, From 22eb8747d345ecc27d409399d3556d0d5c08c48c Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sat, 29 Jan 2022 14:02:55 +0300 Subject: [PATCH 71/88] [GPU/Kernel] Fix space-prefixed hexadecimal number printing --- src/xenia/gpu/command_processor.cc | 4 ++-- src/xenia/gpu/primitive_processor.cc | 4 ++-- src/xenia/kernel/xam/content_manager.cc | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/xenia/gpu/command_processor.cc b/src/xenia/gpu/command_processor.cc index 07ff7f070..7a4ac4e23 100644 --- a/src/xenia/gpu/command_processor.cc +++ b/src/xenia/gpu/command_processor.cc @@ -402,7 +402,7 @@ uint32_t CommandProcessor::ExecutePrimaryBuffer(uint32_t read_index, uint32_t title_id = kernel_state_->GetExecutableModule() ? kernel_state_->GetExecutableModule()->title_id() : 0; - auto file_name = fmt::format("{:8X}_stream.xtr", title_id); + auto file_name = fmt::format("{:08X}_stream.xtr", title_id); auto path = trace_stream_path_ / file_name; trace_writer_.Open(path, title_id); InitializeTrace(); @@ -729,7 +729,7 @@ bool CommandProcessor::ExecutePacketType3(RingBuffer* reader, uint32_t packet) { } else if (trace_state_ == TraceState::kSingleFrame) { // New trace request - we only start tracing at the beginning of a frame. uint32_t title_id = kernel_state_->GetExecutableModule()->title_id(); - auto file_name = fmt::format("{:8X}_{}.xtr", title_id, counter_ - 1); + auto file_name = fmt::format("{:08X}_{}.xtr", title_id, counter_ - 1); auto path = trace_frame_path_ / file_name; trace_writer_.Open(path, title_id); InitializeTrace(); diff --git a/src/xenia/gpu/primitive_processor.cc b/src/xenia/gpu/primitive_processor.cc index 8dbe1ead3..fcb57634e 100644 --- a/src/xenia/gpu/primitive_processor.cc +++ b/src/xenia/gpu/primitive_processor.cc @@ -510,7 +510,7 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) { SharedMemory::kBufferSize - guest_index_base < guest_index_buffer_needed_bytes) { XELOGE( - "Primitive processor: Index buffer at 0x{:8X}, 0x{:X} bytes " + "Primitive processor: Index buffer at 0x{:08X}, 0x{:X} bytes " "required, is out of the physical memory bounds", guest_index_base, guest_index_buffer_needed_bytes); assert_always(); @@ -814,7 +814,7 @@ bool PrimitiveProcessor::Process(ProcessingResult& result_out) { if (!shared_memory_.RequestRange(guest_index_base, guest_index_buffer_needed_bytes)) { XELOGE( - "PrimitiveProcessor: Failed to request index buffer 0x{:8X}, " + "PrimitiveProcessor: Failed to request index buffer 0x{:08X}, " "0x{:X} bytes needed, in the shared memory", guest_index_base, guest_index_buffer_needed_bytes); return false; diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index a4367a48b..883020ce9 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -245,7 +245,7 @@ X_RESULT ContentManager::DeleteContent(const XCONTENT_AGGREGATE_DATA& data) { } std::filesystem::path ContentManager::ResolveGameUserContentPath() { - auto title_id = fmt::format("{:8X}", kernel_state_->title_id()); + auto title_id = fmt::format("{:08X}", kernel_state_->title_id()); auto user_name = xe::to_path(kernel_state_->user_profile()->name()); // Per-game per-profile data location: From 7019205810664a8eebcf3922baffd9f8bd03032c Mon Sep 17 00:00:00 2001 From: gibbed Date: Sat, 29 Jan 2022 03:30:18 -0600 Subject: [PATCH 72/88] [App] Rename ShowCommitID to ShowBuildCommit. --- src/xenia/app/emulator_window.cc | 10 +++++----- src/xenia/app/emulator_window.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index 097d59dfb..12ec273a9 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -585,9 +585,9 @@ bool EmulatorWindow::Initialize() { // Help menu. auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help"); { - help_menu->AddChild( - MenuItem::Create(MenuItem::Type::kString, "Build commit on GitHub...", - "F2", std::bind(&EmulatorWindow::ShowCommitID, this))); + help_menu->AddChild(MenuItem::Create( + MenuItem::Type::kString, "Build commit on GitHub...", "F2", + std::bind(&EmulatorWindow::ShowBuildCommit, this))); help_menu->AddChild(MenuItem::Create( MenuItem::Type::kString, "Recent changes on GitHub...", [this]() { LaunchWebBrowser( @@ -780,7 +780,7 @@ void EmulatorWindow::OnKeyDown(ui::KeyEvent& e) { } break; case ui::VirtualKey::kF2: { - ShowCommitID(); + ShowBuildCommit(); } break; default: @@ -929,7 +929,7 @@ void EmulatorWindow::ToggleDisplayConfigDialog() { void EmulatorWindow::ShowHelpWebsite() { LaunchWebBrowser("https://xenia.jp"); } -void EmulatorWindow::ShowCommitID() { +void EmulatorWindow::ShowBuildCommit() { #ifdef XE_BUILD_IS_PR LaunchWebBrowser( "https://github.com/xenia-project/xenia/pull/" XE_BUILD_PR_NUMBER); diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index ad6c4daee..e6f364f95 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -140,7 +140,7 @@ class EmulatorWindow { void GpuClearCaches(); void ToggleDisplayConfigDialog(); void ShowHelpWebsite(); - void ShowCommitID(); + void ShowBuildCommit(); Emulator* emulator_; ui::WindowedAppContext& app_context_; From c6b2b1e8eb966b61873158af06a3c8545b7b98a6 Mon Sep 17 00:00:00 2001 From: gibbed Date: Sat, 29 Jan 2022 03:32:11 -0600 Subject: [PATCH 73/88] [App] Replace Website help menu item with FAQ. --- src/xenia/app/emulator_window.cc | 13 ++++++++----- src/xenia/app/emulator_window.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index 12ec273a9..75d6d1ae3 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -585,6 +585,10 @@ bool EmulatorWindow::Initialize() { // Help menu. auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help"); { + help_menu->AddChild( + MenuItem::Create(MenuItem::Type::kString, "FA&Q...", "F1", + std::bind(&EmulatorWindow::ShowFAQ, this))); + help_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); help_menu->AddChild(MenuItem::Create( MenuItem::Type::kString, "Build commit on GitHub...", "F2", std::bind(&EmulatorWindow::ShowBuildCommit, this))); @@ -595,9 +599,6 @@ bool EmulatorWindow::Initialize() { "..." XE_BUILD_BRANCH); })); help_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); - help_menu->AddChild( - MenuItem::Create(MenuItem::Type::kString, "&Website...", "F1", - std::bind(&EmulatorWindow::ShowHelpWebsite, this))); help_menu->AddChild(MenuItem::Create( MenuItem::Type::kString, "&About...", [this]() { LaunchWebBrowser("https://xenia.jp/about/"); })); @@ -776,7 +777,7 @@ void EmulatorWindow::OnKeyDown(ui::KeyEvent& e) { } break; case ui::VirtualKey::kF1: { - ShowHelpWebsite(); + ShowFAQ(); } break; case ui::VirtualKey::kF2: { @@ -927,7 +928,9 @@ void EmulatorWindow::ToggleDisplayConfigDialog() { } } -void EmulatorWindow::ShowHelpWebsite() { LaunchWebBrowser("https://xenia.jp"); } +void EmulatorWindow::ShowFAQ() { + LaunchWebBrowser("https://github.com/xenia-project/xenia/wiki/FAQ"); +} void EmulatorWindow::ShowBuildCommit() { #ifdef XE_BUILD_IS_PR diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index e6f364f95..d6be0108a 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -139,7 +139,7 @@ class EmulatorWindow { void GpuTraceFrame(); void GpuClearCaches(); void ToggleDisplayConfigDialog(); - void ShowHelpWebsite(); + void ShowFAQ(); void ShowBuildCommit(); Emulator* emulator_; From 306ee85514451be07a475c6294421d9056484c9c Mon Sep 17 00:00:00 2001 From: gibbed Date: Sat, 29 Jan 2022 03:33:09 -0600 Subject: [PATCH 74/88] [App] Add Compatibility help menu item. --- src/xenia/app/emulator_window.cc | 18 ++++++++++++++++++ src/xenia/app/emulator_window.h | 1 + 2 files changed, 19 insertions(+) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index 75d6d1ae3..c8374edd4 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -589,6 +589,10 @@ bool EmulatorWindow::Initialize() { MenuItem::Create(MenuItem::Type::kString, "FA&Q...", "F1", std::bind(&EmulatorWindow::ShowFAQ, this))); help_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); + help_menu->AddChild( + MenuItem::Create(MenuItem::Type::kString, "Game &compatibility...", + std::bind(&EmulatorWindow::ShowCompatibility, this))); + help_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); help_menu->AddChild(MenuItem::Create( MenuItem::Type::kString, "Build commit on GitHub...", "F2", std::bind(&EmulatorWindow::ShowBuildCommit, this))); @@ -928,6 +932,20 @@ void EmulatorWindow::ToggleDisplayConfigDialog() { } } +void EmulatorWindow::ShowCompatibility() { + const std::string_view base_url = + "https://github.com/xenia-project/game-compatibility/issues"; + std::string url; + // Avoid searching for a title ID of "00000000". + uint32_t title_id = emulator_->title_id(); + if (!title_id) { + url = base_url; + } else { + url = fmt::format("{}?q=is%3Aissue+is%3Aopen+{:08X}", base_url, title_id); + } + LaunchWebBrowser(url); +} + void EmulatorWindow::ShowFAQ() { LaunchWebBrowser("https://github.com/xenia-project/xenia/wiki/FAQ"); } diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index d6be0108a..c57d5f43b 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -139,6 +139,7 @@ class EmulatorWindow { void GpuTraceFrame(); void GpuClearCaches(); void ToggleDisplayConfigDialog(); + void ShowCompatibility(); void ShowFAQ(); void ShowBuildCommit(); From 0dfbb679880d77bc3435118b18f431e18ae10f6a Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Sat, 29 Jan 2022 12:15:34 -0600 Subject: [PATCH 75/88] [AppVeyor] Revert to VS2019. [AppVeyor] Revert to VS2019 due to a possible compiler bug. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 9083c80b9..960ce805e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -23,7 +23,7 @@ skip_branch_with_pr: true pull_requests: do_not_increment_build_number: true -os: Visual Studio 2022 +os: Visual Studio 2019 init: - ps: | From 4b8740d94f9443688550e9df6e05853941e58555 Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Sat, 29 Jan 2022 22:44:24 -0600 Subject: [PATCH 76/88] Don't recursively init/update submodules. Don't recursively init/update submodules. When recursive fetching is desireable, set fetchRecurseSubmodules = true in .gitmodules for the relevant submodule. --- xenia-build | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xenia-build b/xenia-build index a37f76d8e..671f1d7b6 100755 --- a/xenia-build +++ b/xenia-build @@ -410,14 +410,15 @@ def git_is_repository(): def git_submodule_update(): - """Runs a full recursive git submodule init and update. + """Runs a git submodule init and update. """ shell_call([ 'git', + '-c', + 'fetch.recurseSubmodules=on-demand', 'submodule', 'update', '--init', - '--recursive', ]) From 50cf96ff36a9c3e1cd4f768fe3ec285d59b21f5c Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 30 Jan 2022 12:37:14 +0300 Subject: [PATCH 77/88] [D3D12] Don't drain PSO preload creation queue if not queueing at all --- src/xenia/gpu/d3d12/pipeline_cache.cc | 62 ++++++++++++++------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/xenia/gpu/d3d12/pipeline_cache.cc b/src/xenia/gpu/d3d12/pipeline_cache.cc index f8617355a..3a0cb3313 100644 --- a/src/xenia/gpu/d3d12/pipeline_cache.cc +++ b/src/xenia/gpu/d3d12/pipeline_cache.cc @@ -703,37 +703,39 @@ void PipelineCache::InitializeShaderStorage( ++pipelines_created; } - CreateQueuedPipelinesOnProcessorThread(); - if (creation_threads_.size() > creation_thread_original_count) { - { - std::lock_guard lock(creation_request_lock_); - creation_threads_shutdown_from_ = creation_thread_original_count; - // Assuming the queue is empty because of - // CreateQueuedPipelinesOnProcessorThread. - } - creation_request_cond_.notify_all(); - while (creation_threads_.size() > creation_thread_original_count) { - xe::threading::Wait(creation_threads_.back().get(), false); - creation_threads_.pop_back(); - } - bool await_creation_completion_event; - { - // Cleanup so additional threads can be created later again. - std::lock_guard lock(creation_request_lock_); - creation_threads_shutdown_from_ = SIZE_MAX; - // If the invocation is blocking, all the shader storage initialization - // is expected to be done before proceeding, to avoid latency in the - // command processor after the invocation. - await_creation_completion_event = - blocking && creation_threads_busy_ != 0; - if (await_creation_completion_event) { - creation_completion_event_->Reset(); - creation_completion_set_event_ = true; + if (!creation_threads_.empty()) { + CreateQueuedPipelinesOnProcessorThread(); + if (creation_threads_.size() > creation_thread_original_count) { + { + std::lock_guard lock(creation_request_lock_); + creation_threads_shutdown_from_ = creation_thread_original_count; + // Assuming the queue is empty because of + // CreateQueuedPipelinesOnProcessorThread. + } + creation_request_cond_.notify_all(); + while (creation_threads_.size() > creation_thread_original_count) { + xe::threading::Wait(creation_threads_.back().get(), false); + creation_threads_.pop_back(); + } + bool await_creation_completion_event; + { + // Cleanup so additional threads can be created later again. + std::lock_guard lock(creation_request_lock_); + creation_threads_shutdown_from_ = SIZE_MAX; + // If the invocation is blocking, all the shader storage + // initialization is expected to be done before proceeding, to avoid + // latency in the command processor after the invocation. + await_creation_completion_event = + blocking && creation_threads_busy_ != 0; + if (await_creation_completion_event) { + creation_completion_event_->Reset(); + creation_completion_set_event_ = true; + } + } + if (await_creation_completion_event) { + creation_request_cond_.notify_one(); + xe::threading::Wait(creation_completion_event_.get(), false); } - } - if (await_creation_completion_event) { - creation_request_cond_.notify_one(); - xe::threading::Wait(creation_completion_event_.get(), false); } } From 2639f2590f7cc09e2a1147381521ca53907434d4 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 30 Jan 2022 13:22:21 +0300 Subject: [PATCH 78/88] [AppVeyor] Skip all non-Windows commits [ci skip] --- .appveyor.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index 960ce805e..8d2b2e6df 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -10,11 +10,15 @@ skip_commits: files: - .drone.star - .github/** + - android/** - docs/** - src/**/*_posix.* - src/**/*_linux.* + - src/**/*_gnulinux.* - src/**/*_x11.* - src/**/*_gtk.* + - src/**/*_android.* + - src/**/*_mac.* - LICENSE - README.md From c5e33326404d32233498c14fa30fcb0252ff088c Mon Sep 17 00:00:00 2001 From: Joel Linn Date: Sun, 30 Jan 2022 13:19:08 +0100 Subject: [PATCH 79/88] [HID] Update SDL to 2.0.20 --- third_party/SDL2 | 2 +- third_party/SDL2-static.lua | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/third_party/SDL2 b/third_party/SDL2 index ea9bece5e..b424665e0 160000 --- a/third_party/SDL2 +++ b/third_party/SDL2 @@ -1 +1 @@ -Subproject commit ea9bece5ed4e76b6636d941101923487a6378ca6 +Subproject commit b424665e0899769b200231ba943353a5fee1b6b6 diff --git a/third_party/SDL2-static.lua b/third_party/SDL2-static.lua index 492c0d3d9..d03a607a5 100644 --- a/third_party/SDL2-static.lua +++ b/third_party/SDL2-static.lua @@ -42,6 +42,7 @@ project("SDL2") "SDL2/include/SDL_gesture.h", "SDL2/include/SDL_haptic.h", "SDL2/include/SDL_hints.h", + "SDL2/include/SDL_hidapi.h", "SDL2/include/SDL_joystick.h", "SDL2/include/SDL_keyboard.h", "SDL2/include/SDL_keycode.h", @@ -133,7 +134,7 @@ project("SDL2") "SDL2/src/haptic/windows/SDL_windowshaptic_c.h", "SDL2/src/haptic/windows/SDL_xinputhaptic_c.h", "SDL2/src/hidapi/hidapi/hidapi.h", - "SDL2/src/hidapi/SDL_hidapi.h", + "SDL2/src/hidapi/SDL_hidapi_c.h", "SDL2/src/joystick/controller_type.h", "SDL2/src/joystick/hidapi/SDL_hidapijoystick_c.h", "SDL2/src/joystick/hidapi/SDL_hidapi_rumble.h", @@ -168,6 +169,7 @@ project("SDL2") "SDL2/src/render/software/SDL_drawpoint.h", "SDL2/src/render/software/SDL_render_sw_c.h", "SDL2/src/render/software/SDL_rotate.h", + "SDL2/src/render/software/SDL_triangle.h", "SDL2/src/SDL_assert_c.h", "SDL2/src/SDL_dataqueue.h", "SDL2/src/SDL_error_c.h", @@ -293,7 +295,6 @@ project("SDL2") "SDL2/src/joystick/SDL_joystick.c", "SDL2/src/joystick/virtual/SDL_virtualjoystick.c", "SDL2/src/joystick/windows/SDL_dinputjoystick.c", - "SDL2/src/joystick/windows/SDL_mmjoystick.c", "SDL2/src/joystick/windows/SDL_rawinputjoystick.c", "SDL2/src/joystick/windows/SDL_windowsjoystick.c", "SDL2/src/joystick/windows/SDL_windows_gaming_input.c", @@ -343,6 +344,7 @@ project("SDL2") "SDL2/src/render/software/SDL_drawpoint.c", "SDL2/src/render/software/SDL_render_sw.c", "SDL2/src/render/software/SDL_rotate.c", + "SDL2/src/render/software/SDL_triangle.c", "SDL2/src/SDL.c", "SDL2/src/SDL_assert.c", "SDL2/src/SDL_dataqueue.c", @@ -362,7 +364,7 @@ project("SDL2") "SDL2/src/stdlib/SDL_strtokr.c", "SDL2/src/thread/generic/SDL_syscond.c", "SDL2/src/thread/SDL_thread.c", - "SDL2/src/thread/windows/SDL_syscond_srw.c", + "SDL2/src/thread/windows/SDL_syscond_cv.c", "SDL2/src/thread/windows/SDL_sysmutex.c", "SDL2/src/thread/windows/SDL_syssem.c", "SDL2/src/thread/windows/SDL_systhread.c", From d2ef8d3300ea3d2633778df0248bbc8e99c484e1 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 30 Jan 2022 18:36:11 +0300 Subject: [PATCH 80/88] [Base] Android error reporting via SIGABRT/RuntimeException --- .../java/jp/xenia/XeniaRuntimeException.java | 21 +++++++++++ .../xenia/emulator/WindowedAppActivity.java | 12 +++---- src/xenia/base/logging.cc | 9 ++++- src/xenia/base/system_android.cc | 36 +++++++++++++++++++ .../{system_linux.cc => system_gnulinux.cc} | 0 5 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 android/android_studio_project/app/src/main/java/jp/xenia/XeniaRuntimeException.java create mode 100644 src/xenia/base/system_android.cc rename src/xenia/base/{system_linux.cc => system_gnulinux.cc} (100%) diff --git a/android/android_studio_project/app/src/main/java/jp/xenia/XeniaRuntimeException.java b/android/android_studio_project/app/src/main/java/jp/xenia/XeniaRuntimeException.java new file mode 100644 index 000000000..93a046dfb --- /dev/null +++ b/android/android_studio_project/app/src/main/java/jp/xenia/XeniaRuntimeException.java @@ -0,0 +1,21 @@ +package jp.xenia; + +/** + * Base class for all unchecked exceptions thrown by the Xenia project components. + */ +public class XeniaRuntimeException extends RuntimeException { + public XeniaRuntimeException() { + } + + public XeniaRuntimeException(String name) { + super(name); + } + + public XeniaRuntimeException(String name, Throwable cause) { + super(name, cause); + } + + public XeniaRuntimeException(Exception cause) { + super(cause); + } +} diff --git a/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowedAppActivity.java b/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowedAppActivity.java index dd89881c3..9deb60358 100644 --- a/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowedAppActivity.java +++ b/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowedAppActivity.java @@ -3,11 +3,10 @@ package jp.xenia.emulator; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; -import android.util.Log; + +import jp.xenia.XeniaRuntimeException; public abstract class WindowedAppActivity extends Activity { - private static final String TAG = "WindowedAppActivity"; - static { // TODO(Triang3l): Move all demos to libxenia.so. System.loadLibrary("xenia-ui-window-vulkan-demo"); @@ -26,11 +25,12 @@ public abstract class WindowedAppActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mAppContext = initializeWindowedAppOnCreateNative(getWindowedAppIdentifier(), getAssets()); + final String windowedAppIdentifier = getWindowedAppIdentifier(); + mAppContext = initializeWindowedAppOnCreateNative(windowedAppIdentifier, getAssets()); if (mAppContext == 0) { - Log.e(TAG, "Error initializing the windowed app"); finish(); - return; + throw new XeniaRuntimeException( + "Error initializing the windowed app " + windowedAppIdentifier); } } diff --git a/src/xenia/base/logging.cc b/src/xenia/base/logging.cc index d5b9ccef7..bed2fe43b 100644 --- a/src/xenia/base/logging.cc +++ b/src/xenia/base/logging.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -500,7 +501,13 @@ void FatalError(const std::string_view str) { } ShutdownLogging(); - std::exit(1); + +#if XE_PLATFORM_ANDROID + // Throw an error that can be reported to the developers via the store. + std::abort(); +#else + std::exit(EXIT_FAILURE); +#endif // XE_PLATFORM_ANDROID } } // namespace xe diff --git a/src/xenia/base/system_android.cc b/src/xenia/base/system_android.cc new file mode 100644 index 000000000..8290172ca --- /dev/null +++ b/src/xenia/base/system_android.cc @@ -0,0 +1,36 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include "xenia/base/assert.h" +#include "xenia/base/system.h" + +namespace xe { + +void LaunchWebBrowser(const std::string_view url) { + // TODO(Triang3l): Intent.ACTION_VIEW (need a Java VM for the thread - + // possibly restrict this to the UI thread). + assert_always(); +} + +void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); } + +void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) { + // TODO(Triang3l): Likely not needed much at all. ShowSimpleMessageBox is a + // concept pretty unfriendly to platforms like Android because it's blocking, + // and because it can be called from threads other than the UI thread. In the + // normal execution flow, dialogs should preferably be asynchronous, and used + // only in the UI thread. However, non-blocking messages may be good for error + // reporting - investigate the usage of Toasts with respect to threads, and + // aborting the process immediately after showing a Toast. For a Toast, the + // Java VM for the calling thread is needed. +} + +} // namespace xe diff --git a/src/xenia/base/system_linux.cc b/src/xenia/base/system_gnulinux.cc similarity index 100% rename from src/xenia/base/system_linux.cc rename to src/xenia/base/system_gnulinux.cc From 54b057a46b48a150f4e62de495d8be32f84a679d Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 30 Jan 2022 18:36:59 +0300 Subject: [PATCH 81/88] [Android] Switch to Gradle 7.1.0 --- android/android_studio_project/app/build.gradle | 1 - android/android_studio_project/build.gradle | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/android/android_studio_project/app/build.gradle b/android/android_studio_project/app/build.gradle index ff6acf7e2..4888e7d60 100644 --- a/android/android_studio_project/app/build.gradle +++ b/android/android_studio_project/app/build.gradle @@ -4,7 +4,6 @@ plugins { android { compileSdkVersion 30 - buildToolsVersion '30.0.2' ndkVersion '23.0.7599858' defaultConfig { diff --git a/android/android_studio_project/build.gradle b/android/android_studio_project/build.gradle index 971fd1b37..fee7fd669 100644 --- a/android/android_studio_project/build.gradle +++ b/android/android_studio_project/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.3' + classpath 'com.android.tools.build:gradle:7.1.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From 3f817fb241d7777de2c58b02e3b6234ecd9cf639 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 30 Jan 2022 23:35:01 +0300 Subject: [PATCH 82/88] [Base] Android JNIEnv attachment and LaunchWebBrowser --- src/xenia/base/console_app_main_android.cc | 2 +- src/xenia/base/main_android.cc | 93 ++++++- src/xenia/base/main_android.h | 15 +- src/xenia/base/system.h | 7 + src/xenia/base/system_android.cc | 274 ++++++++++++++++++- src/xenia/ui/windowed_app_context_android.cc | 70 +++-- src/xenia/ui/windowed_app_context_android.h | 3 +- 7 files changed, 433 insertions(+), 31 deletions(-) diff --git a/src/xenia/base/console_app_main_android.cc b/src/xenia/base/console_app_main_android.cc index 320128bc1..3e2f79473 100644 --- a/src/xenia/base/console_app_main_android.cc +++ b/src/xenia/base/console_app_main_android.cc @@ -24,7 +24,7 @@ extern "C" int main(int argc, char** argv) { // Initialize Android globals, including logging. Needs parsed cvars. // TODO(Triang3l): Obtain the actual API level. - xe::InitializeAndroidAppFromMainThread(__ANDROID_API__); + xe::InitializeAndroidAppFromMainThread(__ANDROID_API__, nullptr, nullptr); std::vector args; for (int n = 0; n < argc; n++) { diff --git a/src/xenia/base/main_android.cc b/src/xenia/base/main_android.cc index 56211fee2..e1ef53c72 100644 --- a/src/xenia/base/main_android.cc +++ b/src/xenia/base/main_android.cc @@ -9,11 +9,15 @@ #include "xenia/base/main_android.h" +#include +#include #include +#include #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/memory.h" +#include "xenia/base/system.h" #include "xenia/base/threading.h" namespace xe { @@ -22,7 +26,25 @@ static size_t android_initializations_ = 0; static int32_t android_api_level_ = __ANDROID_API__; -void InitializeAndroidAppFromMainThread(int32_t api_level) { +static JNIEnv* android_main_thread_jni_env_ = nullptr; +static JavaVM* android_java_vm_ = nullptr; +static pthread_key_t android_thread_jni_env_key_; +static jobject android_application_context_ = nullptr; + +static void AndroidThreadJNIEnvDestructor(void* jni_env_pointer) { + // The JNIEnv pointer for the main thread is taken externally, the lifetime of + // the attachment is not managed by the key. + JNIEnv* jni_env = static_cast(jni_env_pointer); + if (jni_env && jni_env != android_main_thread_jni_env_) { + android_java_vm_->DetachCurrentThread(); + } + // Multiple iterations of destructor invocations can be done - clear. + pthread_setspecific(android_thread_jni_env_key_, nullptr); +} + +void InitializeAndroidAppFromMainThread(int32_t api_level, + JNIEnv* main_thread_jni_env, + jobject application_context) { if (android_initializations_++) { // Already initialized for another component in the process. return; @@ -32,6 +54,45 @@ void InitializeAndroidAppFromMainThread(int32_t api_level) { // subsystem initialization itself. android_api_level_ = api_level; + android_main_thread_jni_env_ = main_thread_jni_env; + if (main_thread_jni_env) { + // In a Java VM, not just in a process that runs an executable - set up + // the attachment of threads to the Java VM. + if (main_thread_jni_env->GetJavaVM(&android_java_vm_) < 0) { + // Logging has not been initialized yet. + __android_log_write( + ANDROID_LOG_ERROR, "InitializeAndroidAppFromMainThread", + "Failed to get the Java VM from the JNI environment of the main " + "thread"); + std::abort(); + } + if (pthread_key_create(&android_thread_jni_env_key_, + AndroidThreadJNIEnvDestructor)) { + __android_log_write( + ANDROID_LOG_ERROR, "InitializeAndroidAppFromMainThread", + "Failed to create the thread-specific JNI environment key"); + std::abort(); + } + if (pthread_setspecific(android_thread_jni_env_key_, main_thread_jni_env)) { + __android_log_write( + ANDROID_LOG_ERROR, "InitializeAndroidAppFromMainThread", + "Failed to set the thread-specific JNI environment pointer for the " + "main thread"); + std::abort(); + } + if (application_context) { + android_application_context_ = + main_thread_jni_env->NewGlobalRef(application_context); + if (!android_application_context_) { + __android_log_write( + ANDROID_LOG_ERROR, "InitializeAndroidAppFromMainThread", + "Failed to create a global reference to the application context " + "object"); + std::abort(); + } + } + } + // Logging uses threading. xe::threading::AndroidInitialize(); @@ -40,6 +101,15 @@ void InitializeAndroidAppFromMainThread(int32_t api_level) { xe::InitializeLogging("xenia"); xe::memory::AndroidInitialize(); + + if (android_application_context_) { + if (!xe::InitializeAndroidSystemForApplicationContext()) { + __android_log_write(ANDROID_LOG_ERROR, + "InitializeAndroidAppFromMainThread", + "Failed to initialize system UI interaction"); + std::abort(); + } + } } void ShutdownAndroidAppFromMainThread() { @@ -52,15 +122,36 @@ void ShutdownAndroidAppFromMainThread() { return; } + xe::ShutdownAndroidSystem(); + xe::memory::AndroidShutdown(); xe::ShutdownLogging(); xe::threading::AndroidShutdown(); + if (android_application_context_) { + android_main_thread_jni_env_->DeleteGlobalRef(android_application_context_); + android_application_context_ = nullptr; + } + if (android_java_vm_) { + android_java_vm_ = nullptr; + pthread_key_delete(android_thread_jni_env_key_); + } + android_main_thread_jni_env_ = nullptr; + android_api_level_ = __ANDROID_API__; } int32_t GetAndroidApiLevel() { return android_api_level_; } +JNIEnv* GetAndroidThreadJNIEnv() { + if (!android_java_vm_) { + return nullptr; + } + return static_cast(pthread_getspecific(android_thread_jni_env_key_)); +} + +jobject GetAndroidApplicationContext() { return android_application_context_; } + } // namespace xe diff --git a/src/xenia/base/main_android.h b/src/xenia/base/main_android.h index 1f2d23dd2..d871fc15a 100644 --- a/src/xenia/base/main_android.h +++ b/src/xenia/base/main_android.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,6 +10,7 @@ #ifndef XENIA_BASE_MAIN_ANDROID_H_ #define XENIA_BASE_MAIN_ANDROID_H_ +#include #include #include "xenia/base/platform.h" @@ -27,14 +28,22 @@ namespace xe { // counting internally. // // In standalone console apps built with $(BUILD_EXECUTABLE), these functions -// must be called in `main`. -void InitializeAndroidAppFromMainThread(int32_t api_level); +// must be called in `main`, with a null main thread JNI environment. +void InitializeAndroidAppFromMainThread(int32_t api_level, + JNIEnv* main_thread_jni_env, + jobject application_context); void ShutdownAndroidAppFromMainThread(); // May be the minimum supported level if the initialization was done without a // configuration. int32_t GetAndroidApiLevel(); +// May return null if not in a Java VM process, or in case of a failure to +// attach on a non-main thread. +JNIEnv* GetAndroidThreadJNIEnv(); +// Returns the global reference if in an application context, or null otherwise. +jobject GetAndroidApplicationContext(); + } // namespace xe #endif // XENIA_BASE_MAIN_ANDROID_H_ diff --git a/src/xenia/base/system.h b/src/xenia/base/system.h index 4bd0eac2b..b77bdbc69 100644 --- a/src/xenia/base/system.h +++ b/src/xenia/base/system.h @@ -13,10 +13,17 @@ #include #include +#include "xenia/base/platform.h" #include "xenia/base/string.h" namespace xe { +#if XE_PLATFORM_ANDROID +bool InitializeAndroidSystemForApplicationContext(); +void ShutdownAndroidSystem(); +#endif + +// The URL must include the protocol. void LaunchWebBrowser(const std::string_view url); void LaunchFileExplorer(const std::filesystem::path& path); diff --git a/src/xenia/base/system_android.cc b/src/xenia/base/system_android.cc index 8290172ca..af6ee9926 100644 --- a/src/xenia/base/system_android.cc +++ b/src/xenia/base/system_android.cc @@ -7,17 +7,285 @@ ****************************************************************************** */ +#include #include +#include #include "xenia/base/assert.h" +#include "xenia/base/logging.h" +#include "xenia/base/main_android.h" #include "xenia/base/system.h" namespace xe { +// To store jmethodIDs persistently, global references to the classes are +// required to prevent the classes from being unloaded and reloaded, potentially +// changing the method IDs. + +static jclass android_system_application_context_class_ = nullptr; +static jmethodID android_system_application_context_start_activity_ = nullptr; + +static jclass android_system_uri_class_ = nullptr; +static jmethodID android_system_uri_parse_ = nullptr; + +static jclass android_system_intent_class_ = nullptr; +static jfieldID android_system_intent_action_view_field_id_ = nullptr; +static jfieldID android_system_intent_flag_activity_new_task_field_id_ = + nullptr; +static jmethodID android_system_intent_init_action_uri_ = nullptr; +static jmethodID android_system_intent_add_flags_ = nullptr; +static jobject android_system_intent_action_view_ = nullptr; +static jint android_system_intent_flag_activity_new_task_; + +static bool android_system_initialized_ = false; + +bool InitializeAndroidSystemForApplicationContext() { + assert_false(android_system_initialized_); + + JNIEnv* jni_env = GetAndroidThreadJNIEnv(); + if (!jni_env) { + return false; + } + jobject application_context = xe::GetAndroidApplicationContext(); + if (!application_context) { + return false; + } + + // Application context. + { + { + jclass application_context_class_local_ref = + jni_env->GetObjectClass(application_context); + if (!application_context_class_local_ref) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to get the " + "class of the application context"); + ShutdownAndroidSystem(); + return false; + } + android_system_application_context_class_ = + reinterpret_cast(jni_env->NewGlobalRef( + reinterpret_cast(application_context_class_local_ref))); + jni_env->DeleteLocalRef(application_context_class_local_ref); + } + if (!android_system_application_context_class_) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to create a " + "global reference to the class of the application context"); + ShutdownAndroidSystem(); + return false; + } + bool application_context_ids_obtained = true; + application_context_ids_obtained &= + (android_system_application_context_start_activity_ = + jni_env->GetMethodID(android_system_application_context_class_, + "startActivity", + "(Landroid/content/Intent;)V")) != nullptr; + if (!application_context_ids_obtained) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to get the " + "application context class IDs"); + ShutdownAndroidSystem(); + return false; + } + } + + // URI. + { + { + jclass uri_class_local_ref = jni_env->FindClass("android/net/Uri"); + if (!uri_class_local_ref) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to find the " + "URI class"); + ShutdownAndroidSystem(); + return false; + } + android_system_uri_class_ = + reinterpret_cast(jni_env->NewGlobalRef( + reinterpret_cast(uri_class_local_ref))); + jni_env->DeleteLocalRef(uri_class_local_ref); + } + if (!android_system_uri_class_) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to create a " + "global reference to the URI class"); + ShutdownAndroidSystem(); + return false; + } + bool uri_ids_obtained = true; + uri_ids_obtained &= + (android_system_uri_parse_ = jni_env->GetStaticMethodID( + android_system_uri_class_, "parse", + "(Ljava/lang/String;)Landroid/net/Uri;")) != nullptr; + if (!uri_ids_obtained) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to get the URI " + "class IDs"); + ShutdownAndroidSystem(); + return false; + } + } + + // Intent. + { + { + jclass intent_class_local_ref = + jni_env->FindClass("android/content/Intent"); + if (!intent_class_local_ref) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to find the " + "intent class"); + ShutdownAndroidSystem(); + return false; + } + android_system_intent_class_ = + reinterpret_cast(jni_env->NewGlobalRef( + reinterpret_cast(intent_class_local_ref))); + jni_env->DeleteLocalRef(intent_class_local_ref); + } + if (!android_system_intent_class_) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to create a " + "global reference to the intent class"); + ShutdownAndroidSystem(); + return false; + } + bool intent_ids_obtained = true; + intent_ids_obtained &= (android_system_intent_action_view_field_id_ = + jni_env->GetStaticFieldID( + android_system_intent_class_, "ACTION_VIEW", + "Ljava/lang/String;")) != nullptr; + intent_ids_obtained &= + (android_system_intent_flag_activity_new_task_field_id_ = + jni_env->GetStaticFieldID(android_system_intent_class_, + "FLAG_ACTIVITY_NEW_TASK", "I")) != + nullptr; + intent_ids_obtained &= + (android_system_intent_init_action_uri_ = jni_env->GetMethodID( + android_system_intent_class_, "", + "(Ljava/lang/String;Landroid/net/Uri;)V")) != nullptr; + intent_ids_obtained &= + (android_system_intent_add_flags_ = + jni_env->GetMethodID(android_system_intent_class_, "addFlags", + "(I)Landroid/content/Intent;")) != nullptr; + if (!intent_ids_obtained) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to get the " + "intent class IDs"); + ShutdownAndroidSystem(); + return false; + } + { + jobject intent_action_view_local_ref = jni_env->GetStaticObjectField( + android_system_intent_class_, + android_system_intent_action_view_field_id_); + if (!intent_action_view_local_ref) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to get the " + "intent view action string"); + ShutdownAndroidSystem(); + return false; + } + android_system_intent_action_view_ = + jni_env->NewGlobalRef(intent_action_view_local_ref); + jni_env->DeleteLocalRef(intent_action_view_local_ref); + if (!android_system_intent_action_view_) { + XELOGE( + "InitializeAndroidSystemForApplicationContext: Failed to create a " + "global reference to the intent view action string"); + ShutdownAndroidSystem(); + return false; + } + } + android_system_intent_flag_activity_new_task_ = jni_env->GetStaticIntField( + android_system_intent_class_, + android_system_intent_flag_activity_new_task_field_id_); + } + + android_system_initialized_ = true; + return true; +} + +void ShutdownAndroidSystem() { + // May be called from InitializeAndroidSystemForApplicationContext as well. + android_system_initialized_ = false; + android_system_intent_add_flags_ = nullptr; + android_system_intent_init_action_uri_ = nullptr; + android_system_intent_flag_activity_new_task_field_id_ = nullptr; + android_system_intent_action_view_field_id_ = nullptr; + android_system_uri_parse_ = nullptr; + android_system_application_context_start_activity_ = nullptr; + JNIEnv* jni_env = GetAndroidThreadJNIEnv(); + if (jni_env) { + if (android_system_intent_action_view_) { + jni_env->DeleteGlobalRef(android_system_intent_action_view_); + } + if (android_system_intent_class_) { + jni_env->DeleteGlobalRef(android_system_intent_class_); + } + if (android_system_uri_class_) { + jni_env->DeleteGlobalRef(android_system_uri_class_); + } + if (android_system_application_context_class_) { + jni_env->DeleteGlobalRef(android_system_application_context_class_); + } + } + android_system_intent_action_view_ = nullptr; + android_system_intent_class_ = nullptr; + android_system_uri_class_ = nullptr; + android_system_application_context_class_ = nullptr; +} + void LaunchWebBrowser(const std::string_view url) { - // TODO(Triang3l): Intent.ACTION_VIEW (need a Java VM for the thread - - // possibly restrict this to the UI thread). - assert_always(); + if (!android_system_initialized_) { + return; + } + JNIEnv* jni_env = GetAndroidThreadJNIEnv(); + if (!jni_env) { + return; + } + jobject application_context = GetAndroidApplicationContext(); + if (!application_context) { + return; + } + + jstring uri_string = jni_env->NewStringUTF(std::string(url).c_str()); + if (!uri_string) { + XELOGE("LaunchWebBrowser: Failed to create the URI string"); + return; + } + jobject uri = jni_env->CallStaticObjectMethod( + android_system_uri_class_, android_system_uri_parse_, uri_string); + jni_env->DeleteLocalRef(uri_string); + if (!uri) { + XELOGE("LaunchWebBrowser: Failed to parse the URI"); + return; + } + jobject intent = jni_env->NewObject(android_system_intent_class_, + android_system_intent_init_action_uri_, + android_system_intent_action_view_, uri); + jni_env->DeleteLocalRef(uri); + if (!intent) { + XELOGE("LaunchWebBrowser: Failed to create the intent"); + return; + } + // Start a new task - the user may want to be able to switch between the + // emulator and the newly opened web browser, without having to quit the web + // browser to return to the emulator. Also, since the application context, not + // the activity, is used, the new task flag is required. + { + jobject intent_add_flags_result_local_ref = jni_env->CallObjectMethod( + intent, android_system_intent_add_flags_, + android_system_intent_flag_activity_new_task_); + if (intent_add_flags_result_local_ref) { + jni_env->DeleteLocalRef(intent_add_flags_result_local_ref); + } + } + jni_env->CallVoidMethod(application_context, + android_system_application_context_start_activity_, + intent); + jni_env->DeleteLocalRef(intent); } void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); } diff --git a/src/xenia/ui/windowed_app_context_android.cc b/src/xenia/ui/windowed_app_context_android.cc index 5af4efad6..66d1d42ba 100644 --- a/src/xenia/ui/windowed_app_context_android.cc +++ b/src/xenia/ui/windowed_app_context_android.cc @@ -141,26 +141,14 @@ bool AndroidWindowedAppContext::Initialize(JNIEnv* ui_thread_jni_env, } AConfiguration_fromAssetManager(configuration_, asset_manager_); - // Initialize Xenia globals that may depend on the API level, as well as - // logging. - xe::InitializeAndroidAppFromMainThread( - AConfiguration_getSdkVersion(configuration_)); - android_base_initialized_ = true; - - // Initialize interfacing with the WindowedAppActivity. - activity_ = ui_thread_jni_env_->NewGlobalRef(activity); - if (!activity_) { - XELOGE( - "AndroidWindowedAppContext: Failed to create a global reference to the " - "activity"); - Shutdown(); - return false; - } + // Get the activity class, needed for the application context here, and for + // other activity interaction later. { jclass activity_class_local_ref = ui_thread_jni_env_->GetObjectClass(activity); if (!activity_class_local_ref) { - XELOGE("AndroidWindowedAppContext: Failed to get the activity class"); + __android_log_write(ANDROID_LOG_ERROR, "AndroidWindowedAppContext", + "Failed to get the activity class"); Shutdown(); return false; } @@ -170,9 +158,46 @@ bool AndroidWindowedAppContext::Initialize(JNIEnv* ui_thread_jni_env, reinterpret_cast(activity_class_local_ref)); } if (!activity_class_) { + __android_log_write( + ANDROID_LOG_ERROR, "AndroidWindowedAppContext", + "Failed to create a global reference to the activity class"); + Shutdown(); + return false; + } + + // Get the application context. + jmethodID activity_get_application_context = ui_thread_jni_env_->GetMethodID( + activity_class_, "getApplicationContext", "()Landroid/content/Context;"); + if (!activity_get_application_context) { + __android_log_write( + ANDROID_LOG_ERROR, "AndroidWindowedAppContext", + "Failed to get the getApplicationContext method of the activity"); + Shutdown(); + return false; + } + jobject application_context_init_ref = ui_thread_jni_env_->CallObjectMethod( + activity, activity_get_application_context); + if (!application_context_init_ref) { + __android_log_write( + ANDROID_LOG_ERROR, "AndroidWindowedAppContext", + "Failed to get the application context from the activity"); + Shutdown(); + return false; + } + + // Initialize Xenia globals that may depend on the base globals and logging. + xe::InitializeAndroidAppFromMainThread( + AConfiguration_getSdkVersion(configuration_), ui_thread_jni_env_, + application_context_init_ref); + android_base_initialized_ = true; + ui_thread_jni_env_->DeleteLocalRef(application_context_init_ref); + + // Initialize interfacing with the WindowedAppActivity. + activity_ = ui_thread_jni_env_->NewGlobalRef(activity); + if (!activity_) { XELOGE( "AndroidWindowedAppContext: Failed to create a global reference to the " - "activity class"); + "activity"); Shutdown(); return false; } @@ -249,11 +274,6 @@ void AndroidWindowedAppContext::Shutdown() { } activity_method_finish_ = nullptr; - if (activity_class_) { - ui_thread_jni_env_->DeleteGlobalRef( - reinterpret_cast(activity_class_)); - activity_class_ = nullptr; - } if (activity_) { ui_thread_jni_env_->DeleteGlobalRef(activity_); activity_ = nullptr; @@ -264,6 +284,12 @@ void AndroidWindowedAppContext::Shutdown() { android_base_initialized_ = false; } + if (activity_class_) { + ui_thread_jni_env_->DeleteGlobalRef( + reinterpret_cast(activity_class_)); + activity_class_ = nullptr; + } + if (configuration_) { AConfiguration_delete(configuration_); configuration_ = nullptr; diff --git a/src/xenia/ui/windowed_app_context_android.h b/src/xenia/ui/windowed_app_context_android.h index bc7583109..d3947938b 100644 --- a/src/xenia/ui/windowed_app_context_android.h +++ b/src/xenia/ui/windowed_app_context_android.h @@ -95,10 +95,11 @@ class AndroidWindowedAppContext final : public WindowedAppContext { AConfiguration* configuration_ = nullptr; + jclass activity_class_ = nullptr; + bool android_base_initialized_ = false; jobject activity_ = nullptr; - jclass activity_class_ = nullptr; jmethodID activity_method_finish_ = nullptr; // May be read by non-UI threads in NotifyUILoopOfPendingFunctions. From d998c13ee81cb3a9ef2f513ebfa286a92a19383e Mon Sep 17 00:00:00 2001 From: Triang3l Date: Mon, 31 Jan 2022 12:12:49 +0300 Subject: [PATCH 83/88] [Base] Explain why no Android activity in xenia-base [ci skip] --- src/xenia/base/main_android.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/xenia/base/main_android.h b/src/xenia/base/main_android.h index d871fc15a..7fe35e038 100644 --- a/src/xenia/base/main_android.h +++ b/src/xenia/base/main_android.h @@ -42,6 +42,8 @@ int32_t GetAndroidApiLevel(); // attach on a non-main thread. JNIEnv* GetAndroidThreadJNIEnv(); // Returns the global reference if in an application context, or null otherwise. +// This is the application context, not the activity one, because multiple +// activities may be running in one process. jobject GetAndroidApplicationContext(); } // namespace xe From 009f709ad480b2658b8dc3229362c72959828b4a Mon Sep 17 00:00:00 2001 From: Triang3l Date: Mon, 31 Jan 2022 13:00:28 +0300 Subject: [PATCH 84/88] [Base] Remove Android jfieldIDs used only once from the file scope --- src/xenia/base/system_android.cc | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/xenia/base/system_android.cc b/src/xenia/base/system_android.cc index af6ee9926..bc1f34b35 100644 --- a/src/xenia/base/system_android.cc +++ b/src/xenia/base/system_android.cc @@ -18,9 +18,9 @@ namespace xe { -// To store jmethodIDs persistently, global references to the classes are -// required to prevent the classes from being unloaded and reloaded, potentially -// changing the method IDs. +// To store method and field IDs persistently, global references to the classes +// are required to prevent the classes from being unloaded and reloaded, +// potentially changing the IDs. static jclass android_system_application_context_class_ = nullptr; static jmethodID android_system_application_context_start_activity_ = nullptr; @@ -29,9 +29,6 @@ static jclass android_system_uri_class_ = nullptr; static jmethodID android_system_uri_parse_ = nullptr; static jclass android_system_intent_class_ = nullptr; -static jfieldID android_system_intent_action_view_field_id_ = nullptr; -static jfieldID android_system_intent_flag_activity_new_task_field_id_ = - nullptr; static jmethodID android_system_intent_init_action_uri_ = nullptr; static jmethodID android_system_intent_add_flags_ = nullptr; static jobject android_system_intent_action_view_ = nullptr; @@ -152,14 +149,14 @@ bool InitializeAndroidSystemForApplicationContext() { return false; } bool intent_ids_obtained = true; - intent_ids_obtained &= (android_system_intent_action_view_field_id_ = - jni_env->GetStaticFieldID( - android_system_intent_class_, "ACTION_VIEW", - "Ljava/lang/String;")) != nullptr; + jfieldID intent_action_view_id; + intent_ids_obtained &= (intent_action_view_id = jni_env->GetStaticFieldID( + android_system_intent_class_, "ACTION_VIEW", + "Ljava/lang/String;")) != nullptr; + jfieldID intent_flag_activity_new_task_id; intent_ids_obtained &= - (android_system_intent_flag_activity_new_task_field_id_ = - jni_env->GetStaticFieldID(android_system_intent_class_, - "FLAG_ACTIVITY_NEW_TASK", "I")) != + (intent_flag_activity_new_task_id = jni_env->GetStaticFieldID( + android_system_intent_class_, "FLAG_ACTIVITY_NEW_TASK", "I")) != nullptr; intent_ids_obtained &= (android_system_intent_init_action_uri_ = jni_env->GetMethodID( @@ -178,8 +175,7 @@ bool InitializeAndroidSystemForApplicationContext() { } { jobject intent_action_view_local_ref = jni_env->GetStaticObjectField( - android_system_intent_class_, - android_system_intent_action_view_field_id_); + android_system_intent_class_, intent_action_view_id); if (!intent_action_view_local_ref) { XELOGE( "InitializeAndroidSystemForApplicationContext: Failed to get the " @@ -199,8 +195,7 @@ bool InitializeAndroidSystemForApplicationContext() { } } android_system_intent_flag_activity_new_task_ = jni_env->GetStaticIntField( - android_system_intent_class_, - android_system_intent_flag_activity_new_task_field_id_); + android_system_intent_class_, intent_flag_activity_new_task_id); } android_system_initialized_ = true; @@ -212,8 +207,6 @@ void ShutdownAndroidSystem() { android_system_initialized_ = false; android_system_intent_add_flags_ = nullptr; android_system_intent_init_action_uri_ = nullptr; - android_system_intent_flag_activity_new_task_field_id_ = nullptr; - android_system_intent_action_view_field_id_ = nullptr; android_system_uri_parse_ = nullptr; android_system_application_context_start_activity_ = nullptr; JNIEnv* jni_env = GetAndroidThreadJNIEnv(); From c6fc8f706a122a597787c5c35eaa5c69cfa26b67 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Tue, 1 Feb 2022 21:33:20 +0300 Subject: [PATCH 85/88] [Base] GetAndroidThreadJniEnv capitals, move JNI usage tips there --- src/xenia/base/main_android.cc | 2 +- src/xenia/base/main_android.h | 11 ++++++++++- src/xenia/base/system_android.cc | 6 +++--- src/xenia/ui/windowed_app_context_android.h | 8 -------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/xenia/base/main_android.cc b/src/xenia/base/main_android.cc index e1ef53c72..a44ca7d3b 100644 --- a/src/xenia/base/main_android.cc +++ b/src/xenia/base/main_android.cc @@ -145,7 +145,7 @@ void ShutdownAndroidAppFromMainThread() { int32_t GetAndroidApiLevel() { return android_api_level_; } -JNIEnv* GetAndroidThreadJNIEnv() { +JNIEnv* GetAndroidThreadJniEnv() { if (!android_java_vm_) { return nullptr; } diff --git a/src/xenia/base/main_android.h b/src/xenia/base/main_android.h index 7fe35e038..e74a82ffe 100644 --- a/src/xenia/base/main_android.h +++ b/src/xenia/base/main_android.h @@ -38,9 +38,18 @@ void ShutdownAndroidAppFromMainThread(); // configuration. int32_t GetAndroidApiLevel(); +// Useful notes about JNI usage on Android within Xenia: +// - All static libraries defining JNI native functions must be linked to shared +// libraries via LOCAL_WHOLE_STATIC_LIBRARIES. +// - If method or field IDs are cached, a global reference to the class needs to +// be held - it prevents the class from being unloaded by the class loaders +// (in a way that would make the IDs invalid when it's reloaded). +// - GetStringUTFChars (UTF-8) returns null-terminated strings, GetStringChars +// (UTF-16) does not. + // May return null if not in a Java VM process, or in case of a failure to // attach on a non-main thread. -JNIEnv* GetAndroidThreadJNIEnv(); +JNIEnv* GetAndroidThreadJniEnv(); // Returns the global reference if in an application context, or null otherwise. // This is the application context, not the activity one, because multiple // activities may be running in one process. diff --git a/src/xenia/base/system_android.cc b/src/xenia/base/system_android.cc index bc1f34b35..25c14a7bc 100644 --- a/src/xenia/base/system_android.cc +++ b/src/xenia/base/system_android.cc @@ -39,7 +39,7 @@ static bool android_system_initialized_ = false; bool InitializeAndroidSystemForApplicationContext() { assert_false(android_system_initialized_); - JNIEnv* jni_env = GetAndroidThreadJNIEnv(); + JNIEnv* jni_env = GetAndroidThreadJniEnv(); if (!jni_env) { return false; } @@ -209,7 +209,7 @@ void ShutdownAndroidSystem() { android_system_intent_init_action_uri_ = nullptr; android_system_uri_parse_ = nullptr; android_system_application_context_start_activity_ = nullptr; - JNIEnv* jni_env = GetAndroidThreadJNIEnv(); + JNIEnv* jni_env = GetAndroidThreadJniEnv(); if (jni_env) { if (android_system_intent_action_view_) { jni_env->DeleteGlobalRef(android_system_intent_action_view_); @@ -234,7 +234,7 @@ void LaunchWebBrowser(const std::string_view url) { if (!android_system_initialized_) { return; } - JNIEnv* jni_env = GetAndroidThreadJNIEnv(); + JNIEnv* jni_env = GetAndroidThreadJniEnv(); if (!jni_env) { return; } diff --git a/src/xenia/ui/windowed_app_context_android.h b/src/xenia/ui/windowed_app_context_android.h index d3947938b..d12d25766 100644 --- a/src/xenia/ui/windowed_app_context_android.h +++ b/src/xenia/ui/windowed_app_context_android.h @@ -78,14 +78,6 @@ class AndroidWindowedAppContext final : public WindowedAppContext { bool InitializeApp(std::unique_ptr (*app_creator)( WindowedAppContext& app_context)); - // Useful notes about JNI usage on Android within Xenia: - // - All static libraries defining JNI native functions must be linked to - // shared libraries via LOCAL_WHOLE_STATIC_LIBRARIES. - // - If method or field IDs are cached, a global reference to the class needs - // to be held - it prevents the class from being unloaded by the class - // loaders (in a way that would make the IDs invalid when it's reloaded). - // - GetStringUTFChars (UTF-8) returns null-terminated strings, GetStringChars - // (UTF-16) does not. JNIEnv* ui_thread_jni_env_ = nullptr; // The object reference must be held by the app according to From 413d7ded490e1244311993d8e8546a753473c044 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Tue, 1 Feb 2022 22:18:04 +0300 Subject: [PATCH 86/88] [UI] Android surface [skip appveyor] --- .../android_studio_project/app/build.gradle | 4 + .../app/src/main/AndroidManifest.xml | 4 +- .../java/jp/xenia/XeniaRuntimeException.java | 6 +- .../jp/xenia/emulator/WindowDemoActivity.java | 10 ++ .../jp/xenia/emulator/WindowSurfaceView.java | 42 ++++++ .../xenia/emulator/WindowedAppActivity.java | 123 +++++++++++++++++- .../main/res/layout/activity_window_demo.xml | 7 +- .../app/src/main/res/values/strings.xml | 1 + src/xenia/ui/presenter.cc | 14 +- src/xenia/ui/presenter.h | 4 +- src/xenia/ui/window_android.cc | 52 +++++++- src/xenia/ui/window_android.h | 6 + src/xenia/ui/windowed_app_context_android.cc | 90 ++++++++++++- src/xenia/ui/windowed_app_context_android.h | 36 ++++- 14 files changed, 369 insertions(+), 30 deletions(-) create mode 100644 android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowSurfaceView.java diff --git a/android/android_studio_project/app/build.gradle b/android/android_studio_project/app/build.gradle index 4888e7d60..ae753e1b2 100644 --- a/android/android_studio_project/app/build.gradle +++ b/android/android_studio_project/app/build.gradle @@ -81,4 +81,8 @@ android { path file('../../../build/xenia.wks.Android.mk') } } +} + +dependencies { + implementation 'org.jetbrains:annotations:15.0' } \ No newline at end of file diff --git a/android/android_studio_project/app/src/main/AndroidManifest.xml b/android/android_studio_project/app/src/main/AndroidManifest.xml index 8f6d53cb0..25bf5a95f 100644 --- a/android/android_studio_project/app/src/main/AndroidManifest.xml +++ b/android/android_studio_project/app/src/main/AndroidManifest.xml @@ -29,7 +29,9 @@ android:supportsRtl="true" android:theme="@android:style/Theme.Material.Light"> - + diff --git a/android/android_studio_project/app/src/main/java/jp/xenia/XeniaRuntimeException.java b/android/android_studio_project/app/src/main/java/jp/xenia/XeniaRuntimeException.java index 93a046dfb..e5d324e78 100644 --- a/android/android_studio_project/app/src/main/java/jp/xenia/XeniaRuntimeException.java +++ b/android/android_studio_project/app/src/main/java/jp/xenia/XeniaRuntimeException.java @@ -7,15 +7,15 @@ public class XeniaRuntimeException extends RuntimeException { public XeniaRuntimeException() { } - public XeniaRuntimeException(String name) { + public XeniaRuntimeException(final String name) { super(name); } - public XeniaRuntimeException(String name, Throwable cause) { + public XeniaRuntimeException(final String name, final Throwable cause) { super(name, cause); } - public XeniaRuntimeException(Exception cause) { + public XeniaRuntimeException(final Exception cause) { super(cause); } } diff --git a/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowDemoActivity.java b/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowDemoActivity.java index a0dd36f0e..d03a07788 100644 --- a/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowDemoActivity.java +++ b/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowDemoActivity.java @@ -1,8 +1,18 @@ package jp.xenia.emulator; +import android.os.Bundle; + public class WindowDemoActivity extends WindowedAppActivity { @Override protected String getWindowedAppIdentifier() { return "xenia_ui_window_vulkan_demo"; } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_window_demo); + setWindowSurfaceView(findViewById(R.id.window_demo_surface_view)); + } } diff --git a/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowSurfaceView.java b/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowSurfaceView.java new file mode 100644 index 000000000..23b014632 --- /dev/null +++ b/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowSurfaceView.java @@ -0,0 +1,42 @@ +package jp.xenia.emulator; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.SurfaceView; + +public class WindowSurfaceView extends SurfaceView { + public WindowSurfaceView(final Context context) { + super(context); + // Native drawing is invoked from onDraw. + setWillNotDraw(false); + } + + public WindowSurfaceView(final Context context, final AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + } + + public WindowSurfaceView( + final Context context, final AttributeSet attrs, final int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWillNotDraw(false); + } + + public WindowSurfaceView( + final Context context, final AttributeSet attrs, final int defStyleAttr, + final int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + setWillNotDraw(false); + } + + @Override + protected void onDraw(final Canvas canvas) { + final Context context = getContext(); + if (!(context instanceof WindowedAppActivity)) { + return; + } + final WindowedAppActivity activity = (WindowedAppActivity) context; + activity.onWindowSurfaceDraw(false); + } +} diff --git a/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowedAppActivity.java b/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowedAppActivity.java index 9deb60358..073283529 100644 --- a/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowedAppActivity.java +++ b/android/android_studio_project/app/src/main/java/jp/xenia/emulator/WindowedAppActivity.java @@ -3,6 +3,11 @@ package jp.xenia.emulator; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.View; + +import org.jetbrains.annotations.Nullable; import jp.xenia.XeniaRuntimeException; @@ -12,21 +17,87 @@ public abstract class WindowedAppActivity extends Activity { System.loadLibrary("xenia-ui-window-vulkan-demo"); } - private long mAppContext; + private final WindowSurfaceOnLayoutChangeListener mWindowSurfaceOnLayoutChangeListener = + new WindowSurfaceOnLayoutChangeListener(); + private final WindowSurfaceHolderCallback mWindowSurfaceHolderCallback = + new WindowSurfaceHolderCallback(); - private native long initializeWindowedAppOnCreateNative( + // May be 0 while destroying (mainly while the superclass is). + private long mAppContext = 0; + + @Nullable + private WindowSurfaceView mWindowSurfaceView = null; + + private native long initializeWindowedAppOnCreate( String windowedAppIdentifier, AssetManager assetManager); private native void onDestroyNative(long appContext); + private native void onWindowSurfaceLayoutChange( + long appContext, int left, int top, int right, int bottom); + + private native void onWindowSurfaceChanged(long appContext, Surface windowSurface); + + private native void paintWindow(long appContext, boolean forcePaint); + protected abstract String getWindowedAppIdentifier(); + protected void setWindowSurfaceView(@Nullable final WindowSurfaceView windowSurfaceView) { + if (mWindowSurfaceView == windowSurfaceView) { + return; + } + + // Detach from the old surface. + if (mWindowSurfaceView != null) { + mWindowSurfaceView.getHolder().removeCallback(mWindowSurfaceHolderCallback); + mWindowSurfaceView.removeOnLayoutChangeListener(mWindowSurfaceOnLayoutChangeListener); + mWindowSurfaceView = null; + if (mAppContext != 0) { + onWindowSurfaceChanged(mAppContext, null); + } + } + + if (windowSurfaceView == null) { + return; + } + + mWindowSurfaceView = windowSurfaceView; + // The native window code assumes that, when the surface exists, it covers the entire + // window. + // FIXME(Triang3l): This doesn't work if the layout has already been performed. + mWindowSurfaceView.addOnLayoutChangeListener(mWindowSurfaceOnLayoutChangeListener); + final SurfaceHolder windowSurfaceHolder = mWindowSurfaceView.getHolder(); + windowSurfaceHolder.addCallback(mWindowSurfaceHolderCallback); + // If setting after the creation of the surface. + if (mAppContext != 0) { + final Surface windowSurface = windowSurfaceHolder.getSurface(); + if (windowSurface != null) { + onWindowSurfaceChanged(mAppContext, windowSurface); + } + } + } + + public void onWindowSurfaceDraw(final boolean forcePaint) { + if (mAppContext == 0) { + return; + } + paintWindow(mAppContext, forcePaint); + } + + // Used from the native WindowedAppContext. May be called from non-UI threads. + protected void postInvalidateWindowSurface() { + if (mWindowSurfaceView == null) { + return; + } + mWindowSurfaceView.postInvalidate(); + } + @Override - protected void onCreate(Bundle savedInstanceState) { + protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final String windowedAppIdentifier = getWindowedAppIdentifier(); - mAppContext = initializeWindowedAppOnCreateNative(windowedAppIdentifier, getAssets()); + mAppContext = initializeWindowedAppOnCreate(windowedAppIdentifier, getAssets()); if (mAppContext == 0) { finish(); throw new XeniaRuntimeException( @@ -36,10 +107,54 @@ public abstract class WindowedAppActivity extends Activity { @Override protected void onDestroy() { + setWindowSurfaceView(null); if (mAppContext != 0) { onDestroyNative(mAppContext); } mAppContext = 0; super.onDestroy(); } + + private class WindowSurfaceOnLayoutChangeListener implements View.OnLayoutChangeListener { + @Override + public void onLayoutChange( + final View v, final int left, final int top, final int right, final int bottom, + final int oldLeft, final int oldTop, final int oldRight, final int oldBottom) { + if (mAppContext != 0) { + onWindowSurfaceLayoutChange(mAppContext, left, top, right, bottom); + } + } + } + + private class WindowSurfaceHolderCallback implements SurfaceHolder.Callback2 { + @Override + public void surfaceCreated(final SurfaceHolder holder) { + if (mAppContext == 0) { + return; + } + onWindowSurfaceChanged(mAppContext, holder.getSurface()); + } + + @Override + public void surfaceChanged( + final SurfaceHolder holder, final int format, final int width, final int height) { + if (mAppContext == 0) { + return; + } + onWindowSurfaceChanged(mAppContext, holder.getSurface()); + } + + @Override + public void surfaceDestroyed(final SurfaceHolder holder) { + if (mAppContext == 0) { + return; + } + onWindowSurfaceChanged(mAppContext, null); + } + + @Override + public void surfaceRedrawNeeded(final SurfaceHolder holder) { + onWindowSurfaceDraw(true); + } + } } diff --git a/android/android_studio_project/app/src/main/res/layout/activity_window_demo.xml b/android/android_studio_project/app/src/main/res/layout/activity_window_demo.xml index 79f49f81a..3bbd1211b 100644 --- a/android/android_studio_project/app/src/main/res/layout/activity_window_demo.xml +++ b/android/android_studio_project/app/src/main/res/layout/activity_window_demo.xml @@ -1,8 +1,7 @@ - - - \ No newline at end of file + tools:context="jp.xenia.emulator.WindowDemoActivity" /> diff --git a/android/android_studio_project/app/src/main/res/values/strings.xml b/android/android_studio_project/app/src/main/res/values/strings.xml index 6379b7bcb..ed3f21a79 100644 --- a/android/android_studio_project/app/src/main/res/values/strings.xml +++ b/android/android_studio_project/app/src/main/res/values/strings.xml @@ -1,3 +1,4 @@ Xenia + Xenia Window Demo \ No newline at end of file diff --git a/src/xenia/ui/presenter.cc b/src/xenia/ui/presenter.cc index aef8f7309..661a37a8a 100644 --- a/src/xenia/ui/presenter.cc +++ b/src/xenia/ui/presenter.cc @@ -26,13 +26,13 @@ // presenting from the thread refreshing the guest output is not absolutely // necessary, but still may be nice for bypassing the scheduling and the // message queue. -// On GTK, the frame rate of draw signals is limited to the display refresh rate -// internally, so for the lowest latency especially in case the refresh rates -// differ significantly on the guest and the host (like 30/60 Hz presented to -// 144 Hz), drawing from the guest output refreshing thread is highly desirable. -// Presenting directly from the GPU emulation thread also makes debugging GPU -// emulation easier with external tools, as presenting in most cases happens -// exactly between emulation frames. +// On Android and GTK, the frame rate of draw events is limited to the display +// refresh rate internally, so for the lowest latency especially in case the +// refresh rates differ significantly on the guest and the host (like 30/60 Hz +// presented to 144 Hz), drawing from the guest output refreshing thread is +// highly desirable. Presenting directly from the GPU emulation thread also +// makes debugging GPU emulation easier with external tools, as presenting in +// most cases happens exactly between emulation frames. DEFINE_bool( host_present_from_non_ui_thread, true, "Allow the GPU emulation thread to present the guest output to the host " diff --git a/src/xenia/ui/presenter.h b/src/xenia/ui/presenter.h index 9e89c8573..5ead8cb90 100644 --- a/src/xenia/ui/presenter.h +++ b/src/xenia/ui/presenter.h @@ -974,8 +974,8 @@ class Presenter { // frame rates wasting the CPU and the GPU resources and starving everything // else. The waits performed here must be interruptible by guest output // presentation requests to prevent adding arbitrary amounts of latency to it. - // On GTK, this is not needed, the frame rate of draw signals is limited to - // the display refresh rate internally. + // On Android and GTK, this is not needed, the frame rate of draw events is + // limited to the display refresh rate internally. #if XE_PLATFORM_WIN32 static Microsoft::WRL::ComPtr GetDXGIOutputForMonitor( IDXGIFactory1* factory, HMONITOR monitor); diff --git a/src/xenia/ui/window_android.cc b/src/xenia/ui/window_android.cc index c653df217..817f439c7 100644 --- a/src/xenia/ui/window_android.cc +++ b/src/xenia/ui/window_android.cc @@ -29,17 +29,42 @@ std::unique_ptr Window::Create(WindowedAppContext& app_context, AndroidWindow::~AndroidWindow() { EnterDestructor(); - AndroidWindowedAppContext& android_app_context = + auto& android_app_context = static_cast(app_context()); if (android_app_context.GetActivityWindow() == this) { android_app_context.SetActivityWindow(nullptr); } } +void AndroidWindow::OnActivitySurfaceLayoutChange() { + auto& android_app_context = + static_cast(app_context()); + assert_true(android_app_context.GetActivityWindow() == this); + uint32_t physical_width = + uint32_t(android_app_context.window_surface_layout_right() - + android_app_context.window_surface_layout_left()); + uint32_t physical_height = + uint32_t(android_app_context.window_surface_layout_bottom() - + android_app_context.window_surface_layout_top()); + OnDesiredLogicalSizeUpdate(SizeToLogical(physical_width), + SizeToLogical(physical_height)); + WindowDestructionReceiver destruction_receiver(this); + OnActualSizeUpdate(physical_width, physical_height, destruction_receiver); + if (destruction_receiver.IsWindowDestroyedOrClosed()) { + return; + } +} + +uint32_t AndroidWindow::GetLatestDpiImpl() const { + auto& android_app_context = + static_cast(app_context()); + return android_app_context.GetPixelDensity(); +} + bool AndroidWindow::OpenImpl() { // The window is a proxy between the main activity and Xenia, so there can be - // only one for an activity. - AndroidWindowedAppContext& android_app_context = + // only one open window for an activity. + auto& android_app_context = static_cast(app_context()); AndroidWindow* previous_activity_window = android_app_context.GetActivityWindow(); @@ -50,6 +75,10 @@ bool AndroidWindow::OpenImpl() { return false; } android_app_context.SetActivityWindow(this); + + // Report the initial layout. + OnActivitySurfaceLayoutChange(); + return true; } @@ -68,7 +97,7 @@ void AndroidWindow::RequestCloseImpl() { } OnAfterClose(); - AndroidWindowedAppContext& android_app_context = + auto& android_app_context = static_cast(app_context()); if (android_app_context.GetActivityWindow() == this) { android_app_context.SetActivityWindow(nullptr); @@ -78,13 +107,24 @@ void AndroidWindow::RequestCloseImpl() { std::unique_ptr AndroidWindow::CreateSurfaceImpl( Surface::TypeFlags allowed_types) { if (allowed_types & Surface::kTypeFlag_AndroidNativeWindow) { - // TODO(Triang3l): AndroidNativeWindowSurface for the ANativeWindow. + auto& android_app_context = + static_cast(app_context()); + assert_true(android_app_context.GetActivityWindow() == this); + ANativeWindow* activity_window_surface = + android_app_context.GetWindowSurface(); + if (activity_window_surface) { + return std::make_unique( + activity_window_surface); + } } return nullptr; } void AndroidWindow::RequestPaintImpl() { - // TODO(Triang3l): postInvalidate. + auto& android_app_context = + static_cast(app_context()); + assert_true(android_app_context.GetActivityWindow() == this); + android_app_context.PostInvalidateWindowSurface(); } std::unique_ptr MenuItem::Create(Type type, diff --git a/src/xenia/ui/window_android.h b/src/xenia/ui/window_android.h index 74832576d..52fac19ec 100644 --- a/src/xenia/ui/window_android.h +++ b/src/xenia/ui/window_android.h @@ -29,7 +29,13 @@ class AndroidWindow : public Window { uint32_t GetMediumDpi() const override { return 160; } + void OnActivitySurfaceLayoutChange(); + void OnActivitySurfaceChanged() { OnSurfaceChanged(true); } + void PaintActivitySurface(bool force_paint) { OnPaint(force_paint); } + protected: + uint32_t GetLatestDpiImpl() const override; + bool OpenImpl() override; void RequestCloseImpl() override; diff --git a/src/xenia/ui/windowed_app_context_android.cc b/src/xenia/ui/windowed_app_context_android.cc index 66d1d42ba..2deb4e95e 100644 --- a/src/xenia/ui/windowed_app_context_android.cc +++ b/src/xenia/ui/windowed_app_context_android.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -22,6 +24,7 @@ #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/main_android.h" +#include "xenia/ui/window_android.h" #include "xenia/ui/windowed_app.h" namespace xe { @@ -52,6 +55,16 @@ void AndroidWindowedAppContext::PlatformQuitFromUIThread() { } } +void AndroidWindowedAppContext::PostInvalidateWindowSurface() { + // May be called from non-UI threads. + JNIEnv* jni_env = GetAndroidThreadJniEnv(); + if (!jni_env) { + return; + } + jni_env->CallVoidMethod(activity_, + activity_method_post_invalidate_window_surface_); +} + AndroidWindowedAppContext* AndroidWindowedAppContext::JniActivityInitializeWindowedAppOnCreate( JNIEnv* jni_env, jobject activity, jstring windowed_app_identifier, @@ -100,9 +113,54 @@ void AndroidWindowedAppContext::JniActivityOnDestroy() { app_->InvokeOnDestroy(); app_.reset(); } + // Expecting that the destruction of the app will destroy the window as well, + // no need to notify it explicitly. + assert_null(activity_window_); RequestDestruction(); } +void AndroidWindowedAppContext::JniActivityOnWindowSurfaceLayoutChange( + jint left, jint top, jint right, jint bottom) { + window_surface_layout_left_ = left; + window_surface_layout_top_ = top; + window_surface_layout_right_ = right; + window_surface_layout_bottom_ = bottom; + if (activity_window_) { + activity_window_->OnActivitySurfaceLayoutChange(); + } +} + +void AndroidWindowedAppContext::JniActivityOnWindowSurfaceChanged( + jobject window_surface_object) { + // Detach from the old surface. + if (window_surface_) { + ANativeWindow* old_window_surface = window_surface_; + window_surface_ = nullptr; + if (activity_window_) { + activity_window_->OnActivitySurfaceChanged(); + } + ANativeWindow_release(old_window_surface); + } + if (!window_surface_object) { + return; + } + window_surface_ = + ANativeWindow_fromSurface(ui_thread_jni_env_, window_surface_object); + if (!window_surface_) { + return; + } + if (activity_window_) { + activity_window_->OnActivitySurfaceChanged(); + } +} + +void AndroidWindowedAppContext::JniActivityPaintWindow(bool force_paint) { + if (!activity_window_) { + return; + } + activity_window_->PaintActivitySurface(force_paint); +} + AndroidWindowedAppContext::~AndroidWindowedAppContext() { Shutdown(); } bool AndroidWindowedAppContext::Initialize(JNIEnv* ui_thread_jni_env, @@ -205,6 +263,11 @@ bool AndroidWindowedAppContext::Initialize(JNIEnv* ui_thread_jni_env, activity_ids_obtained &= (activity_method_finish_ = ui_thread_jni_env_->GetMethodID( activity_class_, "finish", "()V")) != nullptr; + activity_ids_obtained &= + (activity_method_post_invalidate_window_surface_ = + ui_thread_jni_env_->GetMethodID( + activity_class_, "postInvalidateWindowSurface", "()V")) != + nullptr; if (!activity_ids_obtained) { XELOGE("AndroidWindowedAppContext: Failed to get the activity class IDs"); Shutdown(); @@ -407,7 +470,7 @@ bool AndroidWindowedAppContext::InitializeApp(std::unique_ptr ( extern "C" { JNIEXPORT jlong JNICALL -Java_jp_xenia_emulator_WindowedAppActivity_initializeWindowedAppOnCreateNative( +Java_jp_xenia_emulator_WindowedAppActivity_initializeWindowedAppOnCreate( JNIEnv* jni_env, jobject activity, jstring windowed_app_identifier, jobject asset_manager) { return reinterpret_cast( @@ -423,4 +486,27 @@ Java_jp_xenia_emulator_WindowedAppActivity_onDestroyNative( ->JniActivityOnDestroy(); } +JNIEXPORT void JNICALL +Java_jp_xenia_emulator_WindowedAppActivity_onWindowSurfaceLayoutChange( + JNIEnv* jni_env, jobject activity, jlong app_context_ptr, jint left, + jint top, jint right, jint bottom) { + reinterpret_cast(app_context_ptr) + ->JniActivityOnWindowSurfaceLayoutChange(left, top, right, bottom); +} + +JNIEXPORT void JNICALL +Java_jp_xenia_emulator_WindowedAppActivity_onWindowSurfaceChanged( + JNIEnv* jni_env, jobject activity, jlong app_context_ptr, + jobject window_surface_object) { + reinterpret_cast(app_context_ptr) + ->JniActivityOnWindowSurfaceChanged(window_surface_object); +} + +JNIEXPORT void JNICALL Java_jp_xenia_emulator_WindowedAppActivity_paintWindow( + JNIEnv* jni_env, jobject activity, jlong app_context_ptr, + jboolean force_paint) { + reinterpret_cast(app_context_ptr) + ->JniActivityPaintWindow(bool(force_paint)); +} + } // extern "C" diff --git a/src/xenia/ui/windowed_app_context_android.h b/src/xenia/ui/windowed_app_context_android.h index d12d25766..022826fc1 100644 --- a/src/xenia/ui/windowed_app_context_android.h +++ b/src/xenia/ui/windowed_app_context_android.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2021 Ben Vanik. All rights reserved. * + * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,27 @@ class AndroidWindowedAppContext final : public WindowedAppContext { void PlatformQuitFromUIThread() override; + uint32_t GetPixelDensity() const { + return configuration_ ? uint32_t(AConfiguration_getDensity(configuration_)) + : 160; + } + + int32_t window_surface_layout_left() const { + return window_surface_layout_left_; + } + int32_t window_surface_layout_top() const { + return window_surface_layout_top_; + } + int32_t window_surface_layout_right() const { + return window_surface_layout_right_; + } + int32_t window_surface_layout_bottom() const { + return window_surface_layout_bottom_; + } + + ANativeWindow* GetWindowSurface() const { return window_surface_; } + void PostInvalidateWindowSurface(); + // The single Window instance that will be receiving window callbacks. // Multiple windows cannot be created as one activity or fragment can have // only one layout. This window acts purely as a proxy between the activity @@ -46,6 +68,10 @@ class AndroidWindowedAppContext final : public WindowedAppContext { JNIEnv* jni_env, jobject activity, jstring windowed_app_identifier, jobject asset_manager); void JniActivityOnDestroy(); + void JniActivityOnWindowSurfaceLayoutChange(jint left, jint top, jint right, + jint bottom); + void JniActivityOnWindowSurfaceChanged(jobject window_surface_object); + void JniActivityPaintWindow(bool force_paint); private: enum class UIThreadLooperCallbackCommand : uint8_t { @@ -93,6 +119,7 @@ class AndroidWindowedAppContext final : public WindowedAppContext { jobject activity_ = nullptr; jmethodID activity_method_finish_ = nullptr; + jmethodID activity_method_post_invalidate_window_surface_ = nullptr; // May be read by non-UI threads in NotifyUILoopOfPendingFunctions. ALooper* ui_thread_looper_ = nullptr; @@ -101,6 +128,13 @@ class AndroidWindowedAppContext final : public WindowedAppContext { std::array ui_thread_looper_callback_pipe_{-1, -1}; bool ui_thread_looper_callback_registered_ = false; + int32_t window_surface_layout_left_ = 0; + int32_t window_surface_layout_top_ = 0; + int32_t window_surface_layout_right_ = 0; + int32_t window_surface_layout_bottom_ = 0; + + ANativeWindow* window_surface_ = nullptr; + AndroidWindow* activity_window_ = nullptr; std::unique_ptr app_; From 52ec0acd0c53ef5e498c548980dfc8be04d2f308 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Tue, 1 Feb 2022 22:29:14 +0300 Subject: [PATCH 87/88] [App] Add text saying that post-processing is vendor-independent --- src/xenia/app/emulator_window.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index c8374edd4..a4dbb8786 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -271,6 +271,10 @@ void EmulatorWindow::DisplayConfigDialog::OnDraw(ImGuiIO& io) { // Even if the close button has been pressed, still paint everything not to // have one frame with an empty window. + // Prevent user confusion which has been reported multiple times. + ImGui::TextUnformatted("All effects can be used on GPUs of any brand."); + ImGui::Spacing(); + gpu::CommandProcessor* command_processor = graphics_system->command_processor(); if (command_processor) { From 7977d7ab982bc1db3a86e67720709a5f89eed01a Mon Sep 17 00:00:00 2001 From: Gliniak Date: Tue, 1 Feb 2022 08:11:55 +0100 Subject: [PATCH 88/88] [Base] Changed entry point to wmain for Windows This prevents subapps from crashing when executing wmain specific functions --- src/xenia/base/console_app_main_win.cc | 3 ++- src/xenia/base/console_win.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/xenia/base/console_app_main_win.cc b/src/xenia/base/console_app_main_win.cc index cd2a38dc4..6cc700eec 100644 --- a/src/xenia/base/console_app_main_win.cc +++ b/src/xenia/base/console_app_main_win.cc @@ -13,7 +13,8 @@ #include "xenia/base/console_app_main.h" #include "xenia/base/main_win.h" -int main(int argc_ignored, char** argv_ignored) { +// A wide character entry point is required for functions like _get_wpgmptr. +int wmain(int argc_ignored, wchar_t** argv_ignored) { xe::ConsoleAppEntryInfo entry_info = xe::GetConsoleAppEntryInfo(); std::vector args; diff --git a/src/xenia/base/console_win.cc b/src/xenia/base/console_win.cc index e6b8fb887..252e99b84 100644 --- a/src/xenia/base/console_win.cc +++ b/src/xenia/base/console_win.cc @@ -27,7 +27,7 @@ static bool has_shell_environment_variable() { size_t size = 0; // Check if SHELL exists // If it doesn't, then we are in a Windows Terminal - auto error = getenv_s(&size, nullptr, 0, "SHELL"); + auto error = _wgetenv_s(&size, nullptr, 0, L"SHELL"); if (error) { return false; }

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"%DiFLw$V7-6ViT5pO+bW-=;Mt} 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ⅅ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øJzPT8BJtl;hF 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