Merge branch 'vulkan'

This commit is contained in:
Dr. Chat 2016-06-11 19:23:23 -05:00
commit 841f6fe239
164 changed files with 51895 additions and 4707 deletions

View File

@ -165,10 +165,12 @@ solution("xenia")
include("third_party/capstone.lua") include("third_party/capstone.lua")
include("third_party/gflags.lua") include("third_party/gflags.lua")
include("third_party/glew.lua") include("third_party/glew.lua")
include("third_party/glslang-spirv.lua")
include("third_party/imgui.lua") include("third_party/imgui.lua")
include("third_party/libav.lua") include("third_party/libav.lua")
include("third_party/snappy.lua") include("third_party/snappy.lua")
include("third_party/spirv-tools.lua") include("third_party/spirv-tools.lua")
include("third_party/vulkan/loader")
include("third_party/xxhash.lua") include("third_party/xxhash.lua")
include("third_party/yaml-cpp.lua") include("third_party/yaml-cpp.lua")
@ -182,12 +184,14 @@ solution("xenia")
include("src/xenia/debug/ui") include("src/xenia/debug/ui")
include("src/xenia/gpu") include("src/xenia/gpu")
include("src/xenia/gpu/gl4") include("src/xenia/gpu/gl4")
include("src/xenia/gpu/vulkan")
include("src/xenia/hid") include("src/xenia/hid")
include("src/xenia/hid/nop") include("src/xenia/hid/nop")
include("src/xenia/kernel") include("src/xenia/kernel")
include("src/xenia/ui") include("src/xenia/ui")
include("src/xenia/ui/gl") include("src/xenia/ui/gl")
include("src/xenia/ui/spirv") include("src/xenia/ui/spirv")
include("src/xenia/ui/vulkan")
include("src/xenia/vfs") include("src/xenia/vfs")
if os.is("windows") then if os.is("windows") then

View File

@ -19,6 +19,7 @@ project("xenia-app")
"xenia-debug-ui", "xenia-debug-ui",
"xenia-gpu", "xenia-gpu",
"xenia-gpu-gl4", "xenia-gpu-gl4",
"xenia-gpu-vulkan",
"xenia-hid-nop", "xenia-hid-nop",
"xenia-kernel", "xenia-kernel",
"xenia-ui", "xenia-ui",

View File

@ -25,6 +25,7 @@
// Available graphics systems: // Available graphics systems:
#include "xenia/gpu/gl4/gl4_graphics_system.h" #include "xenia/gpu/gl4/gl4_graphics_system.h"
#include "xenia/gpu/vulkan/vulkan_graphics_system.h"
// Available input drivers: // Available input drivers:
#include "xenia/hid/nop/nop_hid.h" #include "xenia/hid/nop/nop_hid.h"
@ -34,7 +35,7 @@
#endif // XE_PLATFORM_WIN32 #endif // XE_PLATFORM_WIN32
DEFINE_string(apu, "any", "Audio system. Use: [any, nop, xaudio2]"); DEFINE_string(apu, "any", "Audio system. Use: [any, nop, xaudio2]");
DEFINE_string(gpu, "any", "Graphics system. Use: [any, gl4]"); DEFINE_string(gpu, "any", "Graphics system. Use: [any, gl4, vulkan]");
DEFINE_string(hid, "any", "Input system. Use: [any, nop, winkey, xinput]"); DEFINE_string(hid, "any", "Input system. Use: [any, nop, winkey, xinput]");
DEFINE_string(target, "", "Specifies the target .xex or .iso to execute."); DEFINE_string(target, "", "Specifies the target .xex or .iso to execute.");
@ -69,6 +70,9 @@ std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() {
if (FLAGS_gpu.compare("gl4") == 0) { if (FLAGS_gpu.compare("gl4") == 0) {
return std::unique_ptr<gpu::GraphicsSystem>( return std::unique_ptr<gpu::GraphicsSystem>(
new xe::gpu::gl4::GL4GraphicsSystem()); new xe::gpu::gl4::GL4GraphicsSystem());
} else if (FLAGS_gpu.compare("vulkan") == 0) {
return std::unique_ptr<gpu::GraphicsSystem>(
new xe::gpu::vulkan::VulkanGraphicsSystem());
} else { } else {
// Create best available. // Create best available.
std::unique_ptr<gpu::GraphicsSystem> best; std::unique_ptr<gpu::GraphicsSystem> best;

View File

@ -64,6 +64,22 @@ constexpr uint32_t select_bits(uint32_t value, uint32_t a, uint32_t b) {
return (value & make_bitmask(a, b)) >> a; return (value & make_bitmask(a, b)) >> a;
} }
inline uint32_t bit_count(uint32_t v) {
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
}
inline uint32_t bit_count(uint64_t v) {
v = (v & 0x5555555555555555LU) + (v >> 1 & 0x5555555555555555LU);
v = (v & 0x3333333333333333LU) + (v >> 2 & 0x3333333333333333LU);
v = v + (v >> 4) & 0x0F0F0F0F0F0F0F0FLU;
v = v + (v >> 8);
v = v + (v >> 16);
v = v + (v >> 32) & 0x0000007F;
return static_cast<uint32_t>(v);
}
// lzcnt instruction, typed for integers of all sizes. // lzcnt instruction, typed for integers of all sizes.
// The number of leading zero bits in the value parameter. If value is zero, the // The number of leading zero bits in the value parameter. If value is zero, the
// return value is the size of the input operand (8, 16, 32, or 64). If the most // return value is the size of the input operand (8, 16, 32, or 64). If the most

View File

@ -18,109 +18,103 @@ namespace xe {
// http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/f2bc76cc65ffba51a141950f98e75364e49df874/entry/volk/kernels/volk/volk_32u_byteswap.h // http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/f2bc76cc65ffba51a141950f98e75364e49df874/entry/volk/kernels/volk/volk_32u_byteswap.h
// http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/2c4c371885c31222362f70a1cd714415d1398021/entry/volk/kernels/volk/volk_64u_byteswap.h // http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/2c4c371885c31222362f70a1cd714415d1398021/entry/volk/kernels/volk/volk_64u_byteswap.h
void copy_and_swap_16_aligned(uint16_t* dest, const uint16_t* src, void copy_128_aligned(void* dest, const void* src, size_t count) {
size_t count) { std::memcpy(dest, src, count * 16);
}
void copy_and_swap_16_aligned(void* dest, const void* src, size_t count) {
return copy_and_swap_16_unaligned(dest, src, count); return copy_and_swap_16_unaligned(dest, src, count);
} }
void copy_and_swap_16_unaligned(uint16_t* dest, const uint16_t* src, void copy_and_swap_16_unaligned(void* dest_ptr, const void* src_ptr,
size_t count) { size_t count) {
auto dest = reinterpret_cast<uint16_t*>(dest_ptr);
auto src = reinterpret_cast<const uint16_t*>(src_ptr);
size_t i; size_t i;
__m128i input, output;
for (i = 0; i + 8 <= count; i += 8) { for (i = 0; i + 8 <= count; i += 8) {
input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i])); __m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
output = _mm_or_si128(_mm_slli_epi16(input, 8), _mm_srli_epi16(input, 8)); __m128i output =
_mm_or_si128(_mm_slli_epi16(input, 8), _mm_srli_epi16(input, 8));
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output); _mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
} }
for (; i < count; ++i) { // handle residual elements for (; i < count; ++i) { // handle residual elements
dest[i] = byte_swap(src[i]); dest[i] = byte_swap(src[i]);
} }
} }
void copy_and_swap_32_aligned(uint32_t* dest, const uint32_t* src, void copy_and_swap_32_aligned(void* dest, const void* src, size_t count) {
size_t count) {
return copy_and_swap_32_unaligned(dest, src, count); return copy_and_swap_32_unaligned(dest, src, count);
} }
void copy_and_swap_32_unaligned(uint32_t* dest, const uint32_t* src, void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr,
size_t count) { size_t count) {
size_t i; auto dest = reinterpret_cast<uint32_t*>(dest_ptr);
__m128i input, byte1, byte2, byte3, byte4, output; auto src = reinterpret_cast<const uint32_t*>(src_ptr);
__m128i byte2mask = _mm_set1_epi32(0x00FF0000); __m128i byte2mask = _mm_set1_epi32(0x00FF0000);
__m128i byte3mask = _mm_set1_epi32(0x0000FF00); __m128i byte3mask = _mm_set1_epi32(0x0000FF00);
size_t i;
for (i = 0; i + 4 <= count; i += 4) { for (i = 0; i + 4 <= count; i += 4) {
input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i])); __m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
// Do the four shifts.
// Do the four shifts __m128i byte1 = _mm_slli_epi32(input, 24);
byte1 = _mm_slli_epi32(input, 24); __m128i byte2 = _mm_slli_epi32(input, 8);
byte2 = _mm_slli_epi32(input, 8); __m128i byte3 = _mm_srli_epi32(input, 8);
byte3 = _mm_srli_epi32(input, 8); __m128i byte4 = _mm_srli_epi32(input, 24);
byte4 = _mm_srli_epi32(input, 24); // OR bytes together.
__m128i output = _mm_or_si128(byte1, byte4);
// Or bytes together
output = _mm_or_si128(byte1, byte4);
byte2 = _mm_and_si128(byte2, byte2mask); byte2 = _mm_and_si128(byte2, byte2mask);
output = _mm_or_si128(output, byte2); output = _mm_or_si128(output, byte2);
byte3 = _mm_and_si128(byte3, byte3mask); byte3 = _mm_and_si128(byte3, byte3mask);
output = _mm_or_si128(output, byte3); output = _mm_or_si128(output, byte3);
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output); _mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
} }
for (; i < count; ++i) { // handle residual elements for (; i < count; ++i) { // handle residual elements
dest[i] = byte_swap(src[i]); dest[i] = byte_swap(src[i]);
} }
} }
void copy_and_swap_64_aligned(uint64_t* dest, const uint64_t* src, void copy_and_swap_64_aligned(void* dest, const void* src, size_t count) {
size_t count) {
return copy_and_swap_64_unaligned(dest, src, count); return copy_and_swap_64_unaligned(dest, src, count);
} }
void copy_and_swap_64_unaligned(uint64_t* dest, const uint64_t* src, void copy_and_swap_64_unaligned(void* dest_ptr, const void* src_ptr,
size_t count) { size_t count) {
size_t i; auto dest = reinterpret_cast<uint64_t*>(dest_ptr);
__m128i input, byte1, byte2, byte3, byte4, output; auto src = reinterpret_cast<const uint64_t*>(src_ptr);
__m128i byte2mask = _mm_set1_epi32(0x00FF0000); __m128i byte2mask = _mm_set1_epi32(0x00FF0000);
__m128i byte3mask = _mm_set1_epi32(0x0000FF00); __m128i byte3mask = _mm_set1_epi32(0x0000FF00);
size_t i;
for (i = 0; i + 2 <= count; i += 2) { for (i = 0; i + 2 <= count; i += 2) {
input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i])); __m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
// Do the four shifts.
// Do the four shifts __m128i byte1 = _mm_slli_epi32(input, 24);
byte1 = _mm_slli_epi32(input, 24); __m128i byte2 = _mm_slli_epi32(input, 8);
byte2 = _mm_slli_epi32(input, 8); __m128i byte3 = _mm_srli_epi32(input, 8);
byte3 = _mm_srli_epi32(input, 8); __m128i byte4 = _mm_srli_epi32(input, 24);
byte4 = _mm_srli_epi32(input, 24); // OR bytes together.
__m128i output = _mm_or_si128(byte1, byte4);
// Or bytes together
output = _mm_or_si128(byte1, byte4);
byte2 = _mm_and_si128(byte2, byte2mask); byte2 = _mm_and_si128(byte2, byte2mask);
output = _mm_or_si128(output, byte2); output = _mm_or_si128(output, byte2);
byte3 = _mm_and_si128(byte3, byte3mask); byte3 = _mm_and_si128(byte3, byte3mask);
output = _mm_or_si128(output, byte3); output = _mm_or_si128(output, byte3);
// Reorder the two words.
// Reorder the two words
output = _mm_shuffle_epi32(output, _MM_SHUFFLE(2, 3, 0, 1)); output = _mm_shuffle_epi32(output, _MM_SHUFFLE(2, 3, 0, 1));
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output); _mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
} }
for (; i < count; ++i) { // handle residual elements for (; i < count; ++i) { // handle residual elements
dest[i] = byte_swap(src[i]); dest[i] = byte_swap(src[i]);
} }
} }
void copy_and_swap_16_in_32_aligned(uint32_t* dest, const uint32_t* src, void copy_and_swap_16_in_32_aligned(void* dest_ptr, const void* src_ptr,
size_t count) { size_t count) {
auto dest = reinterpret_cast<uint64_t*>(dest_ptr);
auto src = reinterpret_cast<const uint64_t*>(src_ptr);
size_t i; size_t i;
__m128i input, output;
for (i = 0; i + 4 <= count; i += 4) { for (i = 0; i + 4 <= count; i += 4) {
input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i])); __m128i input = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&src[i]));
output = _mm_or_si128(_mm_slli_epi32(input, 16), _mm_srli_epi32(input, 16)); __m128i output =
_mm_or_si128(_mm_slli_epi32(input, 16), _mm_srli_epi32(input, 16));
_mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output); _mm_storeu_si128(reinterpret_cast<__m128i*>(&dest[i]), output);
} }
for (; i < count; ++i) { // handle residual elements for (; i < count; ++i) { // handle residual elements

View File

@ -121,20 +121,15 @@ inline void* low_address(void* address) {
return reinterpret_cast<void*>(uint64_t(address) & 0xFFFFFFFF); return reinterpret_cast<void*>(uint64_t(address) & 0xFFFFFFFF);
} }
void copy_and_swap_16_aligned(uint16_t* dest, const uint16_t* src, void copy_128_aligned(void* dest, const void* src, size_t count);
size_t count);
void copy_and_swap_16_unaligned(uint16_t* dest, const uint16_t* src, void copy_and_swap_16_aligned(void* dest, const void* src, size_t count);
size_t count); void copy_and_swap_16_unaligned(void* dest, const void* src, size_t count);
void copy_and_swap_32_aligned(uint32_t* dest, const uint32_t* src, void copy_and_swap_32_aligned(void* dest, const void* src, size_t count);
size_t count); void copy_and_swap_32_unaligned(void* dest, const void* src, size_t count);
void copy_and_swap_32_unaligned(uint32_t* dest, const uint32_t* src, void copy_and_swap_64_aligned(void* dest, const void* src, size_t count);
size_t count); void copy_and_swap_64_unaligned(void* dest, const void* src, size_t count);
void copy_and_swap_64_aligned(uint64_t* dest, const uint64_t* src, void copy_and_swap_16_in_32_aligned(void* dest, const void* src, size_t count);
size_t count);
void copy_and_swap_64_unaligned(uint64_t* dest, const uint64_t* src,
size_t count);
void copy_and_swap_16_in_32_aligned(uint32_t* dest, const uint32_t* src,
size_t count);
template <typename T> template <typename T>
void copy_and_swap(T* dest, const T* src, size_t count) { void copy_and_swap(T* dest, const T* src, size_t count) {

View File

@ -87,13 +87,12 @@ bool MMIOHandler::CheckStore(uint32_t virtual_address, uint32_t value) {
return false; return false;
} }
uintptr_t MMIOHandler::AddPhysicalWriteWatch(uint32_t guest_address, uintptr_t MMIOHandler::AddPhysicalAccessWatch(uint32_t guest_address,
size_t length, size_t length, WatchType type,
WriteWatchCallback callback, AccessWatchCallback callback,
void* callback_context, void* callback_context,
void* callback_data) { void* callback_data) {
uint32_t base_address = guest_address; uint32_t base_address = guest_address & 0x1FFFFFFF;
assert_true(base_address < 0x1FFFFFFF);
// Can only protect sizes matching system page size. // Can only protect sizes matching system page size.
// This means we need to round up, which will cause spurious access // This means we need to round up, which will cause spurious access
@ -103,32 +102,45 @@ uintptr_t MMIOHandler::AddPhysicalWriteWatch(uint32_t guest_address,
xe::memory::page_size()); xe::memory::page_size());
base_address = base_address - (base_address % xe::memory::page_size()); base_address = base_address - (base_address % xe::memory::page_size());
auto lock = global_critical_region_.Acquire();
// Add to table. The slot reservation may evict a previous watch, which // Add to table. The slot reservation may evict a previous watch, which
// could include our target, so we do it first. // could include our target, so we do it first.
auto entry = new WriteWatchEntry(); auto entry = new AccessWatchEntry();
entry->address = base_address; entry->address = base_address;
entry->length = uint32_t(length); entry->length = uint32_t(length);
entry->callback = callback; entry->callback = callback;
entry->callback_context = callback_context; entry->callback_context = callback_context;
entry->callback_data = callback_data; entry->callback_data = callback_data;
global_critical_region_.mutex().lock(); access_watches_.push_back(entry);
write_watches_.push_back(entry);
global_critical_region_.mutex().unlock();
// Make the desired range read only under all address spaces. auto page_access = memory::PageAccess::kNoAccess;
switch (type) {
case kWatchWrite:
page_access = memory::PageAccess::kReadOnly;
break;
case kWatchReadWrite:
page_access = memory::PageAccess::kNoAccess;
break;
default:
assert_unhandled_case(type);
break;
}
// Protect the range under all address spaces
memory::Protect(physical_membase_ + entry->address, entry->length, memory::Protect(physical_membase_ + entry->address, entry->length,
xe::memory::PageAccess::kReadOnly, nullptr); page_access, nullptr);
memory::Protect(virtual_membase_ + 0xA0000000 + entry->address, entry->length, memory::Protect(virtual_membase_ + 0xA0000000 + entry->address, entry->length,
xe::memory::PageAccess::kReadOnly, nullptr); page_access, nullptr);
memory::Protect(virtual_membase_ + 0xC0000000 + entry->address, entry->length, memory::Protect(virtual_membase_ + 0xC0000000 + entry->address, entry->length,
xe::memory::PageAccess::kReadOnly, nullptr); page_access, nullptr);
memory::Protect(virtual_membase_ + 0xE0000000 + entry->address, entry->length, memory::Protect(virtual_membase_ + 0xE0000000 + entry->address, entry->length,
xe::memory::PageAccess::kReadOnly, nullptr); page_access, nullptr);
return reinterpret_cast<uintptr_t>(entry); return reinterpret_cast<uintptr_t>(entry);
} }
void MMIOHandler::ClearWriteWatch(WriteWatchEntry* entry) { void MMIOHandler::ClearAccessWatch(AccessWatchEntry* entry) {
memory::Protect(physical_membase_ + entry->address, entry->length, memory::Protect(physical_membase_ + entry->address, entry->length,
xe::memory::PageAccess::kReadWrite, nullptr); xe::memory::PageAccess::kReadWrite, nullptr);
memory::Protect(virtual_membase_ + 0xA0000000 + entry->address, entry->length, memory::Protect(virtual_membase_ + 0xA0000000 + entry->address, entry->length,
@ -139,19 +151,20 @@ void MMIOHandler::ClearWriteWatch(WriteWatchEntry* entry) {
xe::memory::PageAccess::kReadWrite, nullptr); xe::memory::PageAccess::kReadWrite, nullptr);
} }
void MMIOHandler::CancelWriteWatch(uintptr_t watch_handle) { void MMIOHandler::CancelAccessWatch(uintptr_t watch_handle) {
auto entry = reinterpret_cast<WriteWatchEntry*>(watch_handle); auto entry = reinterpret_cast<AccessWatchEntry*>(watch_handle);
auto lock = global_critical_region_.Acquire();
// Allow access to the range again. // Allow access to the range again.
ClearWriteWatch(entry); ClearAccessWatch(entry);
// Remove from table. // Remove from table.
global_critical_region_.mutex().lock(); auto it = std::find(access_watches_.begin(), access_watches_.end(), entry);
auto it = std::find(write_watches_.begin(), write_watches_.end(), entry); assert_false(it == access_watches_.end());
if (it != write_watches_.end()) {
write_watches_.erase(it); if (it != access_watches_.end()) {
access_watches_.erase(it);
} }
global_critical_region_.mutex().unlock();
delete entry; delete entry;
} }
@ -159,18 +172,19 @@ void MMIOHandler::CancelWriteWatch(uintptr_t watch_handle) {
void MMIOHandler::InvalidateRange(uint32_t physical_address, size_t length) { void MMIOHandler::InvalidateRange(uint32_t physical_address, size_t length) {
auto lock = global_critical_region_.Acquire(); auto lock = global_critical_region_.Acquire();
for (auto it = write_watches_.begin(); it != write_watches_.end();) { for (auto it = access_watches_.begin(); it != access_watches_.end();) {
auto entry = *it; auto entry = *it;
if ((entry->address <= physical_address && if ((entry->address <= physical_address &&
entry->address + entry->length > physical_address) || entry->address + entry->length > physical_address) ||
(entry->address >= physical_address && (entry->address >= physical_address &&
entry->address < physical_address + length)) { entry->address < physical_address + length)) {
// This watch lies within the range. End it. // This watch lies within the range. End it.
ClearWriteWatch(entry); ClearAccessWatch(entry);
entry->callback(entry->callback_context, entry->callback_data, entry->callback(entry->callback_context, entry->callback_data,
entry->address); entry->address);
it = write_watches_.erase(it); it = access_watches_.erase(it);
delete entry;
continue; continue;
} }
@ -178,50 +192,49 @@ void MMIOHandler::InvalidateRange(uint32_t physical_address, size_t length) {
} }
} }
bool MMIOHandler::CheckWriteWatch(uint64_t fault_address) { bool MMIOHandler::IsRangeWatched(uint32_t physical_address, size_t length) {
uint32_t physical_address = uint32_t(fault_address); auto lock = global_critical_region_.Acquire();
if (physical_address > 0x1FFFFFFF) {
physical_address &= 0x1FFFFFFF; for (auto it = access_watches_.begin(); it != access_watches_.end(); ++it) {
} auto entry = *it;
std::list<WriteWatchEntry*> pending_invalidates; if ((entry->address <= physical_address &&
global_critical_region_.mutex().lock(); entry->address + entry->length > physical_address) ||
// Now that we hold the lock, recheck and see if the pages are still (entry->address >= physical_address &&
// protected. entry->address < physical_address + length)) {
memory::PageAccess cur_access; // This watch lies within the range.
size_t page_length = memory::page_size();
memory::QueryProtect((void*)fault_address, page_length, cur_access);
if (cur_access != memory::PageAccess::kReadOnly &&
cur_access != memory::PageAccess::kNoAccess) {
// Another thread has cleared this write watch. Abort.
global_critical_region_.mutex().unlock();
return true; return true;
} }
}
for (auto it = write_watches_.begin(); it != write_watches_.end();) { return false;
}
bool MMIOHandler::CheckAccessWatch(uint32_t physical_address) {
auto lock = global_critical_region_.Acquire();
bool hit = false;
for (auto it = access_watches_.begin(); it != access_watches_.end();) {
auto entry = *it; auto entry = *it;
if (entry->address <= physical_address && if (entry->address <= physical_address &&
entry->address + entry->length > physical_address) { entry->address + entry->length > physical_address) {
// Hit! Remove the writewatch. // Hit! Remove the watch.
pending_invalidates.push_back(entry); hit = true;
ClearAccessWatch(entry);
entry->callback(entry->callback_context, entry->callback_data,
physical_address);
ClearWriteWatch(entry); it = access_watches_.erase(it);
it = write_watches_.erase(it); delete entry;
continue; continue;
} }
++it; ++it;
} }
global_critical_region_.mutex().unlock();
if (pending_invalidates.empty()) { if (!hit) {
// Rethrow access violation - range was not being watched. // Rethrow access violation - range was not being watched.
return false; return false;
} }
while (!pending_invalidates.empty()) {
auto entry = pending_invalidates.back();
pending_invalidates.pop_back();
entry->callback(entry->callback_context, entry->callback_data,
physical_address);
delete entry;
}
// Range was watched, so lets eat this access violation. // Range was watched, so lets eat this access violation.
return true; return true;
} }
@ -414,9 +427,33 @@ bool MMIOHandler::ExceptionCallback(Exception* ex) {
} }
} }
if (!range) { if (!range) {
auto fault_address = reinterpret_cast<uint8_t*>(ex->fault_address());
uint32_t guest_address = 0;
if (fault_address >= virtual_membase_ &&
fault_address < physical_membase_) {
// Faulting on a virtual address.
guest_address = static_cast<uint32_t>(ex->fault_address()) & 0x1FFFFFFF;
} else {
// Faulting on a physical address.
guest_address = static_cast<uint32_t>(ex->fault_address());
}
// HACK: Recheck if the pages are still protected (race condition - another
// thread clears the writewatch we just hit)
// Do this under the lock so we don't introduce another race condition.
auto lock = global_critical_region_.Acquire();
memory::PageAccess cur_access;
size_t page_length = memory::page_size();
memory::QueryProtect((void*)fault_address, page_length, cur_access);
if (cur_access != memory::PageAccess::kReadOnly &&
cur_access != memory::PageAccess::kNoAccess) {
// Another thread has cleared this write watch. Abort.
return true;
}
// Access is not found within any range, so fail and let the caller handle // Access is not found within any range, so fail and let the caller handle
// it (likely by aborting). // it (likely by aborting).
return CheckWriteWatch(ex->fault_address()); return CheckAccessWatch(guest_address);
} }
auto rip = ex->pc(); auto rip = ex->pc();

View File

@ -28,8 +28,7 @@ typedef uint32_t (*MMIOReadCallback)(void* ppc_context, void* callback_context,
uint32_t addr); uint32_t addr);
typedef void (*MMIOWriteCallback)(void* ppc_context, void* callback_context, typedef void (*MMIOWriteCallback)(void* ppc_context, void* callback_context,
uint32_t addr, uint32_t value); uint32_t addr, uint32_t value);
typedef void (*AccessWatchCallback)(void* context_ptr, void* data_ptr,
typedef void (*WriteWatchCallback)(void* context_ptr, void* data_ptr,
uint32_t address); uint32_t address);
struct MMIORange { struct MMIORange {
@ -46,6 +45,12 @@ class MMIOHandler {
public: public:
virtual ~MMIOHandler(); virtual ~MMIOHandler();
enum WatchType {
kWatchInvalid = 0,
kWatchWrite = 1,
kWatchReadWrite = 2,
};
static std::unique_ptr<MMIOHandler> Install(uint8_t* virtual_membase, static std::unique_ptr<MMIOHandler> Install(uint8_t* virtual_membase,
uint8_t* physical_membase, uint8_t* physical_membase,
uint8_t* membase_end); uint8_t* membase_end);
@ -59,17 +64,24 @@ class MMIOHandler {
bool CheckLoad(uint32_t virtual_address, uint32_t* out_value); bool CheckLoad(uint32_t virtual_address, uint32_t* out_value);
bool CheckStore(uint32_t virtual_address, uint32_t value); bool CheckStore(uint32_t virtual_address, uint32_t value);
uintptr_t AddPhysicalWriteWatch(uint32_t guest_address, size_t length, // Memory watches: These are one-shot alarms that fire a callback (in the
WriteWatchCallback callback, // context of the thread that caused the callback) when a memory range is
// either written to or read from, depending on the watch type. These fire as
// soon as a read/write happens, and only fire once.
// These watches may be spuriously fired if memory is accessed nearby.
uintptr_t AddPhysicalAccessWatch(uint32_t guest_address, size_t length,
WatchType type, AccessWatchCallback callback,
void* callback_context, void* callback_data); void* callback_context, void* callback_data);
void CancelWriteWatch(uintptr_t watch_handle); void CancelAccessWatch(uintptr_t watch_handle);
void InvalidateRange(uint32_t physical_address, size_t length); void InvalidateRange(uint32_t physical_address, size_t length);
bool IsRangeWatched(uint32_t physical_address, size_t length);
protected: protected:
struct WriteWatchEntry { struct AccessWatchEntry {
uint32_t address; uint32_t address;
uint32_t length; uint32_t length;
WriteWatchCallback callback; WatchType type;
AccessWatchCallback callback;
void* callback_context; void* callback_context;
void* callback_data; void* callback_data;
}; };
@ -83,8 +95,8 @@ class MMIOHandler {
static bool ExceptionCallbackThunk(Exception* ex, void* data); static bool ExceptionCallbackThunk(Exception* ex, void* data);
bool ExceptionCallback(Exception* ex); bool ExceptionCallback(Exception* ex);
void ClearWriteWatch(WriteWatchEntry* entry); void ClearAccessWatch(AccessWatchEntry* entry);
bool CheckWriteWatch(uint64_t fault_address); bool CheckAccessWatch(uint32_t guest_address);
uint8_t* virtual_membase_; uint8_t* virtual_membase_;
uint8_t* physical_membase_; uint8_t* physical_membase_;
@ -94,7 +106,7 @@ class MMIOHandler {
xe::global_critical_region global_critical_region_; xe::global_critical_region global_critical_region_;
// TODO(benvanik): data structure magic. // TODO(benvanik): data structure magic.
std::list<WriteWatchEntry*> write_watches_; std::list<AccessWatchEntry*> access_watches_;
static MMIOHandler* global_handler_; static MMIOHandler* global_handler_;
}; };

View File

@ -140,10 +140,15 @@ bool Processor::Setup() {
frontend_ = std::move(frontend); frontend_ = std::move(frontend);
// Stack walker is used when profiling, debugging, and dumping. // Stack walker is used when profiling, debugging, and dumping.
// Note that creation may fail, in which case we'll have to disable those
// features.
stack_walker_ = StackWalker::Create(backend_->code_cache()); stack_walker_ = StackWalker::Create(backend_->code_cache());
if (!stack_walker_) { if (!stack_walker_) {
XELOGE("Unable to create stack walker"); // TODO(benvanik): disable features.
return false; if (FLAGS_debug) {
XELOGW("Disabling --debug due to lack of stack walker");
FLAGS_debug = false;
}
} }
// Open the trace data path, if requested. // Open the trace data path, if requested.

View File

@ -58,6 +58,7 @@ struct StackFrame {
class StackWalker { class StackWalker {
public: public:
// Creates a stack walker. Only one should exist within a process. // Creates a stack walker. Only one should exist within a process.
// May fail if another process has mucked with ours (like RenderDoc).
static std::unique_ptr<StackWalker> Create(backend::CodeCache* code_cache); static std::unique_ptr<StackWalker> Create(backend::CodeCache* code_cache);
// Dumps all thread stacks to the log. // Dumps all thread stacks to the log.

View File

@ -106,7 +106,7 @@ bool InitializeStackWalker() {
options |= SYMOPT_FAIL_CRITICAL_ERRORS; options |= SYMOPT_FAIL_CRITICAL_ERRORS;
sym_set_options_(options); sym_set_options_(options);
if (!sym_initialize_(GetCurrentProcess(), nullptr, TRUE)) { if (!sym_initialize_(GetCurrentProcess(), nullptr, TRUE)) {
XELOGE("Unable to initialize symbol services"); XELOGE("Unable to initialize symbol services - already in use?");
return false; return false;
} }
@ -311,7 +311,7 @@ std::unique_ptr<StackWalker> StackWalker::Create(
backend::CodeCache* code_cache) { backend::CodeCache* code_cache) {
auto stack_walker = std::make_unique<Win32StackWalker>(code_cache); auto stack_walker = std::make_unique<Win32StackWalker>(code_cache);
if (!stack_walker->Initialize()) { if (!stack_walker->Initialize()) {
XELOGE("Unable to initialize stack walker"); XELOGE("Unable to initialize stack walker: debug/save states disabled");
return nullptr; return nullptr;
} }
return std::unique_ptr<StackWalker>(stack_walker.release()); return std::unique_ptr<StackWalker>(stack_walker.release());

View File

@ -1019,8 +1019,7 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_EXT(RingBuffer* reader,
1, // max z 1, // max z
}; };
assert_true(endianness == Endian::k8in16); assert_true(endianness == Endian::k8in16);
xe::copy_and_swap_16_aligned( xe::copy_and_swap_16_aligned(memory_->TranslatePhysical(address), extents,
reinterpret_cast<uint16_t*>(memory_->TranslatePhysical(address)), extents,
xe::countof(extents)); xe::countof(extents));
trace_writer_.WriteMemoryWrite(CpuToGpu(address), sizeof(extents)); trace_writer_.WriteMemoryWrite(CpuToGpu(address), sizeof(extents));
return true; return true;

View File

@ -84,9 +84,9 @@ class CommandProcessor {
swap_request_handler_ = fn; swap_request_handler_ = fn;
} }
void RequestFrameTrace(const std::wstring& root_path); virtual void RequestFrameTrace(const std::wstring& root_path);
void BeginTracing(const std::wstring& root_path); virtual void BeginTracing(const std::wstring& root_path);
void EndTracing(); virtual void EndTracing();
void InitializeRingBuffer(uint32_t ptr, uint32_t page_count); void InitializeRingBuffer(uint32_t ptr, uint32_t page_count);
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size); void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size);

View File

@ -1410,7 +1410,7 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateVertexBuffers() {
// as we copy and only if it differs from the previous value committing // as we copy and only if it differs from the previous value committing
// it (and if it matches just discard and reuse). // it (and if it matches just discard and reuse).
xe::copy_and_swap_32_aligned( xe::copy_and_swap_32_aligned(
reinterpret_cast<uint32_t*>(allocation.host_ptr), allocation.host_ptr,
memory_->TranslatePhysical<const uint32_t*>(fetch->address << 2), memory_->TranslatePhysical<const uint32_t*>(fetch->address << 2),
valid_range / 4); valid_range / 4);

View File

@ -7,8 +7,8 @@
****************************************************************************** ******************************************************************************
*/ */
#ifndef XENIA_GPU_GL4_COMMAND_PROCESSOR_H_ #ifndef XENIA_GPU_GL4_GL4_COMMAND_PROCESSOR_H_
#define XENIA_GPU_GL4_COMMAND_PROCESSOR_H_ #define XENIA_GPU_GL4_GL4_COMMAND_PROCESSOR_H_
#include <atomic> #include <atomic>
#include <cstring> #include <cstring>
@ -234,4 +234,4 @@ class GL4CommandProcessor : public CommandProcessor {
} // namespace gpu } // namespace gpu
} // namespace xe } // namespace xe
#endif // XENIA_GPU_GL4_COMMAND_PROCESSOR_H_ #endif // XENIA_GPU_GL4_GL4_COMMAND_PROCESSOR_H_

View File

@ -427,7 +427,7 @@ TextureCache::TextureEntry* TextureCache::LookupOrInsertTexture(
// Not found, create. // Not found, create.
auto entry = std::make_unique<TextureEntry>(); auto entry = std::make_unique<TextureEntry>();
entry->texture_info = texture_info; entry->texture_info = texture_info;
entry->write_watch_handle = 0; entry->access_watch_handle = 0;
entry->pending_invalidation = false; entry->pending_invalidation = false;
entry->handle = 0; entry->handle = 0;
@ -442,6 +442,7 @@ TextureCache::TextureEntry* TextureCache::LookupOrInsertTexture(
// Found! Acquire the handle and remove the readbuffer entry. // Found! Acquire the handle and remove the readbuffer entry.
read_buffer_textures_.erase(it); read_buffer_textures_.erase(it);
entry->handle = read_buffer_entry->handle; entry->handle = read_buffer_entry->handle;
entry->access_watch_handle = read_buffer_entry->access_watch_handle;
delete read_buffer_entry; delete read_buffer_entry;
// TODO(benvanik): set more texture properties? swizzle/etc? // TODO(benvanik): set more texture properties? swizzle/etc?
auto entry_ptr = entry.get(); auto entry_ptr = entry.get();
@ -495,14 +496,15 @@ TextureCache::TextureEntry* TextureCache::LookupOrInsertTexture(
// Add a write watch. If any data in the given range is touched we'll get a // Add a write watch. If any data in the given range is touched we'll get a
// callback and evict the texture. We could reuse the storage, though the // callback and evict the texture. We could reuse the storage, though the
// driver is likely in a better position to pool that kind of stuff. // driver is likely in a better position to pool that kind of stuff.
entry->write_watch_handle = memory_->AddPhysicalWriteWatch( entry->access_watch_handle = memory_->AddPhysicalAccessWatch(
texture_info.guest_address, texture_info.input_length, texture_info.guest_address, texture_info.input_length,
cpu::MMIOHandler::kWatchWrite,
[](void* context_ptr, void* data_ptr, uint32_t address) { [](void* context_ptr, void* data_ptr, uint32_t address) {
auto self = reinterpret_cast<TextureCache*>(context_ptr); auto self = reinterpret_cast<TextureCache*>(context_ptr);
auto touched_entry = reinterpret_cast<TextureEntry*>(data_ptr); auto touched_entry = reinterpret_cast<TextureEntry*>(data_ptr);
// Clear watch handle first so we don't redundantly // Clear watch handle first so we don't redundantly
// remove. // remove.
touched_entry->write_watch_handle = 0; touched_entry->access_watch_handle = 0;
touched_entry->pending_invalidation = true; touched_entry->pending_invalidation = true;
// Add to pending list so Scavenge will clean it up. // Add to pending list so Scavenge will clean it up.
self->invalidated_textures_mutex_.lock(); self->invalidated_textures_mutex_.lock();
@ -574,14 +576,27 @@ GLuint TextureCache::ConvertTexture(Blitter* blitter, uint32_t guest_address,
dest_rect, GL_LINEAR, swap_channels); dest_rect, GL_LINEAR, swap_channels);
} }
// HACK: remove texture from write watch list so readback won't kill us. // Setup a read/write access watch. If the game tries to touch the memory
// Not needed now, as readback is disabled. // we were supposed to populate with this texture, then we'll actually
/* // populate it.
if (texture_entry->write_watch_handle) { if (texture_entry->access_watch_handle) {
memory_->CancelWriteWatch(texture_entry->write_watch_handle); memory_->CancelAccessWatch(texture_entry->access_watch_handle);
texture_entry->write_watch_handle = 0; texture_entry->access_watch_handle = 0;
} }
//*/
texture_entry->access_watch_handle = memory_->AddPhysicalAccessWatch(
guest_address, texture_entry->texture_info.input_length,
cpu::MMIOHandler::kWatchReadWrite,
[](void* context, void* data, uint32_t address) {
auto touched_entry = reinterpret_cast<TextureEntry*>(data);
touched_entry->access_watch_handle = 0;
// This happens. RDR resolves to a texture then upsizes it, BF1943
// writes to a resolved texture.
// TODO (for Vulkan): Copy this texture back into system memory.
// assert_always();
},
nullptr, texture_entry);
return texture_entry->handle; return texture_entry->handle;
} }
@ -618,6 +633,20 @@ GLuint TextureCache::ConvertTexture(Blitter* blitter, uint32_t guest_address,
entry->block_height = block_height; entry->block_height = block_height;
entry->format = format; entry->format = format;
entry->access_watch_handle = memory_->AddPhysicalAccessWatch(
guest_address, block_height * block_width * 4,
cpu::MMIOHandler::kWatchReadWrite,
[](void* context, void* data, uint32_t address) {
auto entry = reinterpret_cast<ReadBufferTexture*>(data);
entry->access_watch_handle = 0;
// This happens. RDR resolves to a texture then upsizes it, BF1943
// writes to a resolved texture.
// TODO (for Vulkan): Copy this texture back into system memory.
// assert_always();
},
nullptr, entry.get());
glCreateTextures(GL_TEXTURE_2D, 1, &entry->handle); glCreateTextures(GL_TEXTURE_2D, 1, &entry->handle);
glTextureParameteri(entry->handle, GL_TEXTURE_BASE_LEVEL, 0); glTextureParameteri(entry->handle, GL_TEXTURE_BASE_LEVEL, 0);
glTextureParameteri(entry->handle, GL_TEXTURE_MAX_LEVEL, 1); glTextureParameteri(entry->handle, GL_TEXTURE_MAX_LEVEL, 1);
@ -636,9 +665,9 @@ GLuint TextureCache::ConvertTexture(Blitter* blitter, uint32_t guest_address,
} }
void TextureCache::EvictTexture(TextureEntry* entry) { void TextureCache::EvictTexture(TextureEntry* entry) {
if (entry->write_watch_handle) { if (entry->access_watch_handle) {
memory_->CancelWriteWatch(entry->write_watch_handle); memory_->CancelAccessWatch(entry->access_watch_handle);
entry->write_watch_handle = 0; entry->access_watch_handle = 0;
} }
for (auto& view : entry->views) { for (auto& view : entry->views) {
@ -662,19 +691,13 @@ void TextureSwap(Endian endianness, void* dest, const void* src,
size_t length) { size_t length) {
switch (endianness) { switch (endianness) {
case Endian::k8in16: case Endian::k8in16:
xe::copy_and_swap_16_aligned(reinterpret_cast<uint16_t*>(dest), xe::copy_and_swap_16_aligned(dest, src, length / 2);
reinterpret_cast<const uint16_t*>(src),
length / 2);
break; break;
case Endian::k8in32: case Endian::k8in32:
xe::copy_and_swap_32_aligned(reinterpret_cast<uint32_t*>(dest), xe::copy_and_swap_32_aligned(dest, src, length / 4);
reinterpret_cast<const uint32_t*>(src),
length / 4);
break; break;
case Endian::k16in32: // Swap high and low 16 bits within a 32 bit word case Endian::k16in32: // Swap high and low 16 bits within a 32 bit word
xe::copy_and_swap_16_in_32_aligned(reinterpret_cast<uint32_t*>(dest), xe::copy_and_swap_16_in_32_aligned(dest, src, length);
reinterpret_cast<const uint32_t*>(src),
length);
break; break;
default: default:
case Endian::kUnspecified: case Endian::kUnspecified:

View File

@ -44,7 +44,7 @@ class TextureCache {
}; };
struct TextureEntry { struct TextureEntry {
TextureInfo texture_info; TextureInfo texture_info;
uintptr_t write_watch_handle; uintptr_t access_watch_handle;
GLuint handle; GLuint handle;
bool pending_invalidation; bool pending_invalidation;
std::vector<std::unique_ptr<TextureEntryView>> views; std::vector<std::unique_ptr<TextureEntryView>> views;
@ -74,8 +74,12 @@ class TextureCache {
TextureFormat format, bool swap_channels, TextureFormat format, bool swap_channels,
GLuint src_texture, Rect2D src_rect, Rect2D dest_rect); GLuint src_texture, Rect2D src_rect, Rect2D dest_rect);
TextureEntry* LookupAddress(uint32_t guest_address, uint32_t width,
uint32_t height, TextureFormat format);
private: private:
struct ReadBufferTexture { struct ReadBufferTexture {
uintptr_t access_watch_handle;
uint32_t guest_address; uint32_t guest_address;
uint32_t logical_width; uint32_t logical_width;
uint32_t logical_height; uint32_t logical_height;
@ -90,8 +94,6 @@ class TextureCache {
void EvictSampler(SamplerEntry* entry); void EvictSampler(SamplerEntry* entry);
TextureEntry* LookupOrInsertTexture(const TextureInfo& texture_info, TextureEntry* LookupOrInsertTexture(const TextureInfo& texture_info,
uint64_t opt_hash = 0); uint64_t opt_hash = 0);
TextureEntry* LookupAddress(uint32_t guest_address, uint32_t width,
uint32_t height, TextureFormat format);
void EvictTexture(TextureEntry* entry); void EvictTexture(TextureEntry* entry);
bool UploadTexture2D(GLuint texture, const TextureInfo& texture_info); bool UploadTexture2D(GLuint texture, const TextureInfo& texture_info);

View File

@ -7,6 +7,7 @@ project("xenia-gpu")
kind("StaticLib") kind("StaticLib")
language("C++") language("C++")
links({ links({
"glslang-spirv",
"snappy", "snappy",
"spirv-tools", "spirv-tools",
"xenia-base", "xenia-base",
@ -21,6 +22,8 @@ project("xenia-gpu")
project_root.."/third_party/gflags/src", project_root.."/third_party/gflags/src",
}) })
local_platform_files() local_platform_files()
local_platform_files("spirv")
local_platform_files("spirv/passes")
group("src") group("src")
project("xenia-gpu-shader-compiler") project("xenia-gpu-shader-compiler")
@ -29,6 +32,7 @@ project("xenia-gpu-shader-compiler")
language("C++") language("C++")
links({ links({
"gflags", "gflags",
"glslang-spirv",
"spirv-tools", "spirv-tools",
"xenia-base", "xenia-base",
"xenia-gpu", "xenia-gpu",

View File

@ -99,6 +99,17 @@ struct InstructionResult {
bool has_all_writes() const { bool has_all_writes() const {
return write_mask[0] && write_mask[1] && write_mask[2] && write_mask[3]; return write_mask[0] && write_mask[1] && write_mask[2] && write_mask[3];
} }
// Returns number of components written
uint32_t num_writes() const {
uint32_t total = 0;
for (int i = 0; i < 4; i++) {
if (write_mask[i]) {
total++;
}
}
return total;
}
// Returns true if any non-constant components are written. // Returns true if any non-constant components are written.
bool stores_non_constants() const { bool stores_non_constants() const {
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
@ -493,6 +504,24 @@ class Shader {
ParsedTextureFetchInstruction fetch_instr; ParsedTextureFetchInstruction fetch_instr;
}; };
struct ConstantRegisterMap {
// Bitmap of all kConstantFloat registers read by the shader.
// Any shader can only read up to 256 of the 512, and the base is dependent
// on the shader type. Each bit corresponds to a storage index from the type
// base, so bit 0 in a vertex shader is register 0, and bit 0 in a fragment
// shader is register 256.
uint64_t float_bitmap[256 / 64];
// Bitmap of all kConstantInt registers read by the shader.
// Each bit corresponds to a storage index [0-31].
uint32_t int_bitmap;
// Bitmap of all kConstantBool registers read by the shader.
// Each bit corresponds to a storage index [0-255].
uint32_t bool_bitmap[256 / 32];
// Computed byte count of all registers required when packed.
uint32_t packed_byte_length;
};
Shader(ShaderType shader_type, uint64_t ucode_data_hash, Shader(ShaderType shader_type, uint64_t ucode_data_hash,
const uint32_t* ucode_dwords, size_t ucode_dword_count); const uint32_t* ucode_dwords, size_t ucode_dword_count);
virtual ~Shader(); virtual ~Shader();
@ -518,12 +547,20 @@ class Shader {
return texture_bindings_; return texture_bindings_;
} }
// Bitmaps of all constant registers accessed by the shader.
const ConstantRegisterMap& constant_register_map() const {
return constant_register_map_;
}
// Returns true if the given color target index [0-3]. // Returns true if the given color target index [0-3].
bool writes_color_target(int i) const { return writes_color_targets_[i]; } bool writes_color_target(int i) const { return writes_color_targets_[i]; }
// True if the shader was translated and prepared without error. // True if the shader was translated and prepared without error.
bool is_valid() const { return is_valid_; } bool is_valid() const { return is_valid_; }
// True if the shader has already been translated.
bool is_translated() const { return is_translated_; }
// Errors that occurred during translation. // Errors that occurred during translation.
const std::vector<Error>& errors() const { return errors_; } const std::vector<Error>& errors() const { return errors_; }
@ -564,9 +601,11 @@ class Shader {
std::vector<VertexBinding> vertex_bindings_; std::vector<VertexBinding> vertex_bindings_;
std::vector<TextureBinding> texture_bindings_; std::vector<TextureBinding> texture_bindings_;
ConstantRegisterMap constant_register_map_ = {0};
bool writes_color_targets_[4] = {false, false, false, false}; bool writes_color_targets_[4] = {false, false, false, false};
bool is_valid_ = false; bool is_valid_ = false;
bool is_translated_ = false;
std::vector<Error> errors_; std::vector<Error> errors_;
std::string ucode_disassembly_; std::string ucode_disassembly_;

View File

@ -14,6 +14,7 @@
#include <set> #include <set>
#include <string> #include <string>
#include "xenia/base/logging.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
namespace xe { namespace xe {
@ -50,16 +51,18 @@ void ShaderTranslator::Reset() {
ucode_disasm_buffer_.Reset(); ucode_disasm_buffer_.Reset();
ucode_disasm_line_number_ = 0; ucode_disasm_line_number_ = 0;
previous_ucode_disasm_scan_offset_ = 0; previous_ucode_disasm_scan_offset_ = 0;
register_count_ = 64;
total_attrib_count_ = 0; total_attrib_count_ = 0;
vertex_bindings_.clear(); vertex_bindings_.clear();
texture_bindings_.clear(); texture_bindings_.clear();
std::memset(&constant_register_map_, 0, sizeof(constant_register_map_));
for (size_t i = 0; i < xe::countof(writes_color_targets_); ++i) { for (size_t i = 0; i < xe::countof(writes_color_targets_); ++i) {
writes_color_targets_[i] = false; writes_color_targets_[i] = false;
} }
} }
bool ShaderTranslator::GatherAllBindingInformation(Shader* shader) { bool ShaderTranslator::GatherAllBindingInformation(Shader* shader) {
// FIXME: This is kind of silly. // DEPRECATED: remove this codepath when GL4 goes away.
Reset(); Reset();
shader_type_ = shader->type(); shader_type_ = shader->type();
@ -93,9 +96,21 @@ bool ShaderTranslator::GatherAllBindingInformation(Shader* shader) {
return true; return true;
} }
bool ShaderTranslator::Translate(Shader* shader,
xenos::xe_gpu_program_cntl_t cntl) {
Reset();
register_count_ = shader->type() == ShaderType::kVertex ? cntl.vs_regs + 1
: cntl.ps_regs + 1;
return TranslateInternal(shader);
}
bool ShaderTranslator::Translate(Shader* shader) { bool ShaderTranslator::Translate(Shader* shader) {
Reset(); Reset();
return TranslateInternal(shader);
}
bool ShaderTranslator::TranslateInternal(Shader* shader) {
shader_type_ = shader->type(); shader_type_ = shader->type();
ucode_dwords_ = shader->ucode_dwords(); ucode_dwords_ = shader->ucode_dwords();
ucode_dword_count_ = shader->ucode_dword_count(); ucode_dword_count_ = shader->ucode_dword_count();
@ -124,16 +139,36 @@ bool ShaderTranslator::Translate(Shader* shader) {
TranslateBlocks(); TranslateBlocks();
// Compute total bytes used by the register map.
// This saves us work later when we need to pack them.
constant_register_map_.packed_byte_length = 0;
for (int i = 0; i < 4; ++i) {
// Each bit indicates a vec4 (4 floats).
constant_register_map_.packed_byte_length +=
4 * 4 * xe::bit_count(constant_register_map_.float_bitmap[i]);
}
// Each bit indicates a single word.
constant_register_map_.packed_byte_length +=
4 * xe::bit_count(constant_register_map_.int_bitmap);
// Direct map between words and words we upload.
for (int i = 0; i < 8; ++i) {
if (constant_register_map_.bool_bitmap[i]) {
constant_register_map_.packed_byte_length += 4;
}
}
shader->errors_ = std::move(errors_); shader->errors_ = std::move(errors_);
shader->translated_binary_ = CompleteTranslation(); shader->translated_binary_ = CompleteTranslation();
shader->ucode_disassembly_ = ucode_disasm_buffer_.to_string(); shader->ucode_disassembly_ = ucode_disasm_buffer_.to_string();
shader->vertex_bindings_ = std::move(vertex_bindings_); shader->vertex_bindings_ = std::move(vertex_bindings_);
shader->texture_bindings_ = std::move(texture_bindings_); shader->texture_bindings_ = std::move(texture_bindings_);
shader->constant_register_map_ = std::move(constant_register_map_);
for (size_t i = 0; i < xe::countof(writes_color_targets_); ++i) { for (size_t i = 0; i < xe::countof(writes_color_targets_); ++i) {
shader->writes_color_targets_[i] = writes_color_targets_[i]; shader->writes_color_targets_[i] = writes_color_targets_[i];
} }
shader->is_valid_ = true; shader->is_valid_ = true;
shader->is_translated_ = true;
for (const auto& error : shader->errors_) { for (const auto& error : shader->errors_) {
if (error.is_fatal) { if (error.is_fatal) {
shader->is_valid_ = false; shader->is_valid_ = false;
@ -141,6 +176,8 @@ bool ShaderTranslator::Translate(Shader* shader) {
} }
} }
PostTranslation(shader);
return shader->is_valid_; return shader->is_valid_;
} }
@ -331,7 +368,7 @@ bool ShaderTranslator::TranslateBlocks() {
// This is what freedreno does. // This is what freedreno does.
uint32_t max_cf_dword_index = static_cast<uint32_t>(ucode_dword_count_); uint32_t max_cf_dword_index = static_cast<uint32_t>(ucode_dword_count_);
std::set<uint32_t> label_addresses; std::set<uint32_t> label_addresses;
for (uint32_t i = 0; i < max_cf_dword_index; i += 3) { for (uint32_t i = 0, cf_index = 0; i < max_cf_dword_index; i += 3) {
ControlFlowInstruction cf_a; ControlFlowInstruction cf_a;
ControlFlowInstruction cf_b; ControlFlowInstruction cf_b;
UnpackControlFlowInstructions(ucode_dwords_ + i, &cf_a, &cf_b); UnpackControlFlowInstructions(ucode_dwords_ + i, &cf_a, &cf_b);
@ -345,6 +382,11 @@ bool ShaderTranslator::TranslateBlocks() {
} }
AddControlFlowTargetLabel(cf_a, &label_addresses); AddControlFlowTargetLabel(cf_a, &label_addresses);
AddControlFlowTargetLabel(cf_b, &label_addresses); AddControlFlowTargetLabel(cf_b, &label_addresses);
PreProcessControlFlowInstruction(cf_index, cf_a);
++cf_index;
PreProcessControlFlowInstruction(cf_index, cf_b);
++cf_index;
} }
// Translate all instructions. // Translate all instructions.
@ -488,6 +530,8 @@ void ShaderTranslator::TranslateControlFlowCondExec(
i.instruction_count = cf.count(); i.instruction_count = cf.count();
i.type = ParsedExecInstruction::Type::kConditional; i.type = ParsedExecInstruction::Type::kConditional;
i.bool_constant_index = cf.bool_address(); i.bool_constant_index = cf.bool_address();
constant_register_map_.bool_bitmap[i.bool_constant_index / 32] |=
1 << (i.bool_constant_index % 32);
i.condition = cf.condition(); i.condition = cf.condition();
switch (cf.opcode()) { switch (cf.opcode()) {
case ControlFlowOpcode::kCondExec: case ControlFlowOpcode::kCondExec:
@ -527,6 +571,7 @@ void ShaderTranslator::TranslateControlFlowLoopStart(
ParsedLoopStartInstruction i; ParsedLoopStartInstruction i;
i.dword_index = cf_index_; i.dword_index = cf_index_;
i.loop_constant_index = cf.loop_id(); i.loop_constant_index = cf.loop_id();
constant_register_map_.int_bitmap |= 1 << i.loop_constant_index;
i.is_repeat = cf.is_repeat(); i.is_repeat = cf.is_repeat();
i.loop_skip_address = cf.address(); i.loop_skip_address = cf.address();
@ -542,6 +587,7 @@ void ShaderTranslator::TranslateControlFlowLoopEnd(
i.is_predicated_break = cf.is_predicated_break(); i.is_predicated_break = cf.is_predicated_break();
i.predicate_condition = cf.condition(); i.predicate_condition = cf.condition();
i.loop_constant_index = cf.loop_id(); i.loop_constant_index = cf.loop_id();
constant_register_map_.int_bitmap |= 1 << i.loop_constant_index;
i.loop_body_address = cf.address(); i.loop_body_address = cf.address();
i.Disassemble(&ucode_disasm_buffer_); i.Disassemble(&ucode_disasm_buffer_);
@ -562,6 +608,8 @@ void ShaderTranslator::TranslateControlFlowCondCall(
} else { } else {
i.type = ParsedCallInstruction::Type::kConditional; i.type = ParsedCallInstruction::Type::kConditional;
i.bool_constant_index = cf.bool_address(); i.bool_constant_index = cf.bool_address();
constant_register_map_.bool_bitmap[i.bool_constant_index / 32] |=
1 << (i.bool_constant_index % 32);
i.condition = cf.condition(); i.condition = cf.condition();
} }
@ -593,6 +641,8 @@ void ShaderTranslator::TranslateControlFlowCondJmp(
} else { } else {
i.type = ParsedJumpInstruction::Type::kConditional; i.type = ParsedJumpInstruction::Type::kConditional;
i.bool_constant_index = cf.bool_address(); i.bool_constant_index = cf.bool_address();
constant_register_map_.bool_bitmap[i.bool_constant_index / 32] |=
1 << (i.bool_constant_index % 32);
i.condition = cf.condition(); i.condition = cf.condition();
} }
@ -950,16 +1000,19 @@ void ShaderTranslator::TranslateAluInstruction(const AluInstruction& op) {
return; return;
} }
ParsedAluInstruction instr;
if (op.has_vector_op()) { if (op.has_vector_op()) {
const auto& opcode_info = const auto& opcode_info =
alu_vector_opcode_infos_[static_cast<int>(op.vector_opcode())]; alu_vector_opcode_infos_[static_cast<int>(op.vector_opcode())];
ParseAluVectorInstruction(op, opcode_info); ParseAluVectorInstruction(op, opcode_info, instr);
ProcessAluInstruction(instr);
} }
if (op.has_scalar_op()) { if (op.has_scalar_op()) {
const auto& opcode_info = const auto& opcode_info =
alu_scalar_opcode_infos_[static_cast<int>(op.scalar_opcode())]; alu_scalar_opcode_infos_[static_cast<int>(op.scalar_opcode())];
ParseAluScalarInstruction(op, opcode_info); ParseAluScalarInstruction(op, opcode_info, instr);
ProcessAluInstruction(instr);
} }
} }
@ -1008,9 +1061,8 @@ void ParseAluInstructionOperand(const AluInstruction& op, int i,
uint32_t a = swizzle & 0x3; uint32_t a = swizzle & 0x3;
out_op->components[0] = GetSwizzleFromComponentIndex(a); out_op->components[0] = GetSwizzleFromComponentIndex(a);
} else if (swizzle_component_count == 2) { } else if (swizzle_component_count == 2) {
swizzle >>= 4; uint32_t a = ((swizzle >> 6) + 3) & 0x3;
uint32_t a = ((swizzle >> 2) + 3) & 0x3; uint32_t b = ((swizzle >> 0) + 0) & 0x3;
uint32_t b = (swizzle + 2) & 0x3;
out_op->components[0] = GetSwizzleFromComponentIndex(a); out_op->components[0] = GetSwizzleFromComponentIndex(a);
out_op->components[1] = GetSwizzleFromComponentIndex(b); out_op->components[1] = GetSwizzleFromComponentIndex(b);
} else { } else {
@ -1052,8 +1104,8 @@ void ParseAluInstructionOperandSpecial(const AluInstruction& op,
} }
void ShaderTranslator::ParseAluVectorInstruction( void ShaderTranslator::ParseAluVectorInstruction(
const AluInstruction& op, const AluOpcodeInfo& opcode_info) { const AluInstruction& op, const AluOpcodeInfo& opcode_info,
ParsedAluInstruction i; ParsedAluInstruction& i) {
i.dword_index = 0; i.dword_index = 0;
i.type = ParsedAluInstruction::Type::kVector; i.type = ParsedAluInstruction::Type::kVector;
i.vector_opcode = op.vector_opcode(); i.vector_opcode = op.vector_opcode();
@ -1084,9 +1136,19 @@ void ShaderTranslator::ParseAluVectorInstruction(
i.result.storage_target = InstructionStorageTarget::kPointSize; i.result.storage_target = InstructionStorageTarget::kPointSize;
break; break;
default: default:
assert_true(dest_num < 16); if (dest_num < 16) {
i.result.storage_target = InstructionStorageTarget::kInterpolant; i.result.storage_target = InstructionStorageTarget::kInterpolant;
i.result.storage_index = dest_num; i.result.storage_index = dest_num;
} else {
// Unimplemented.
// assert_always();
XELOGE(
"ShaderTranslator::ParseAluVectorInstruction: Unsupported write "
"to export %d",
dest_num);
i.result.storage_target = InstructionStorageTarget::kNone;
i.result.storage_index = 0;
}
break; break;
} }
} else if (is_pixel_shader()) { } else if (is_pixel_shader()) {
@ -1150,16 +1212,22 @@ void ShaderTranslator::ParseAluVectorInstruction(
for (int j = 0; j < i.operand_count; ++j) { for (int j = 0; j < i.operand_count; ++j) {
ParseAluInstructionOperand( ParseAluInstructionOperand(
op, j + 1, opcode_info.src_swizzle_component_count, &i.operands[j]); op, j + 1, opcode_info.src_swizzle_component_count, &i.operands[j]);
// Track constant float register loads.
if (i.operands[j].storage_source ==
InstructionStorageSource::kConstantFloat) {
auto register_index = i.operands[j].storage_index;
constant_register_map_.float_bitmap[register_index / 64] |=
1ull << (register_index % 64);
}
} }
i.Disassemble(&ucode_disasm_buffer_); i.Disassemble(&ucode_disasm_buffer_);
ProcessAluInstruction(i);
} }
void ShaderTranslator::ParseAluScalarInstruction( void ShaderTranslator::ParseAluScalarInstruction(
const AluInstruction& op, const AluOpcodeInfo& opcode_info) { const AluInstruction& op, const AluOpcodeInfo& opcode_info,
ParsedAluInstruction i; ParsedAluInstruction& i) {
i.dword_index = 0; i.dword_index = 0;
i.type = ParsedAluInstruction::Type::kScalar; i.type = ParsedAluInstruction::Type::kScalar;
i.scalar_opcode = op.scalar_opcode(); i.scalar_opcode = op.scalar_opcode();
@ -1198,9 +1266,19 @@ void ShaderTranslator::ParseAluScalarInstruction(
i.result.storage_target = InstructionStorageTarget::kPointSize; i.result.storage_target = InstructionStorageTarget::kPointSize;
break; break;
default: default:
assert_true(dest_num < 16); if (dest_num < 16) {
i.result.storage_target = InstructionStorageTarget::kInterpolant; i.result.storage_target = InstructionStorageTarget::kInterpolant;
i.result.storage_index = dest_num; i.result.storage_index = dest_num;
} else {
// Unimplemented.
// assert_always();
XELOGE(
"ShaderTranslator::ParseAluScalarInstruction: Unsupported write "
"to export %d",
dest_num);
i.result.storage_target = InstructionStorageTarget::kNone;
i.result.storage_index = 0;
}
break; break;
} }
} else if (is_pixel_shader()) { } else if (is_pixel_shader()) {
@ -1243,17 +1321,22 @@ void ShaderTranslator::ParseAluScalarInstruction(
uint32_t reg2 = (static_cast<int>(op.scalar_opcode()) & 1) | uint32_t reg2 = (static_cast<int>(op.scalar_opcode()) & 1) |
(src3_swizzle & 0x3C) | (op.src_is_temp(3) << 1); (src3_swizzle & 0x3C) | (op.src_is_temp(3) << 1);
int const_slot = (op.src_is_temp(1) || op.src_is_temp(2)) ? 1 : 0; int const_slot = (op.src_is_temp(1) || op.src_is_temp(2)) ? 1 : 0;
ParseAluInstructionOperandSpecial( ParseAluInstructionOperandSpecial(
op, InstructionStorageSource::kConstantFloat, op.src_reg(3), op, InstructionStorageSource::kConstantFloat, op.src_reg(3),
op.src_negate(3), 0, swiz_a, &i.operands[0]); op.src_negate(3), 0, swiz_a, &i.operands[0]);
// Track constant float register loads.
auto register_index = i.operands[0].storage_index;
constant_register_map_.float_bitmap[register_index / 64] |=
1ull << (register_index % 64);
ParseAluInstructionOperandSpecial(op, InstructionStorageSource::kRegister, ParseAluInstructionOperandSpecial(op, InstructionStorageSource::kRegister,
reg2, op.src_negate(3), const_slot, reg2, op.src_negate(3), const_slot,
swiz_b, &i.operands[1]); swiz_b, &i.operands[1]);
} }
i.Disassemble(&ucode_disasm_buffer_); i.Disassemble(&ucode_disasm_buffer_);
ProcessAluInstruction(i);
} }
} // namespace gpu } // namespace gpu

View File

@ -27,8 +27,10 @@ class ShaderTranslator {
virtual ~ShaderTranslator(); virtual ~ShaderTranslator();
// Gathers all vertex/texture bindings. Implicitly called in Translate. // Gathers all vertex/texture bindings. Implicitly called in Translate.
// TODO: Move this functionality to Shader. // DEPRECATED(benvanik): remove this when shader cache is removed.
bool GatherAllBindingInformation(Shader* shader); bool GatherAllBindingInformation(Shader* shader);
bool Translate(Shader* shader, xenos::xe_gpu_program_cntl_t cntl);
bool Translate(Shader* shader); bool Translate(Shader* shader);
protected: protected:
@ -37,6 +39,8 @@ class ShaderTranslator {
// Resets translator state before beginning translation. // Resets translator state before beginning translation.
virtual void Reset(); virtual void Reset();
// Register count.
uint32_t register_count() const { return register_count_; }
// True if the current shader is a vertex shader. // True if the current shader is a vertex shader.
bool is_vertex_shader() const { return shader_type_ == ShaderType::kVertex; } bool is_vertex_shader() const { return shader_type_ == ShaderType::kVertex; }
// True if the current shader is a pixel shader. // True if the current shader is a pixel shader.
@ -70,6 +74,17 @@ class ShaderTranslator {
return std::vector<uint8_t>(); return std::vector<uint8_t>();
} }
// Handles post-translation tasks when the shader has been fully translated.
virtual void PostTranslation(Shader* shader) {}
// Sets the host disassembly on a shader.
void set_host_disassembly(Shader* shader, std::string value) {
shader->host_disassembly_ = std::move(value);
}
// Pre-process a control-flow instruction before anything else.
virtual void PreProcessControlFlowInstruction(
uint32_t cf_index, const ucode::ControlFlowInstruction& instr) {}
// Handles translation for control flow label addresses. // Handles translation for control flow label addresses.
// This is triggered once for each label required (due to control flow // This is triggered once for each label required (due to control flow
// operations) before any of the instructions within the target exec. // operations) before any of the instructions within the target exec.
@ -120,6 +135,8 @@ class ShaderTranslator {
int src_swizzle_component_count; int src_swizzle_component_count;
}; };
bool TranslateInternal(Shader* shader);
void MarkUcodeInstruction(uint32_t dword_offset); void MarkUcodeInstruction(uint32_t dword_offset);
void AppendUcodeDisasm(char c); void AppendUcodeDisasm(char c);
void AppendUcodeDisasm(const char* value); void AppendUcodeDisasm(const char* value);
@ -162,14 +179,18 @@ class ShaderTranslator {
void TranslateAluInstruction(const ucode::AluInstruction& op); void TranslateAluInstruction(const ucode::AluInstruction& op);
void ParseAluVectorInstruction(const ucode::AluInstruction& op, void ParseAluVectorInstruction(const ucode::AluInstruction& op,
const AluOpcodeInfo& opcode_info); const AluOpcodeInfo& opcode_info,
ParsedAluInstruction& instr);
void ParseAluScalarInstruction(const ucode::AluInstruction& op, void ParseAluScalarInstruction(const ucode::AluInstruction& op,
const AluOpcodeInfo& opcode_info); const AluOpcodeInfo& opcode_info,
ParsedAluInstruction& instr);
// Input shader metadata and microcode. // Input shader metadata and microcode.
ShaderType shader_type_; ShaderType shader_type_;
const uint32_t* ucode_dwords_; const uint32_t* ucode_dwords_;
size_t ucode_dword_count_; size_t ucode_dword_count_;
xenos::xe_gpu_program_cntl_t program_cntl_;
uint32_t register_count_;
// Accumulated translation errors. // Accumulated translation errors.
std::vector<Shader::Error> errors_; std::vector<Shader::Error> errors_;
@ -191,6 +212,7 @@ class ShaderTranslator {
int total_attrib_count_ = 0; int total_attrib_count_ = 0;
std::vector<Shader::VertexBinding> vertex_bindings_; std::vector<Shader::VertexBinding> vertex_bindings_;
std::vector<Shader::TextureBinding> texture_bindings_; std::vector<Shader::TextureBinding> texture_bindings_;
Shader::ConstantRegisterMap constant_register_map_ = {0};
bool writes_color_targets_[4] = {false, false, false, false}; bool writes_color_targets_[4] = {false, false, false, false};
static const AluOpcodeInfo alu_vector_opcode_infos_[0x20]; static const AluOpcodeInfo alu_vector_opcode_infos_[0x20];

View File

@ -0,0 +1,36 @@
/**
******************************************************************************
* 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/gpu/spirv/compiler.h"
namespace xe {
namespace gpu {
namespace spirv {
Compiler::Compiler() {}
void Compiler::AddPass(std::unique_ptr<CompilerPass> pass) {
compiler_passes_.push_back(std::move(pass));
}
bool Compiler::Compile(spv::Module* module) {
for (auto& pass : compiler_passes_) {
if (!pass->Run(module)) {
return false;
}
}
return true;
}
void Compiler::Reset() { compiler_passes_.clear(); }
} // namespace spirv
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,41 @@
/**
******************************************************************************
* 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_GPU_SPIRV_COMPILER_H_
#define XENIA_GPU_SPIRV_COMPILER_H_
#include "xenia/base/arena.h"
#include "xenia/gpu/spirv/compiler_pass.h"
#include "third_party/glslang-spirv/SpvBuilder.h"
#include "third_party/spirv/GLSL.std.450.hpp11"
namespace xe {
namespace gpu {
namespace spirv {
// SPIR-V Compiler. Designed to optimize SPIR-V code before feeding it into the
// drivers.
class Compiler {
public:
Compiler();
void AddPass(std::unique_ptr<CompilerPass> pass);
void Reset();
bool Compile(spv::Module* module);
private:
std::vector<std::unique_ptr<CompilerPass>> compiler_passes_;
};
} // namespace spirv
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_SPIRV_COMPILER_H_

View File

@ -2,30 +2,36 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#ifndef XENIA_UI_SPIRV_SPIRV_OPTIMIZER_H_ #ifndef XENIA_GPU_SPIRV_COMPILER_PASS_H_
#define XENIA_UI_SPIRV_SPIRV_OPTIMIZER_H_ #define XENIA_GPU_SPIRV_COMPILER_PASS_H_
#include "xenia/ui/spirv/spirv_util.h" #include "xenia/base/arena.h"
#include "third_party/glslang-spirv/SpvBuilder.h"
#include "third_party/spirv/GLSL.std.450.hpp11"
namespace xe { namespace xe {
namespace ui { namespace gpu {
namespace spirv { namespace spirv {
class SpirvOptimizer { class CompilerPass {
public: public:
SpirvOptimizer(); CompilerPass() = default;
~SpirvOptimizer(); virtual ~CompilerPass() {}
virtual bool Run(spv::Module* module) = 0;
private: private:
xe::Arena ir_arena_;
}; };
} // namespace spirv } // namespace spirv
} // namespace ui } // namespace gpu
} // namespace xe } // namespace xe
#endif // XENIA_UI_SPIRV_SPIRV_OPTIMIZER_H_ #endif

View File

@ -0,0 +1,30 @@
/**
******************************************************************************
* 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/gpu/spirv/passes/control_flow_analysis_pass.h"
namespace xe {
namespace gpu {
namespace spirv {
ControlFlowAnalysisPass::ControlFlowAnalysisPass() {}
bool ControlFlowAnalysisPass::Run(spv::Module* module) {
for (auto function : module->getFunctions()) {
// For each OpBranchConditional, see if we can find a point where control
// flow converges and then append an OpSelectionMerge.
// Potential problems: while loops constructed from branch instructions
}
return true;
}
} // namespace spirv
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,34 @@
/**
******************************************************************************
* 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_GPU_SPIRV_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
#define XENIA_GPU_SPIRV_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_
#include "xenia/gpu/spirv/compiler_pass.h"
namespace xe {
namespace gpu {
namespace spirv {
// Control-flow analysis pass. Runs through control-flow and adds merge opcodes
// where necessary.
class ControlFlowAnalysisPass : public CompilerPass {
public:
ControlFlowAnalysisPass();
bool Run(spv::Module* module) override;
private:
};
} // namespace spirv
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_SPIRV_PASSES_CONTROL_FLOW_ANALYSIS_PASS_H_

View File

@ -0,0 +1,48 @@
/**
******************************************************************************
* 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/gpu/spirv/passes/control_flow_simplification_pass.h"
namespace xe {
namespace gpu {
namespace spirv {
ControlFlowSimplificationPass::ControlFlowSimplificationPass() {}
bool ControlFlowSimplificationPass::Run(spv::Module* module) {
for (auto function : module->getFunctions()) {
// Walk through the blocks in the function and merge any blocks which are
// unconditionally dominated.
for (auto it = function->getBlocks().end() - 1;
it != function->getBlocks().begin() - 1;) {
auto block = *it;
if (!block->isUnreachable() && block->getPredecessors().size() == 1) {
auto prev_block = block->getPredecessors()[0];
auto last_instr =
prev_block->getInstruction(prev_block->getInstructionCount() - 1);
if (last_instr->getOpCode() == spv::Op::OpBranch) {
if (prev_block->getSuccessors().size() == 1 &&
prev_block->getSuccessors()[0] == block) {
// We're dominated by this block. Merge into it.
prev_block->merge(block);
block->setUnreachable();
}
}
}
--it;
}
}
return true;
}
} // namespace spirv
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,34 @@
/**
******************************************************************************
* 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_GPU_SPIRV_PASSES_CONTROL_FLOW_SIMPLIFICATION_PASS_H_
#define XENIA_GPU_SPIRV_PASSES_CONTROL_FLOW_SIMPLIFICATION_PASS_H_
#include "xenia/gpu/spirv/compiler_pass.h"
namespace xe {
namespace gpu {
namespace spirv {
// Control-flow simplification pass. Combines adjacent blocks and marks
// any unreachable blocks.
class ControlFlowSimplificationPass : public CompilerPass {
public:
ControlFlowSimplificationPass();
bool Run(spv::Module* module) override;
private:
};
} // namespace spirv
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_SPIRV_PASSES_CONTROL_FLOW_SIMPLIFICATION_PASS_H_

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -14,12 +14,38 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "third_party/glslang-spirv/SpvBuilder.h"
#include "third_party/spirv/GLSL.std.450.hpp11"
#include "xenia/gpu/shader_translator.h" #include "xenia/gpu/shader_translator.h"
#include "xenia/ui/spirv/spirv_emitter.h" #include "xenia/gpu/spirv/compiler.h"
#include "xenia/ui/spirv/spirv_disassembler.h"
#include "xenia/ui/spirv/spirv_validator.h"
namespace xe { namespace xe {
namespace gpu { namespace gpu {
// Push constants embedded within the command buffer.
// The total size of this struct must be <= 128b (as that's the commonly
// supported size).
struct SpirvPushConstants {
// Accessible to vertex shader only:
float window_scale[4]; // sx,sy, ?, ?
float vtx_fmt[4];
// Accessible to fragment shader only:
float alpha_test[4]; // alpha test enable, func, ref, ?
uint32_t ps_param_gen;
};
static_assert(sizeof(SpirvPushConstants) <= 128,
"Push constants must fit <= 128b");
constexpr uint32_t kSpirvPushConstantVertexRangeOffset = 0;
constexpr uint32_t kSpirvPushConstantVertexRangeSize = (sizeof(float) * 4) * 2;
constexpr uint32_t kSpirvPushConstantFragmentRangeOffset =
kSpirvPushConstantVertexRangeSize;
constexpr uint32_t kSpirvPushConstantFragmentRangeSize =
(sizeof(float) * 4) + sizeof(uint32_t);
constexpr uint32_t kSpirvPushConstantsSize = sizeof(SpirvPushConstants);
class SpirvShaderTranslator : public ShaderTranslator { class SpirvShaderTranslator : public ShaderTranslator {
public: public:
SpirvShaderTranslator(); SpirvShaderTranslator();
@ -28,8 +54,13 @@ class SpirvShaderTranslator : public ShaderTranslator {
protected: protected:
void StartTranslation() override; void StartTranslation() override;
std::vector<uint8_t> CompleteTranslation() override; std::vector<uint8_t> CompleteTranslation() override;
void PostTranslation(Shader* shader) override;
void PreProcessControlFlowInstruction(
uint32_t cf_index, const ucode::ControlFlowInstruction& instr) override;
void ProcessLabel(uint32_t cf_index) override; void ProcessLabel(uint32_t cf_index) override;
void ProcessControlFlowInstructionBegin(uint32_t cf_index) override;
void ProcessControlFlowInstructionEnd(uint32_t cf_index) override;
void ProcessControlFlowNopInstruction() override; void ProcessControlFlowNopInstruction() override;
void ProcessExecInstructionBegin(const ParsedExecInstruction& instr) override; void ProcessExecInstructionBegin(const ParsedExecInstruction& instr) override;
void ProcessExecInstructionEnd(const ParsedExecInstruction& instr) override; void ProcessExecInstructionEnd(const ParsedExecInstruction& instr) override;
@ -51,6 +82,11 @@ class SpirvShaderTranslator : public ShaderTranslator {
void ProcessVectorAluInstruction(const ParsedAluInstruction& instr); void ProcessVectorAluInstruction(const ParsedAluInstruction& instr);
void ProcessScalarAluInstruction(const ParsedAluInstruction& instr); void ProcessScalarAluInstruction(const ParsedAluInstruction& instr);
// Creates a call to the given GLSL intrinsic.
spv::Id SpirvShaderTranslator::CreateGlslStd450InstructionCall(
spv::Decoration precision, spv::Id result_type,
spv::GLSLstd450 instruction_ordinal, std::vector<spv::Id> args);
// Loads an operand into a value. // Loads an operand into a value.
// The value returned will be in the form described in the operand (number of // The value returned will be in the form described in the operand (number of
// components, etc). // components, etc).
@ -60,7 +96,52 @@ class SpirvShaderTranslator : public ShaderTranslator {
// the proper components will be selected. // the proper components will be selected.
void StoreToResult(spv::Id source_value_id, const InstructionResult& result); void StoreToResult(spv::Id source_value_id, const InstructionResult& result);
xe::ui::spirv::SpirvEmitter emitter_; xe::ui::spirv::SpirvDisassembler disassembler_;
xe::ui::spirv::SpirvValidator validator_;
xe::gpu::spirv::Compiler compiler_;
// True if there's an open predicated block
bool open_predicated_block_ = false;
bool predicated_block_cond_ = false;
spv::Block* predicated_block_end_ = nullptr;
// TODO(benvanik): replace with something better, make reusable, etc.
std::unique_ptr<spv::Builder> builder_;
spv::Id glsl_std_450_instruction_set_ = 0;
// Generated function
spv::Function* translated_main_ = 0;
// Types.
spv::Id float_type_ = 0, bool_type_ = 0, int_type_ = 0, uint_type_ = 0;
spv::Id vec2_float_type_ = 0, vec3_float_type_ = 0, vec4_float_type_ = 0;
spv::Id vec4_uint_type_ = 0;
spv::Id vec4_bool_type_ = 0;
// Constants.
spv::Id vec4_float_zero_ = 0, vec4_float_one_ = 0;
// Array of AMD registers.
// These values are all pointers.
spv::Id registers_ptr_ = 0, registers_type_ = 0;
spv::Id consts_ = 0, a0_ = 0, aL_ = 0, p0_ = 0;
spv::Id ps_ = 0, pv_ = 0; // IDs of previous results
spv::Id pos_ = 0;
spv::Id push_consts_ = 0;
spv::Id interpolators_ = 0;
spv::Id vertex_id_ = 0;
spv::Id frag_outputs_ = 0, frag_depth_ = 0;
spv::Id samplers_ = 0;
spv::Id tex_[4] = {0}; // Images {1D, 2D, 3D, Cube}
// Map of {binding -> {offset -> spv input}}
std::map<uint32_t, std::map<uint32_t, spv::Id>> vertex_binding_map_;
struct CFBlock {
spv::Block* block = nullptr;
bool prev_dominates = true;
};
std::map<uint32_t, CFBlock> cf_blocks_;
}; };
} // namespace gpu } // namespace gpu

View File

@ -88,6 +88,66 @@ enum class TextureFormat : uint32_t {
kUnknown = 0xFFFFFFFFu, kUnknown = 0xFFFFFFFFu,
}; };
inline size_t GetTexelSize(TextureFormat format) {
switch (format) {
case TextureFormat::k_1_5_5_5:
return 2;
break;
case TextureFormat::k_2_10_10_10:
return 4;
break;
case TextureFormat::k_4_4_4_4:
return 2;
break;
case TextureFormat::k_5_6_5:
return 2;
break;
case TextureFormat::k_8:
return 1;
break;
case TextureFormat::k_8_8:
return 2;
break;
case TextureFormat::k_8_8_8_8:
return 4;
break;
case TextureFormat::k_16:
return 4;
break;
case TextureFormat::k_16_FLOAT:
return 4;
break;
case TextureFormat::k_16_16:
return 4;
break;
case TextureFormat::k_16_16_FLOAT:
return 4;
break;
case TextureFormat::k_16_16_16_16:
return 8;
break;
case TextureFormat::k_16_16_16_16_FLOAT:
return 8;
break;
case TextureFormat::k_32_FLOAT:
return 4;
break;
case TextureFormat::k_32_32_FLOAT:
return 8;
break;
case TextureFormat::k_32_32_32_32_FLOAT:
return 16;
break;
case TextureFormat::k_10_11_11:
case TextureFormat::k_11_11_10:
return 4;
break;
default:
assert_unhandled_case(format);
return 0;
}
}
inline TextureFormat ColorFormatToTextureFormat(ColorFormat color_format) { inline TextureFormat ColorFormatToTextureFormat(ColorFormat color_format) {
return static_cast<TextureFormat>(color_format); return static_cast<TextureFormat>(color_format);
} }

View File

@ -13,6 +13,7 @@
#include "third_party/stb/stb_image_write.h" #include "third_party/stb/stb_image_write.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/profiling.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
#include "xenia/gpu/command_processor.h" #include "xenia/gpu/command_processor.h"
@ -189,10 +190,16 @@ void TraceDump::Run() {
}); });
xe::threading::Fence capture_fence; xe::threading::Fence capture_fence;
bool did_capture = false;
loop_->PostDelayed( loop_->PostDelayed(
[&]() { [&]() {
// Capture. // Capture.
auto raw_image = window_->context()->Capture(); auto raw_image = window_->context()->Capture();
if (!raw_image) {
// Failed to capture anything.
capture_fence.Signal();
return;
}
// Save framebuffer png. // Save framebuffer png.
std::string png_path = xe::to_string(base_output_path_ + L".png"); std::string png_path = xe::to_string(base_output_path_ + L".png");
@ -201,6 +208,7 @@ void TraceDump::Run() {
raw_image->data.data(), raw_image->data.data(),
static_cast<int>(raw_image->stride)); static_cast<int>(raw_image->stride));
did_capture = true;
capture_fence.Signal(); capture_fence.Signal();
}, },
50); 50);
@ -211,10 +219,13 @@ void TraceDump::Run() {
loop_->Quit(); loop_->Quit();
loop_->AwaitQuit(); loop_->AwaitQuit();
player_.reset(); Profiler::Shutdown();
emulator_.reset();
window_.reset(); window_.reset();
loop_.reset(); loop_.reset();
player_.reset();
emulator_.reset();
// TODO(benvanik): die if failed to capture?
} }
} // namespace gpu } // namespace gpu

View File

@ -51,7 +51,7 @@ void TracePlayer::SeekFrame(int target_frame) {
assert_true(frame->start_ptr <= frame->end_ptr); assert_true(frame->start_ptr <= frame->end_ptr);
PlayTrace(frame->start_ptr, frame->end_ptr - frame->start_ptr, PlayTrace(frame->start_ptr, frame->end_ptr - frame->start_ptr,
TracePlaybackMode::kBreakOnSwap); TracePlaybackMode::kBreakOnSwap, false);
} }
void TracePlayer::SeekCommand(int target_command) { void TracePlayer::SeekCommand(int target_command) {
@ -71,11 +71,11 @@ void TracePlayer::SeekCommand(int target_command) {
const auto& previous_command = frame->commands[previous_command_index]; const auto& previous_command = frame->commands[previous_command_index];
PlayTrace(previous_command.end_ptr, PlayTrace(previous_command.end_ptr,
command.end_ptr - previous_command.end_ptr, command.end_ptr - previous_command.end_ptr,
TracePlaybackMode::kBreakOnSwap); TracePlaybackMode::kBreakOnSwap, false);
} else { } else {
// Full playback from frame start. // Full playback from frame start.
PlayTrace(frame->start_ptr, command.end_ptr - frame->start_ptr, PlayTrace(frame->start_ptr, command.end_ptr - frame->start_ptr,
TracePlaybackMode::kBreakOnSwap); TracePlaybackMode::kBreakOnSwap, true);
} }
} }
@ -84,19 +84,25 @@ void TracePlayer::WaitOnPlayback() {
} }
void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size, void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size,
TracePlaybackMode playback_mode) { TracePlaybackMode playback_mode,
graphics_system_->command_processor()->CallInThread( bool clear_caches) {
[this, trace_data, trace_size, playback_mode]() { playing_trace_ = true;
PlayTraceOnThread(trace_data, trace_size, playback_mode); graphics_system_->command_processor()->CallInThread([=]() {
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches);
}); });
} }
void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
size_t trace_size, size_t trace_size,
TracePlaybackMode playback_mode) { TracePlaybackMode playback_mode,
bool clear_caches) {
auto memory = graphics_system_->memory(); auto memory = graphics_system_->memory();
auto command_processor = graphics_system_->command_processor(); auto command_processor = graphics_system_->command_processor();
if (clear_caches) {
command_processor->ClearCaches();
}
command_processor->set_swap_mode(SwapMode::kIgnored); command_processor->set_swap_mode(SwapMode::kIgnored);
playback_percent_ = 0; playback_percent_ = 0;
auto trace_end = trace_data + trace_size; auto trace_end = trace_data + trace_size;

View File

@ -50,9 +50,9 @@ class TracePlayer : public TraceReader {
private: private:
void PlayTrace(const uint8_t* trace_data, size_t trace_size, void PlayTrace(const uint8_t* trace_data, size_t trace_size,
TracePlaybackMode playback_mode); TracePlaybackMode playback_mode, bool clear_caches);
void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size, void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size,
TracePlaybackMode playback_mode); TracePlaybackMode playback_mode, bool clear_caches);
xe::ui::Loop* loop_; xe::ui::Loop* loop_;
GraphicsSystem* graphics_system_; GraphicsSystem* graphics_system_;

View File

@ -75,6 +75,10 @@ void TraceReader::ParseTrace() {
const uint8_t* packet_start_ptr = nullptr; const uint8_t* packet_start_ptr = nullptr;
const uint8_t* last_ptr = trace_ptr; const uint8_t* last_ptr = trace_ptr;
bool pending_break = false; bool pending_break = false;
auto current_command_buffer = new CommandBuffer();
current_frame.command_tree =
std::unique_ptr<CommandBuffer>(current_command_buffer);
while (trace_ptr < trace_data_ + trace_size_) { while (trace_ptr < trace_data_ + trace_size_) {
++current_frame.command_count; ++current_frame.command_count;
auto type = static_cast<TraceCommandType>(xe::load<uint32_t>(trace_ptr)); auto type = static_cast<TraceCommandType>(xe::load<uint32_t>(trace_ptr));
@ -94,11 +98,29 @@ void TraceReader::ParseTrace() {
auto cmd = auto cmd =
reinterpret_cast<const IndirectBufferStartCommand*>(trace_ptr); reinterpret_cast<const IndirectBufferStartCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd) + cmd->count * 4; trace_ptr += sizeof(*cmd) + cmd->count * 4;
// Traverse down a level.
auto sub_command_buffer = new CommandBuffer();
sub_command_buffer->parent = current_command_buffer;
current_command_buffer->commands.push_back(
CommandBuffer::Command(sub_command_buffer));
current_command_buffer = sub_command_buffer;
break; break;
} }
case TraceCommandType::kIndirectBufferEnd: { case TraceCommandType::kIndirectBufferEnd: {
auto cmd = reinterpret_cast<const IndirectBufferEndCommand*>(trace_ptr); auto cmd = reinterpret_cast<const IndirectBufferEndCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd); trace_ptr += sizeof(*cmd);
// IB packet is wrapped in a kPacketStart/kPacketEnd. Skip the end.
auto end_cmd = reinterpret_cast<const PacketEndCommand*>(trace_ptr);
assert_true(end_cmd->type == TraceCommandType::kPacketEnd);
trace_ptr += sizeof(*cmd);
// Go back up a level. If parent is null, this frame started in an
// indirect buffer.
if (current_command_buffer->parent) {
current_command_buffer = current_command_buffer->parent;
}
break; break;
} }
case TraceCommandType::kPacketStart: { case TraceCommandType::kPacketStart: {
@ -125,6 +147,8 @@ void TraceReader::ParseTrace() {
command.end_ptr = trace_ptr; command.end_ptr = trace_ptr;
current_frame.commands.push_back(std::move(command)); current_frame.commands.push_back(std::move(command));
last_ptr = trace_ptr; last_ptr = trace_ptr;
current_command_buffer->commands.push_back(CommandBuffer::Command(
uint32_t(current_frame.commands.size() - 1)));
break; break;
} }
case PacketCategory::kSwap: case PacketCategory::kSwap:
@ -136,6 +160,9 @@ void TraceReader::ParseTrace() {
if (pending_break) { if (pending_break) {
current_frame.end_ptr = trace_ptr; current_frame.end_ptr = trace_ptr;
frames_.push_back(std::move(current_frame)); frames_.push_back(std::move(current_frame));
current_command_buffer = new CommandBuffer();
current_frame.command_tree =
std::unique_ptr<CommandBuffer>(current_command_buffer);
current_frame.start_ptr = trace_ptr; current_frame.start_ptr = trace_ptr;
current_frame.end_ptr = nullptr; current_frame.end_ptr = nullptr;
current_frame.command_count = 0; current_frame.command_count = 0;

View File

@ -11,6 +11,7 @@
#define XENIA_GPU_TRACE_READER_H_ #define XENIA_GPU_TRACE_READER_H_
#include <string> #include <string>
#include <vector>
#include "xenia/base/mapped_memory.h" #include "xenia/base/mapped_memory.h"
#include "xenia/gpu/trace_protocol.h" #include "xenia/gpu/trace_protocol.h"
@ -51,6 +52,42 @@ namespace gpu {
class TraceReader { class TraceReader {
public: public:
struct CommandBuffer {
struct Command {
enum class Type {
kCommand,
kBuffer,
};
Command() {}
Command(Command&& other) {
type = other.type;
command_id = other.command_id;
command_subtree = std::move(other.command_subtree);
}
Command(CommandBuffer* buf) {
type = Type::kBuffer;
command_subtree = std::unique_ptr<CommandBuffer>(buf);
}
Command(uint32_t id) {
type = Type::kCommand;
command_id = id;
}
~Command() = default;
Type type;
uint32_t command_id = -1;
std::unique_ptr<CommandBuffer> command_subtree = nullptr;
};
CommandBuffer() {}
~CommandBuffer() {}
// Parent command buffer, if one exists.
CommandBuffer* parent = nullptr;
std::vector<Command> commands;
};
struct Frame { struct Frame {
struct Command { struct Command {
enum class Type { enum class Type {
@ -74,7 +111,12 @@ class TraceReader {
const uint8_t* start_ptr = nullptr; const uint8_t* start_ptr = nullptr;
const uint8_t* end_ptr = nullptr; const uint8_t* end_ptr = nullptr;
int command_count = 0; int command_count = 0;
// Flat list of all commands in this frame.
std::vector<Command> commands; std::vector<Command> commands;
// Tree of all command buffers
std::unique_ptr<CommandBuffer> command_tree;
}; };
TraceReader() = default; TraceReader() = default;

View File

@ -390,6 +390,66 @@ void TraceViewer::DrawPacketDisassemblerUI() {
ImGui::End(); ImGui::End();
} }
int TraceViewer::RecursiveDrawCommandBufferUI(
const TraceReader::Frame* frame, TraceReader::CommandBuffer* buffer) {
int selected_id = -1;
int column_width = int(ImGui::GetContentRegionMax().x);
for (size_t i = 0; i < buffer->commands.size(); i++) {
switch (buffer->commands[i].type) {
case TraceReader::CommandBuffer::Command::Type::kBuffer: {
auto subtree = buffer->commands[i].command_subtree.get();
if (!subtree->commands.size()) {
continue;
}
ImGui::PushID(int(i));
if (ImGui::TreeNode((void*)0, "Indirect Buffer %d", i)) {
ImGui::Indent();
auto id = RecursiveDrawCommandBufferUI(
frame, buffer->commands[i].command_subtree.get());
ImGui::Unindent();
ImGui::TreePop();
if (id != -1) {
selected_id = id;
}
}
ImGui::PopID();
} break;
case TraceReader::CommandBuffer::Command::Type::kCommand: {
uint32_t command_id = buffer->commands[i].command_id;
const auto& command = frame->commands[command_id];
bool is_selected = command_id == player_->current_command_index();
const char* label;
switch (command.type) {
case TraceReader::Frame::Command::Type::kDraw:
label = "Draw";
break;
case TraceReader::Frame::Command::Type::kSwap:
label = "Swap";
break;
}
ImGui::PushID(command_id);
if (ImGui::Selectable(label, &is_selected)) {
selected_id = command_id;
}
ImGui::SameLine(column_width - 60.0f);
ImGui::Text("%d", command_id);
ImGui::PopID();
// if (did_seek && target_command == i) {
// ImGui::SetScrollPosHere();
// }
} break;
}
}
return selected_id;
}
void TraceViewer::DrawCommandListUI() { void TraceViewer::DrawCommandListUI() {
ImGui::SetNextWindowPos(ImVec2(5, 70), ImGuiSetCond_FirstUseEver); ImGui::SetNextWindowPos(ImVec2(5, 70), ImGuiSetCond_FirstUseEver);
if (!ImGui::Begin("Command List", nullptr, ImVec2(200, 640))) { if (!ImGui::Begin("Command List", nullptr, ImVec2(200, 640))) {
@ -473,31 +533,12 @@ void TraceViewer::DrawCommandListUI() {
ImGui::SetScrollPosHere(); ImGui::SetScrollPosHere();
} }
for (int i = 0; i < int(frame->commands.size()); ++i) { auto id = RecursiveDrawCommandBufferUI(frame, frame->command_tree.get());
ImGui::PushID(i); if (id != -1 && id != player_->current_command_index() &&
is_selected = i == player_->current_command_index(); !player_->is_playing_trace()) {
const auto& command = frame->commands[i]; player_->SeekCommand(id);
const char* label;
switch (command.type) {
case TraceReader::Frame::Command::Type::kDraw:
label = "Draw";
break;
case TraceReader::Frame::Command::Type::kSwap:
label = "Swap";
break;
}
if (ImGui::Selectable(label, &is_selected)) {
if (!player_->is_playing_trace()) {
player_->SeekCommand(i);
}
}
ImGui::SameLine(column_width - 60.0f);
ImGui::Text("%d", i);
ImGui::PopID();
if (did_seek && target_command == i) {
ImGui::SetScrollPosHere();
}
} }
ImGui::EndChild(); ImGui::EndChild();
ImGui::End(); ImGui::End();
} }
@ -639,8 +680,8 @@ void TraceViewer::DrawTextureInfo(
ImGui::Columns(2); ImGui::Columns(2);
ImVec2 button_size(256, 256); ImVec2 button_size(256, 256);
if (ImGui::ImageButton(ImTextureID(texture | ui::ImGuiDrawer::kIgnoreAlpha), if (ImGui::ImageButton(ImTextureID(texture), button_size, ImVec2(0, 0),
button_size, ImVec2(0, 0), ImVec2(1, 1))) { ImVec2(1, 1))) {
// show viewer // show viewer
} }
ImGui::NextColumn(); ImGui::NextColumn();
@ -1108,11 +1149,14 @@ void TraceViewer::DrawStateUI() {
((window_scissor_br >> 16) & 0x7FFF) - ((window_scissor_br >> 16) & 0x7FFF) -
((window_scissor_tl >> 16) & 0x7FFF)); ((window_scissor_tl >> 16) & 0x7FFF));
uint32_t surface_info = regs[XE_GPU_REG_RB_SURFACE_INFO].u32; uint32_t surface_info = regs[XE_GPU_REG_RB_SURFACE_INFO].u32;
uint32_t surface_actual = (surface_info >> 18) & 0x3FFF;
uint32_t surface_pitch = surface_info & 0x3FFF; uint32_t surface_pitch = surface_info & 0x3FFF;
auto surface_msaa = (surface_info >> 16) & 0x3; auto surface_msaa = (surface_info >> 16) & 0x3;
static const char* kMsaaNames[] = { static const char* kMsaaNames[] = {
"1X", "2X", "4X", "1X", "2X", "4X",
}; };
ImGui::BulletText("Surface Pitch - Actual: %d - %d", surface_pitch,
surface_actual);
ImGui::BulletText("Surface MSAA: %s", kMsaaNames[surface_msaa]); ImGui::BulletText("Surface MSAA: %s", kMsaaNames[surface_msaa]);
uint32_t vte_control = regs[XE_GPU_REG_PA_CL_VTE_CNTL].u32; uint32_t vte_control = regs[XE_GPU_REG_PA_CL_VTE_CNTL].u32;
bool vport_xscale_enable = (vte_control & (1 << 0)) > 0; bool vport_xscale_enable = (vte_control & (1 << 0)) > 0;
@ -1124,6 +1168,9 @@ void TraceViewer::DrawStateUI() {
assert_true(vport_xscale_enable == vport_yscale_enable == assert_true(vport_xscale_enable == vport_yscale_enable ==
vport_zscale_enable == vport_xoffset_enable == vport_zscale_enable == vport_xoffset_enable ==
vport_yoffset_enable == vport_zoffset_enable); vport_yoffset_enable == vport_zoffset_enable);
if (!vport_xscale_enable) {
ImGui::PushStyleColor(ImGuiCol_Text, kColorIgnored);
}
ImGui::BulletText( ImGui::BulletText(
"Viewport Offset: %f, %f, %f", "Viewport Offset: %f, %f, %f",
vport_xoffset_enable ? regs[XE_GPU_REG_PA_CL_VPORT_XOFFSET].f32 : 0, vport_xoffset_enable ? regs[XE_GPU_REG_PA_CL_VPORT_XOFFSET].f32 : 0,
@ -1134,6 +1181,10 @@ void TraceViewer::DrawStateUI() {
vport_xscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_XSCALE].f32 : 1, vport_xscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_XSCALE].f32 : 1,
vport_yscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_YSCALE].f32 : 1, vport_yscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_YSCALE].f32 : 1,
vport_zscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_ZSCALE].f32 : 1); vport_zscale_enable ? regs[XE_GPU_REG_PA_CL_VPORT_ZSCALE].f32 : 1);
if (!vport_xscale_enable) {
ImGui::PopStyleColor();
}
ImGui::BulletText("Vertex Format: %s, %s, %s, %s", ImGui::BulletText("Vertex Format: %s, %s, %s, %s",
((vte_control >> 8) & 0x1) ? "x/w0" : "x", ((vte_control >> 8) & 0x1) ? "x/w0" : "x",
((vte_control >> 8) & 0x1) ? "y/w0" : "y", ((vte_control >> 8) & 0x1) ? "y/w0" : "y",
@ -1318,7 +1369,7 @@ void TraceViewer::DrawStateUI() {
if (write_mask) { if (write_mask) {
auto color_target = GetColorRenderTarget(surface_pitch, surface_msaa, auto color_target = GetColorRenderTarget(surface_pitch, surface_msaa,
color_base, color_format); color_base, color_format);
tex = ImTextureID(color_target | ui::ImGuiDrawer::kIgnoreAlpha); tex = ImTextureID(color_target);
if (ImGui::ImageButton(tex, button_size, ImVec2(0, 0), if (ImGui::ImageButton(tex, button_size, ImVec2(0, 0),
ImVec2(1, 1))) { ImVec2(1, 1))) {
// show viewer // show viewer
@ -1330,10 +1381,9 @@ void TraceViewer::DrawStateUI() {
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::Text( ImGui::Text("Color Target %d (%s), base %.4X, pitch %d, format %d", i,
"Color Target %d (%s), base %.4X, pitch %d, msaa %d, format %d", write_mask ? "enabled" : "disabled", color_base,
i, write_mask ? "enabled" : "disabled", color_base, surface_pitch, surface_pitch, color_format);
surface_msaa, color_format);
if (tex) { if (tex) {
ImVec2 rel_pos; ImVec2 rel_pos;
@ -1407,17 +1457,19 @@ void TraceViewer::DrawStateUI() {
auto button_pos = ImGui::GetCursorScreenPos(); auto button_pos = ImGui::GetCursorScreenPos();
ImVec2 button_size(256, 256); ImVec2 button_size(256, 256);
ImGui::ImageButton( ImGui::ImageButton(ImTextureID(depth_target), button_size, ImVec2(0, 0),
ImTextureID(depth_target | ui::ImGuiDrawer::kIgnoreAlpha), ImVec2(1, 1));
button_size, ImVec2(0, 0), ImVec2(1, 1));
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::Text("Depth Target: base %.4X, pitch %d, format %d", depth_base,
surface_pitch, depth_format);
ImVec2 rel_pos; ImVec2 rel_pos;
rel_pos.x = ImGui::GetMousePos().x - button_pos.x; rel_pos.x = ImGui::GetMousePos().x - button_pos.x;
rel_pos.y = ImGui::GetMousePos().y - button_pos.y; rel_pos.y = ImGui::GetMousePos().y - button_pos.y;
ZoomedImage(ImTextureID(depth_target | ui::ImGuiDrawer::kIgnoreAlpha), ZoomedImage(ImTextureID(depth_target), rel_pos, button_size, 32.f,
rel_pos, button_size, 32.f, ImVec2(256, 256)); ImVec2(256, 256));
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
@ -1439,7 +1491,8 @@ void TraceViewer::DrawStateUI() {
} }
ImGui::EndChild(); ImGui::EndChild();
} }
if (ImGui::CollapsingHeader("Vertex Shader Output")) { if (ImGui::CollapsingHeader("Vertex Shader Output") &&
QueryVSOutputElementSize()) {
auto size = QueryVSOutputSize(); auto size = QueryVSOutputSize();
auto el_size = QueryVSOutputElementSize(); auto el_size = QueryVSOutputElementSize();
if (size > 0) { if (size > 0) {

View File

@ -54,9 +54,9 @@ class TraceViewer {
virtual uintptr_t GetTextureEntry(const TextureInfo& texture_info, virtual uintptr_t GetTextureEntry(const TextureInfo& texture_info,
const SamplerInfo& sampler_info) = 0; const SamplerInfo& sampler_info) = 0;
virtual size_t QueryVSOutputSize() = 0; virtual size_t QueryVSOutputSize() { return 0; }
virtual size_t QueryVSOutputElementSize() = 0; virtual size_t QueryVSOutputElementSize() { return 0; }
virtual bool QueryVSOutput(void* buffer, size_t size) = 0; virtual bool QueryVSOutput(void* buffer, size_t size) { return false; }
virtual bool Setup(); virtual bool Setup();
@ -80,6 +80,8 @@ class TraceViewer {
void DrawUI(); void DrawUI();
void DrawControllerUI(); void DrawControllerUI();
void DrawPacketDisassemblerUI(); void DrawPacketDisassemblerUI();
int RecursiveDrawCommandBufferUI(const TraceReader::Frame* frame,
TraceReader::CommandBuffer* buffer);
void DrawCommandListUI(); void DrawCommandListUI();
void DrawStateUI(); void DrawStateUI();

View File

@ -0,0 +1,334 @@
/**
******************************************************************************
* 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/gpu/vulkan/buffer_cache.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/base/profiling.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/vulkan/vulkan_gpu_flags.h"
namespace xe {
namespace gpu {
namespace vulkan {
using xe::ui::vulkan::CheckResult;
constexpr VkDeviceSize kConstantRegisterUniformRange =
512 * 4 * 4 + 8 * 4 + 32 * 4;
BufferCache::BufferCache(RegisterFile* register_file,
ui::vulkan::VulkanDevice* device, size_t capacity)
: register_file_(register_file), device_(*device) {
transient_buffer_ = std::make_unique<ui::vulkan::CircularBuffer>(device);
if (!transient_buffer_->Initialize(capacity,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)) {
assert_always();
}
// 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 = 1;
VkDescriptorPoolSize pool_sizes[1];
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
pool_sizes[0].descriptorCount = 2;
descriptor_pool_info.poolSizeCount = 1;
descriptor_pool_info.pPoolSizes = pool_sizes;
auto err = vkCreateDescriptorPool(device_, &descriptor_pool_info, nullptr,
&descriptor_pool_);
CheckResult(err, "vkCreateDescriptorPool");
// Create the descriptor set layout used for our uniform buffer.
// As it is a static binding that uses dynamic offsets during draws we can
// create this once and reuse it forever.
VkDescriptorSetLayoutBinding vertex_uniform_binding;
vertex_uniform_binding.binding = 0;
vertex_uniform_binding.descriptorType =
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
vertex_uniform_binding.descriptorCount = 1;
vertex_uniform_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
vertex_uniform_binding.pImmutableSamplers = nullptr;
VkDescriptorSetLayoutBinding fragment_uniform_binding;
fragment_uniform_binding.binding = 1;
fragment_uniform_binding.descriptorType =
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
fragment_uniform_binding.descriptorCount = 1;
fragment_uniform_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
fragment_uniform_binding.pImmutableSamplers = nullptr;
VkDescriptorSetLayoutCreateInfo descriptor_set_layout_info;
descriptor_set_layout_info.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptor_set_layout_info.pNext = nullptr;
descriptor_set_layout_info.flags = 0;
VkDescriptorSetLayoutBinding uniform_bindings[] = {
vertex_uniform_binding, fragment_uniform_binding,
};
descriptor_set_layout_info.bindingCount =
static_cast<uint32_t>(xe::countof(uniform_bindings));
descriptor_set_layout_info.pBindings = uniform_bindings;
err = vkCreateDescriptorSetLayout(device_, &descriptor_set_layout_info,
nullptr, &descriptor_set_layout_);
CheckResult(err, "vkCreateDescriptorSetLayout");
// Create the descriptor we'll use for the uniform buffer.
// This is what we hand out to everyone (who then also needs to use our
// offsets).
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_;
err = vkAllocateDescriptorSets(device_, &set_alloc_info,
&transient_descriptor_set_);
CheckResult(err, "vkAllocateDescriptorSets");
// Initialize descriptor set with our buffers.
VkDescriptorBufferInfo buffer_info;
buffer_info.buffer = transient_buffer_->gpu_buffer();
buffer_info.offset = 0;
buffer_info.range = kConstantRegisterUniformRange;
VkWriteDescriptorSet descriptor_writes[2];
auto& vertex_uniform_binding_write = descriptor_writes[0];
vertex_uniform_binding_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
vertex_uniform_binding_write.pNext = nullptr;
vertex_uniform_binding_write.dstSet = transient_descriptor_set_;
vertex_uniform_binding_write.dstBinding = 0;
vertex_uniform_binding_write.dstArrayElement = 0;
vertex_uniform_binding_write.descriptorCount = 1;
vertex_uniform_binding_write.descriptorType =
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
vertex_uniform_binding_write.pBufferInfo = &buffer_info;
auto& fragment_uniform_binding_write = descriptor_writes[1];
fragment_uniform_binding_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
fragment_uniform_binding_write.pNext = nullptr;
fragment_uniform_binding_write.dstSet = transient_descriptor_set_;
fragment_uniform_binding_write.dstBinding = 1;
fragment_uniform_binding_write.dstArrayElement = 0;
fragment_uniform_binding_write.descriptorCount = 1;
fragment_uniform_binding_write.descriptorType =
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
fragment_uniform_binding_write.pBufferInfo = &buffer_info;
vkUpdateDescriptorSets(device_, 2, descriptor_writes, 0, nullptr);
}
BufferCache::~BufferCache() {
vkFreeDescriptorSets(device_, descriptor_pool_, 1,
&transient_descriptor_set_);
vkDestroyDescriptorSetLayout(device_, descriptor_set_layout_, nullptr);
vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr);
transient_buffer_->Shutdown();
}
std::pair<VkDeviceSize, VkDeviceSize> BufferCache::UploadConstantRegisters(
const Shader::ConstantRegisterMap& vertex_constant_register_map,
const Shader::ConstantRegisterMap& pixel_constant_register_map,
std::shared_ptr<ui::vulkan::Fence> fence) {
// Fat struct, including all registers:
// struct {
// vec4 float[512];
// uint bool[8];
// uint loop[32];
// };
auto offset = AllocateTransientData(kConstantRegisterUniformRange, fence);
if (offset == VK_WHOLE_SIZE) {
// OOM.
return {VK_WHOLE_SIZE, VK_WHOLE_SIZE};
}
// Copy over all the registers.
const auto& values = register_file_->values;
uint8_t* dest_ptr = transient_buffer_->host_base() + offset;
std::memcpy(dest_ptr, &values[XE_GPU_REG_SHADER_CONSTANT_000_X].f32,
(512 * 4 * 4));
dest_ptr += 512 * 4 * 4;
std::memcpy(dest_ptr, &values[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].u32,
8 * 4);
dest_ptr += 8 * 4;
std::memcpy(dest_ptr, &values[XE_GPU_REG_SHADER_CONSTANT_LOOP_00].u32,
32 * 4);
dest_ptr += 32 * 4;
return {offset, offset};
// Packed upload code.
// This is not currently supported by the shaders, but would be awesome.
// We should be able to use this for any shader that does not do dynamic
// constant indexing.
#if 0
// Allocate space in the buffer for our data.
auto offset =
AllocateTransientData(constant_register_map.packed_byte_length, fence);
if (offset == VK_WHOLE_SIZE) {
// OOM.
return VK_WHOLE_SIZE;
}
// Run through registers and copy them into the buffer.
// TODO(benvanik): optimize this - it's hit twice every call.
const auto& values = register_file_->values;
uint8_t* dest_ptr =
reinterpret_cast<uint8_t*>(transient_buffer_data_) + offset;
for (int i = 0; i < 4; ++i) {
auto piece = constant_register_map.float_bitmap[i];
if (!piece) {
continue;
}
for (int j = 0, sh = 0; j < 64; ++j, sh << 1) {
if (piece & sh) {
xe::copy_128_aligned(
dest_ptr,
&values[XE_GPU_REG_SHADER_CONSTANT_000_X + i * 64 + j].f32, 1);
dest_ptr += 16;
}
}
}
for (int i = 0; i < 32; ++i) {
if (constant_register_map.int_bitmap & (1 << i)) {
xe::store<uint32_t>(dest_ptr,
values[XE_GPU_REG_SHADER_CONSTANT_LOOP_00 + i].u32);
dest_ptr += 4;
}
}
for (int i = 0; i < 8; ++i) {
if (constant_register_map.bool_bitmap[i]) {
xe::store<uint32_t>(
dest_ptr, values[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031 + i].u32);
dest_ptr += 4;
}
}
return offset;
#endif // 0
}
std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadIndexBuffer(
const void* source_ptr, size_t source_length, IndexFormat format,
std::shared_ptr<ui::vulkan::Fence> fence) {
// TODO(benvanik): check cache.
// Allocate space in the buffer for our data.
auto offset = AllocateTransientData(source_length, fence);
if (offset == VK_WHOLE_SIZE) {
// OOM.
return {nullptr, VK_WHOLE_SIZE};
}
// Copy data into the buffer.
// TODO(benvanik): get min/max indices and pass back?
// TODO(benvanik): memcpy then use compute shaders to swap?
if (format == IndexFormat::kInt16) {
// Endian::k8in16, swap half-words.
xe::copy_and_swap_16_aligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 2);
} else if (format == IndexFormat::kInt32) {
// Endian::k8in32, swap words.
xe::copy_and_swap_32_aligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 4);
}
return {transient_buffer_->gpu_buffer(), offset};
}
std::pair<VkBuffer, VkDeviceSize> BufferCache::UploadVertexBuffer(
const void* source_ptr, size_t source_length, Endian endian,
std::shared_ptr<ui::vulkan::Fence> fence) {
// TODO(benvanik): check cache.
// Allocate space in the buffer for our data.
auto offset = AllocateTransientData(source_length, fence);
if (offset == VK_WHOLE_SIZE) {
// OOM.
return {nullptr, VK_WHOLE_SIZE};
}
// Copy data into the buffer.
// TODO(benvanik): memcpy then use compute shaders to swap?
assert_true(endian == Endian::k8in32);
if (endian == Endian::k8in32) {
// Endian::k8in32, swap words.
xe::copy_and_swap_32_aligned(transient_buffer_->host_base() + offset,
source_ptr, source_length / 4);
}
return {transient_buffer_->gpu_buffer(), offset};
}
VkDeviceSize BufferCache::AllocateTransientData(
VkDeviceSize length, std::shared_ptr<ui::vulkan::Fence> fence) {
// Try fast path (if we have space).
VkDeviceSize offset = TryAllocateTransientData(length, fence);
if (offset != VK_WHOLE_SIZE) {
return offset;
}
// Ran out of easy allocations.
// Try consuming fences before we panic.
transient_buffer_->Scavenge();
// Try again. It may still fail if we didn't get enough space back.
offset = TryAllocateTransientData(length, fence);
return offset;
}
VkDeviceSize BufferCache::TryAllocateTransientData(
VkDeviceSize length, std::shared_ptr<ui::vulkan::Fence> fence) {
auto alloc = transient_buffer_->Acquire(length, fence);
if (alloc) {
return alloc->offset;
}
// No more space.
return VK_WHOLE_SIZE;
}
void BufferCache::Flush(VkCommandBuffer command_buffer) {
// 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
// run out of space.
if (true) {
// VkEvent finish_event;
// vkCmdSetEvent(cmd_buffer, finish_event,
// VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
}
// Flush memory.
// TODO(benvanik): subrange.
VkMappedMemoryRange dirty_range;
dirty_range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
dirty_range.pNext = nullptr;
dirty_range.memory = transient_buffer_->gpu_memory();
dirty_range.offset = 0;
dirty_range.size = transient_buffer_->capacity();
vkFlushMappedMemoryRanges(device_, 1, &dirty_range);
}
void BufferCache::InvalidateCache() {
// TODO(benvanik): caching.
}
void BufferCache::ClearCache() {
// TODO(benvanik): caching.
}
void BufferCache::Scavenge() { transient_buffer_->Scavenge(); }
} // namespace vulkan
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,117 @@
/**
******************************************************************************
* 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_GPU_VULKAN_BUFFER_CACHE_H_
#define XENIA_GPU_VULKAN_BUFFER_CACHE_H_
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/shader.h"
#include "xenia/gpu/xenos.h"
#include "xenia/ui/vulkan/circular_buffer.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
namespace xe {
namespace gpu {
namespace vulkan {
// Efficiently manages buffers of various kinds.
// Used primarily for uploading index and vertex data from guest memory and
// transient data like shader constants.
class BufferCache {
public:
BufferCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device,
size_t capacity);
~BufferCache();
// Descriptor set containing the dynamic uniform buffer used for constant
// uploads. Used in conjunction with a dynamic offset returned by
// UploadConstantRegisters.
// The set contains two bindings:
// binding = 0: for use in vertex shaders
// binding = 1: for use in fragment shaders
VkDescriptorSet constant_descriptor_set() const {
return transient_descriptor_set_;
}
VkDescriptorSetLayout constant_descriptor_set_layout() const {
return descriptor_set_layout_;
}
// Uploads the constants specified in the register maps to the transient
// uniform storage buffer.
// The registers are tightly packed in order as [floats, ints, bools].
// Returns an offset that can be used with the transient_descriptor_set or
// VK_WHOLE_SIZE if the constants could not be uploaded (OOM).
// The returned offsets may alias.
std::pair<VkDeviceSize, VkDeviceSize> UploadConstantRegisters(
const Shader::ConstantRegisterMap& vertex_constant_register_map,
const Shader::ConstantRegisterMap& pixel_constant_register_map,
std::shared_ptr<ui::vulkan::Fence> fence);
// Uploads index buffer data from guest memory, possibly eliding with
// recently uploaded data or cached copies.
// Returns a buffer and offset that can be used with vkCmdBindIndexBuffer.
// Size will be VK_WHOLE_SIZE if the data could not be uploaded (OOM).
std::pair<VkBuffer, VkDeviceSize> UploadIndexBuffer(
const void* source_ptr, size_t source_length, IndexFormat format,
std::shared_ptr<ui::vulkan::Fence> fence);
// Uploads vertex buffer data from guest memory, possibly eliding with
// recently uploaded data or cached copies.
// Returns a buffer and offset that can be used with vkCmdBindVertexBuffers.
// Size will be VK_WHOLE_SIZE if the data could not be uploaded (OOM).
std::pair<VkBuffer, VkDeviceSize> UploadVertexBuffer(
const void* source_ptr, size_t source_length, Endian endian,
std::shared_ptr<ui::vulkan::Fence> fence);
// Flushes all pending data to the GPU.
// Until this is called the GPU is not guaranteed to see any data.
// The given command buffer will be used to queue up events so that the
// cache can determine when data has been consumed.
void Flush(VkCommandBuffer command_buffer);
// Marks the cache as potentially invalid.
// This is not as strong as ClearCache and is a hint that any and all data
// should be verified before being reused.
void InvalidateCache();
// Clears all cached content and prevents future elision with pending data.
void ClearCache();
// Wipes all data no longer needed.
void Scavenge();
private:
// Allocates a block of memory in the transient buffer.
// When memory is not available fences are checked and space is reclaimed.
// Returns VK_WHOLE_SIZE if requested amount of memory is not available.
VkDeviceSize AllocateTransientData(VkDeviceSize length,
std::shared_ptr<ui::vulkan::Fence> fence);
// Tries to allocate a block of memory in the transient buffer.
// Returns VK_WHOLE_SIZE if requested amount of memory is not available.
VkDeviceSize TryAllocateTransientData(
VkDeviceSize length, std::shared_ptr<ui::vulkan::Fence> fence);
RegisterFile* register_file_ = nullptr;
VkDevice device_ = nullptr;
// Staging ringbuffer we cycle through fast. Used for data we don't
// plan on keeping past the current frame.
std::unique_ptr<ui::vulkan::CircularBuffer> transient_buffer_ = nullptr;
VkDescriptorPool descriptor_pool_ = nullptr;
VkDescriptorSetLayout descriptor_set_layout_ = nullptr;
VkDescriptorSet transient_descriptor_set_ = nullptr;
};
} // namespace vulkan
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_VULKAN_BUFFER_CACHE_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,288 @@
/**
******************************************************************************
* 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_GPU_VULKAN_PIPELINE_CACHE_H_
#define XENIA_GPU_VULKAN_PIPELINE_CACHE_H_
#include <unordered_map>
#include "third_party/xxhash/xxhash.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/spirv_shader_translator.h"
#include "xenia/gpu/vulkan/render_cache.h"
#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"
namespace xe {
namespace gpu {
namespace vulkan {
// Configures and caches pipelines based on render state.
// This is responsible for properly setting all state required for a draw
// including shaders, various blend/etc options, and input configuration.
class PipelineCache {
public:
enum class UpdateStatus {
kCompatible,
kMismatch,
kError,
};
PipelineCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device,
VkDescriptorSetLayout uniform_descriptor_set_layout,
VkDescriptorSetLayout texture_descriptor_set_layout);
~PipelineCache();
// Loads a shader from the cache, possibly translating it.
VulkanShader* LoadShader(ShaderType shader_type, uint32_t guest_address,
const uint32_t* host_address, uint32_t dword_count);
// Configures a pipeline using the current render state and the given render
// pass. If a previously available pipeline is available it will be used,
// otherwise a new one may be created. Any state that can be set dynamically
// in the command buffer is issued at this time.
// Returns whether the pipeline could be successfully created.
UpdateStatus ConfigurePipeline(VkCommandBuffer command_buffer,
const RenderState* render_state,
VulkanShader* vertex_shader,
VulkanShader* pixel_shader,
PrimitiveType primitive_type,
VkPipeline* pipeline_out);
// Sets required dynamic state on the command buffer.
// Only state that has changed since the last call will be set unless
// full_update is true.
bool SetDynamicState(VkCommandBuffer command_buffer, bool full_update);
// Pipeline layout shared by all pipelines.
VkPipelineLayout pipeline_layout() const { return pipeline_layout_; }
// Clears all cached content.
void ClearCache();
private:
// Creates or retrieves an existing pipeline for the currently configured
// state.
VkPipeline GetPipeline(const RenderState* render_state, uint64_t hash_key);
bool TranslateShader(VulkanShader* shader, xenos::xe_gpu_program_cntl_t cntl);
void DumpShaderDisasmNV(const VkGraphicsPipelineCreateInfo& info);
// Gets a geometry shader used to emulate the given primitive type.
// Returns nullptr if the primitive doesn't need to be emulated.
VkShaderModule GetGeometryShader(PrimitiveType primitive_type,
bool is_line_mode);
RegisterFile* register_file_ = nullptr;
VkDevice device_ = nullptr;
// Reusable shader translator.
SpirvShaderTranslator shader_translator_;
// Disassembler used to get the SPIRV disasm. Only used in debug.
xe::ui::spirv::SpirvDisassembler disassembler_;
// All loaded shaders mapped by their guest hash key.
std::unordered_map<uint64_t, VulkanShader*> shader_map_;
// Vulkan pipeline cache, which in theory helps us out.
// This can be serialized to disk and reused, if we want.
VkPipelineCache pipeline_cache_ = nullptr;
// Layout used for all pipelines describing our uniforms, textures, and push
// constants.
VkPipelineLayout pipeline_layout_ = nullptr;
// Shared geometry shaders.
struct {
VkShaderModule line_quad_list;
VkShaderModule point_list;
VkShaderModule quad_list;
VkShaderModule rect_list;
} geometry_shaders_;
// Hash state used to incrementally produce pipeline hashes during update.
// By the time the full update pass has run the hash will represent the
// current state in a way that can uniquely identify the produced VkPipeline.
XXH64_state_t hash_state_;
// All previously generated pipelines mapped by hash.
std::unordered_map<uint64_t, VkPipeline> cached_pipelines_;
// Previously used pipeline. This matches our current state settings
// and allows us to quickly(ish) reuse the pipeline if no registers have
// changed.
VkPipeline current_pipeline_ = nullptr;
private:
UpdateStatus UpdateState(VulkanShader* vertex_shader,
VulkanShader* pixel_shader,
PrimitiveType primitive_type);
UpdateStatus UpdateShaderStages(VulkanShader* vertex_shader,
VulkanShader* pixel_shader,
PrimitiveType primitive_type);
UpdateStatus UpdateVertexInputState(VulkanShader* vertex_shader);
UpdateStatus UpdateInputAssemblyState(PrimitiveType primitive_type);
UpdateStatus UpdateViewportState();
UpdateStatus UpdateRasterizationState(PrimitiveType primitive_type);
UpdateStatus UpdateMultisampleState();
UpdateStatus UpdateDepthStencilState();
UpdateStatus UpdateColorBlendState();
bool SetShadowRegister(uint32_t* dest, uint32_t register_name);
bool SetShadowRegister(float* dest, uint32_t register_name);
struct UpdateRenderTargetsRegisters {
uint32_t rb_modecontrol;
uint32_t rb_surface_info;
uint32_t rb_color_info;
uint32_t rb_color1_info;
uint32_t rb_color2_info;
uint32_t rb_color3_info;
uint32_t rb_color_mask;
uint32_t rb_depthcontrol;
uint32_t rb_stencilrefmask;
uint32_t rb_depth_info;
UpdateRenderTargetsRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_render_targets_regs_;
struct UpdateShaderStagesRegisters {
PrimitiveType primitive_type;
uint32_t pa_su_sc_mode_cntl;
uint32_t sq_program_cntl;
VulkanShader* vertex_shader;
VulkanShader* pixel_shader;
UpdateShaderStagesRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_shader_stages_regs_;
VkPipelineShaderStageCreateInfo update_shader_stages_info_[3];
uint32_t update_shader_stages_stage_count_ = 0;
struct UpdateVertexInputStateRegisters {
VulkanShader* vertex_shader;
UpdateVertexInputStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_vertex_input_state_regs_;
VkPipelineVertexInputStateCreateInfo update_vertex_input_state_info_;
VkVertexInputBindingDescription update_vertex_input_state_binding_descrs_[64];
VkVertexInputAttributeDescription
update_vertex_input_state_attrib_descrs_[64];
struct UpdateInputAssemblyStateRegisters {
PrimitiveType primitive_type;
uint32_t pa_su_sc_mode_cntl;
uint32_t multi_prim_ib_reset_index;
UpdateInputAssemblyStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_input_assembly_state_regs_;
VkPipelineInputAssemblyStateCreateInfo update_input_assembly_state_info_;
struct UpdateViewportStateRegisters {
// uint32_t pa_cl_clip_cntl;
uint32_t rb_surface_info;
uint32_t pa_cl_vte_cntl;
uint32_t pa_su_sc_mode_cntl;
uint32_t pa_sc_window_offset;
uint32_t pa_sc_window_scissor_tl;
uint32_t pa_sc_window_scissor_br;
float pa_cl_vport_xoffset;
float pa_cl_vport_yoffset;
float pa_cl_vport_zoffset;
float pa_cl_vport_xscale;
float pa_cl_vport_yscale;
float pa_cl_vport_zscale;
UpdateViewportStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_viewport_state_regs_;
VkPipelineViewportStateCreateInfo update_viewport_state_info_;
struct UpdateRasterizationStateRegisters {
PrimitiveType primitive_type;
uint32_t pa_su_sc_mode_cntl;
uint32_t pa_sc_screen_scissor_tl;
uint32_t pa_sc_screen_scissor_br;
uint32_t pa_sc_viz_query;
uint32_t multi_prim_ib_reset_index;
UpdateRasterizationStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_rasterization_state_regs_;
VkPipelineRasterizationStateCreateInfo update_rasterization_state_info_;
struct UpdateMultisampleStateeRegisters {
uint32_t pa_sc_aa_config;
uint32_t pa_su_sc_mode_cntl;
uint32_t rb_surface_info;
UpdateMultisampleStateeRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_multisample_state_regs_;
VkPipelineMultisampleStateCreateInfo update_multisample_state_info_;
struct UpdateDepthStencilStateRegisters {
uint32_t rb_depthcontrol;
uint32_t rb_stencilrefmask;
UpdateDepthStencilStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_depth_stencil_state_regs_;
VkPipelineDepthStencilStateCreateInfo update_depth_stencil_state_info_;
struct UpdateColorBlendStateRegisters {
uint32_t rb_colorcontrol;
uint32_t rb_color_mask;
uint32_t rb_blendcontrol[4];
uint32_t rb_modecontrol;
UpdateColorBlendStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_color_blend_state_regs_;
VkPipelineColorBlendStateCreateInfo update_color_blend_state_info_;
VkPipelineColorBlendAttachmentState update_color_blend_attachment_states_[4];
struct SetDynamicStateRegisters {
uint32_t pa_sc_window_offset;
uint32_t pa_su_sc_mode_cntl;
uint32_t pa_sc_window_scissor_tl;
uint32_t pa_sc_window_scissor_br;
uint32_t rb_surface_info;
uint32_t pa_cl_vte_cntl;
float pa_cl_vport_xoffset;
float pa_cl_vport_yoffset;
float pa_cl_vport_zoffset;
float pa_cl_vport_xscale;
float pa_cl_vport_yscale;
float pa_cl_vport_zscale;
float rb_blend_rgba[4];
uint32_t sq_program_cntl;
uint32_t sq_context_misc;
uint32_t rb_colorcontrol;
float rb_alpha_ref;
SetDynamicStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} set_dynamic_state_registers_;
};
} // namespace vulkan
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_VULKAN_PIPELINE_CACHE_H_

View File

@ -0,0 +1,128 @@
project_root = "../../../.."
include(project_root.."/tools/build")
group("src")
project("xenia-gpu-vulkan")
uuid("717590b4-f579-4162-8f23-0624e87d6cca")
kind("StaticLib")
language("C++")
links({
"vulkan-loader",
"xenia-base",
"xenia-gpu",
"xenia-ui",
"xenia-ui-spirv",
"xenia-ui-vulkan",
"xxhash",
})
defines({
})
includedirs({
project_root.."/third_party/gflags/src",
})
local_platform_files()
files({
"shaders/bin/*.h",
})
-- TODO(benvanik): kill this and move to the debugger UI.
group("src")
project("xenia-gpu-vulkan-trace-viewer")
uuid("86a1dddc-a26a-4885-8c55-cf745225d93e")
kind("WindowedApp")
language("C++")
links({
"gflags",
"imgui",
"vulkan-loader",
"xenia-apu",
"xenia-apu-nop",
"xenia-apu-xaudio2",
"xenia-base",
"xenia-core",
"xenia-cpu",
"xenia-cpu-backend-x64",
"xenia-gpu",
"xenia-gpu-vulkan",
"xenia-hid-nop",
"xenia-hid-winkey",
"xenia-hid-xinput",
"xenia-kernel",
"xenia-ui",
"xenia-ui-spirv",
"xenia-ui-vulkan",
"xenia-vfs",
})
flags({
"WinMain", -- Use WinMain instead of main.
})
defines({
})
includedirs({
project_root.."/third_party/gflags/src",
})
files({
"vulkan_trace_viewer_main.cc",
"../../base/main_"..platform_suffix..".cc",
})
filter("platforms:Windows")
-- Only create the .user file if it doesn't already exist.
local user_file = project_root.."/build/xenia-gpu-vulkan-trace-viewer.vcxproj.user"
if not os.isfile(user_file) then
debugdir(project_root)
debugargs({
"--flagfile=scratch/flags.txt",
"2>&1",
"1>scratch/stdout-trace-viewer.txt",
})
end
group("src")
project("xenia-gpu-vulkan-trace-dump")
uuid("0dd0dd1c-b321-494d-ab9a-6c062f0c65cc")
kind("ConsoleApp")
language("C++")
links({
"gflags",
"imgui",
"vulkan-loader",
"xenia-apu",
"xenia-apu-nop",
"xenia-apu-xaudio2",
"xenia-base",
"xenia-core",
"xenia-cpu",
"xenia-cpu-backend-x64",
"xenia-gpu",
"xenia-gpu-vulkan",
"xenia-hid-nop",
"xenia-hid-winkey",
"xenia-hid-xinput",
"xenia-kernel",
"xenia-ui",
"xenia-ui-spirv",
"xenia-ui-vulkan",
"xenia-vfs",
})
defines({
})
includedirs({
project_root.."/third_party/gflags/src",
})
files({
"vulkan_trace_dump_main.cc",
"../../base/main_"..platform_suffix..".cc",
})
filter("platforms:Windows")
-- Only create the .user file if it doesn't already exist.
local user_file = project_root.."/build/xenia-gpu-vulkan-trace-dump.vcxproj.user"
if not os.isfile(user_file) then
debugdir(project_root)
debugargs({
"--flagfile=scratch/flags.txt",
"2>&1",
"1>scratch/stdout-trace-dump.txt",
})
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,383 @@
/**
******************************************************************************
* 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_GPU_VULKAN_RENDER_CACHE_H_
#define XENIA_GPU_VULKAN_RENDER_CACHE_H_
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/shader.h"
#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"
namespace xe {
namespace gpu {
namespace vulkan {
// TODO(benvanik): make public API?
class CachedTileView;
class CachedFramebuffer;
class CachedRenderPass;
// Uniquely identifies EDRAM tiles.
struct TileViewKey {
// Offset into EDRAM in 5120b tiles.
uint16_t tile_offset;
// Tile width of the view in base 80x16 tiles.
uint16_t tile_width;
// Tile height of the view in base 80x16 tiles.
uint16_t tile_height;
// 1 if format is ColorRenderTargetFormat, else DepthRenderTargetFormat.
uint16_t color_or_depth : 1;
// Surface MSAA samples
uint16_t msaa_samples : 2;
// Either ColorRenderTargetFormat or DepthRenderTargetFormat.
uint16_t edram_format : 13;
};
static_assert(sizeof(TileViewKey) == 8, "Key must be tightly packed");
// Cached view representing EDRAM memory.
// TODO(benvanik): reuse VkImage's with multiple VkViews for compatible
// formats?
class CachedTileView {
public:
// Key identifying the view in the cache.
TileViewKey key;
// Image
VkImage image = nullptr;
// Simple view on the image matching the format.
VkImageView image_view = nullptr;
// Memory buffer
VkDeviceMemory memory = nullptr;
// Image sample count
VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_1_BIT;
CachedTileView(ui::vulkan::VulkanDevice* device,
VkCommandBuffer command_buffer, VkDeviceMemory edram_memory,
TileViewKey view_key);
~CachedTileView();
bool IsEqual(const TileViewKey& other_key) const {
auto a = reinterpret_cast<const uint64_t*>(&key);
auto b = reinterpret_cast<const uint64_t*>(&other_key);
return *a == *b;
}
bool operator<(const CachedTileView& other) const {
return key.tile_offset < other.key.tile_offset;
}
private:
VkDevice device_ = nullptr;
};
// Parsed render configuration from the current render state.
struct RenderConfiguration {
// Render mode (color+depth, depth-only, etc).
xenos::ModeControl mode_control;
// Target surface pitch multiplied by MSAA, in pixels.
uint32_t surface_pitch_px;
// ESTIMATED target surface height multiplied by MSAA, in pixels.
uint32_t surface_height_px;
// Surface MSAA setting.
MsaaSamples surface_msaa;
// Color attachments for the 4 render targets.
struct {
bool used;
uint32_t edram_base;
ColorRenderTargetFormat format;
} color[4];
// Depth/stencil attachment.
struct {
bool used;
uint32_t edram_base;
DepthRenderTargetFormat format;
} depth_stencil;
};
// Current render state based on the register-specified configuration.
struct RenderState {
// Parsed configuration.
RenderConfiguration config;
// Render pass (to be used with pipelines/etc).
CachedRenderPass* render_pass = nullptr;
VkRenderPass render_pass_handle = nullptr;
// Target framebuffer bound to the render pass.
CachedFramebuffer* framebuffer = nullptr;
VkFramebuffer framebuffer_handle = nullptr;
bool color_attachment_written[4] = {false};
bool depth_attachment_written = false;
};
// Manages the virtualized EDRAM and the render target cache.
//
// On the 360 the render target is an opaque block of memory in EDRAM that's
// only accessible via resolves. We use this to our advantage to simulate
// something like it as best we can by having a shared backing memory with
// a multitude of views for each tile location in EDRAM.
//
// This allows us to have the same base address write to the same memory
// regardless of framebuffer format. Resolving then uses whatever format the
// resolve requests straight from the backing memory.
//
// EDRAM is a beast and we only approximate it as best we can. Basically,
// the 10MiB of EDRAM is composed of 2048 5120b tiles. Each tile is 80x16px.
// +-----+-----+-----+---
// |tile0|tile1|tile2|... 2048 times
// +-----+-----+-----+---
// Operations dealing with EDRAM deal in tile offsets, so base 0x100 is tile
// offset 256, 256*5120=1310720b into the buffer. All rendering operations are
// aligned to tiles so trying to draw at 256px wide will have a real width of
// 320px by rounding up to the next tile.
//
// MSAA and other settings will modify the exact pixel sizes, like 4X makes
// each tile effectively 40x8px / 2X makes each tile 80x8px, but they are still
// all 5120b. As we try to emulate this we adjust our viewport when rendering to
// stretch pixels as needed.
//
// It appears that games also take advantage of MSAA stretching tiles when doing
// clears. Games will clear a view with 1/2X pitch/height and 4X MSAA and then
// later draw to that view with 1X pitch/height and 1X MSAA.
//
// The good news is that games cannot read EDRAM directly but must use a copy
// operation to get the data out. That gives us a chance to do whatever we
// need to (re-tile, etc) only when requested.
//
// To approximate the tiled EDRAM layout we use a single large chunk of memory.
// From this memory we create many VkImages (and VkImageViews) of various
// formats and dimensions as requested by the game. These are used as
// attachments during rendering and as sources during copies. They are also
// heavily aliased - lots of images will reference the same locations in the
// underlying EDRAM buffer. The only requirement is that there are no hazards
// with specific tiles (reading/writing the same tile through different images)
// and otherwise it should be ok *fingers crossed*.
//
// One complication is the copy/resolve process itself: we need to give back
// the data asked for in the format desired and where it goes is arbitrary
// (any address in physical memory). If the game is good we get resolves of
// EDRAM into fixed base addresses with scissored regions. If the game is bad
// we are broken.
//
// Resolves from EDRAM result in tiled textures - that's texture tiles, not
// EDRAM tiles. If we wanted to ensure byte-for-byte correctness we'd need to
// then tile the images as we wrote them out. For now, we just attempt to
// get the (X, Y) in linear space and do that. This really comes into play
// when multiple resolves write to the same texture or memory aliased by
// multiple textures - which is common due to predicated tiling. The examples
// below demonstrate what this looks like, but the important thing is that
// we are aware of partial textures and overlapping regions.
//
// TODO(benvanik): what, if any, barriers do we need? any transitions?
//
// Example with multiple render targets:
// Two color targets of 256x256px tightly packed in EDRAM:
// color target 0: base 0x0, pitch 320, scissor 0,0, 256x256
// starts at tile 0, buffer offset 0
// contains 64 tiles (320/80)*(256/16)
// color target 1: base 0x40, pitch 320, scissor 256,0, 256x256
// starts at tile 64 (after color target 0), buffer offset 327680b
// contains 64 tiles
// In EDRAM each set of 64 tiles is contiguous:
// +------+------+ +------+------+------+
// |ct0.0 |ct0.1 |...|ct0.63|ct1.0 |ct1.1 |...
// +------+------+ +------+------+------+
// To render into these, we setup two VkImages:
// image 0: bound to buffer offset 0, 320x256x4=327680b
// image 1: bound to buffer offset 327680b, 320x256x4=327680b
// So when we render to them:
// +------+-+ scissored to 256x256, actually 320x256
// | . | | <- . appears at some untiled offset in the buffer, but
// | | | consistent if aliased with the same format
// +------+-+
// In theory, this gives us proper aliasing in most cases.
//
// Example with horizontal predicated tiling:
// Trying to render 1024x576 @4X MSAA, splitting into two regions
// horizontally:
// +----------+
// | 1024x288 |
// +----------+
// | 1024x288 |
// +----------+
// EDRAM configured for 1056x288px with tile size 2112x567px (4X MSAA):
// color target 0: base 0x0, pitch 1080, 26x36 tiles
// First render (top):
// window offset 0,0
// scissor 0,0, 1024x288
// First resolve (top):
// RB_COPY_DEST_BASE 0x1F45D000
// RB_COPY_DEST_PITCH pitch=1024, height=576
// vertices: 0,0, 1024,0, 1024,288
// Second render (bottom):
// window offset 0,-288
// scissor 0,288, 1024x288
// Second resolve (bottom):
// RB_COPY_DEST_BASE 0x1F57D000 (+1179648b)
// RB_COPY_DEST_PITCH pitch=1024, height=576
// (exactly 1024x288*4b after first resolve)
// vertices: 0,288, 1024,288, 1024,576
// Resolving here is easy as the textures are contiguous in memory. We can
// snoop in the first resolve with the dest height to know the total size,
// and in the second resolve see that it overlaps and place it in the
// existing target.
//
// Example with vertical predicated tiling:
// Trying to render 1280x720 @2X MSAA, splitting into two regions
// vertically:
// +-----+-----+
// | 640 | 640 |
// | x | x |
// | 720 | 720 |
// +-----+-----+
// EDRAM configured for 640x736px with tile size 640x1472px (2X MSAA):
// color target 0: base 0x0, pitch 640, 8x92 tiles
// First render (left):
// window offset 0,0
// scissor 0,0, 640x720
// First resolve (left):
// RB_COPY_DEST_BASE 0x1BC6D000
// RB_COPY_DEST_PITCH pitch=1280, height=720
// vertices: 0,0, 640,0, 640,720
// Second render (right):
// window offset -640,0
// scissor 640,0, 640x720
// Second resolve (right):
// RB_COPY_DEST_BASE 0x1BC81000 (+81920b)
// RB_COPY_DEST_PITCH pitch=1280, height=720
// vertices: 640,0, 1280,0, 1280,720
// Resolving here is much more difficult as resolves are tiled and the right
// half of the texture is 81920b away:
// 81920/4bpp=20480px, /32 (texture tile size)=640px
// We know the texture size with the first resolve and with the second we
// 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();
// Call this to determine if you should start a new render pass or continue
// with an already open pass.
bool dirty() const;
// Begins a render pass targeting the state-specified framebuffer formats.
// The command buffer will be transitioned into the render pass phase.
const RenderState* BeginRenderPass(VkCommandBuffer command_buffer,
VulkanShader* vertex_shader,
VulkanShader* pixel_shader);
// Ends the current render pass.
// The command buffer will be transitioned out of the render pass phase.
void EndRenderPass();
// Clears all cached content.
void ClearCache();
// Queues commands to copy EDRAM contents into an image.
// The command buffer must not be inside of a render pass when calling this.
void RawCopyToImage(VkCommandBuffer command_buffer, uint32_t edram_base,
VkImage image, VkImageLayout image_layout,
bool color_or_depth, VkOffset3D offset,
VkExtent3D extents);
// Queues commands to blit EDRAM contents into an image.
// The command buffer must not be inside of a render pass when calling this.
void BlitToImage(VkCommandBuffer command_buffer, uint32_t edram_base,
uint32_t pitch, uint32_t height, MsaaSamples num_samples,
VkImage image, VkImageLayout image_layout,
bool color_or_depth, uint32_t format, VkFilter filter,
VkOffset3D offset, VkExtent3D extents);
// Queues commands to clear EDRAM contents with a solid color.
// The command buffer must not be inside of a render pass when calling this.
void ClearEDRAMColor(VkCommandBuffer command_buffer, uint32_t edram_base,
ColorRenderTargetFormat format, uint32_t pitch,
uint32_t height, MsaaSamples num_samples, float* color);
// Queues commands to clear EDRAM contents with depth/stencil values.
// The command buffer must not be inside of a render pass when calling this.
void ClearEDRAMDepthStencil(VkCommandBuffer command_buffer,
uint32_t edram_base,
DepthRenderTargetFormat format, uint32_t pitch,
uint32_t height, MsaaSamples num_samples,
float depth, uint32_t stencil);
// Queues commands to fill EDRAM contents with a constant value.
// The command buffer must not be inside of a render pass when calling this.
void FillEDRAM(VkCommandBuffer command_buffer, uint32_t value);
private:
// Parses the current state into a configuration object.
bool ParseConfiguration(RenderConfiguration* config);
// Finds a tile view. Returns nullptr if none found matching the key.
CachedTileView* FindTileView(const TileViewKey& view_key) const;
// Gets or creates a tile view with the given parameters.
CachedTileView* FindOrCreateTileView(VkCommandBuffer command_buffer,
const TileViewKey& view_key);
void UpdateTileView(VkCommandBuffer command_buffer, CachedTileView* view,
bool load, bool insert_barrier = true);
// Gets or creates a render pass and frame buffer for the given configuration.
// This attempts to reuse as much as possible across render passes and
// framebuffers.
bool ConfigureRenderPass(VkCommandBuffer command_buffer,
RenderConfiguration* config,
CachedRenderPass** out_render_pass,
CachedFramebuffer** out_framebuffer);
RegisterFile* register_file_ = nullptr;
ui::vulkan::VulkanDevice* device_ = nullptr;
// Entire 10MiB of EDRAM.
VkDeviceMemory edram_memory_ = nullptr;
// Buffer overlayed 1:1 with edram_memory_ to allow raw access.
VkBuffer edram_buffer_ = nullptr;
// Cache of VkImage and VkImageView's for all of our EDRAM tilings.
// TODO(benvanik): non-linear lookup? Should only be a small number of these.
std::vector<CachedTileView*> cached_tile_views_;
// Cache of render passes based on formats.
std::vector<CachedRenderPass*> cached_render_passes_;
// Shadows of the registers that impact the render pass we choose.
// If the registers don't change between passes we can quickly reuse the
// previous one.
struct ShadowRegisters {
uint32_t rb_modecontrol;
uint32_t rb_surface_info;
uint32_t rb_color_info;
uint32_t rb_color1_info;
uint32_t rb_color2_info;
uint32_t rb_color3_info;
uint32_t rb_depth_info;
uint32_t pa_sc_window_scissor_tl;
uint32_t pa_sc_window_scissor_br;
ShadowRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} shadow_registers_;
bool SetShadowRegister(uint32_t* dest, uint32_t register_name);
// Configuration used for the current/previous Begin/End, representing the
// current shadow register state.
RenderState current_state_;
// Only valid during a BeginRenderPass/EndRenderPass block.
VkCommandBuffer current_command_buffer_ = nullptr;
};
} // namespace vulkan
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_VULKAN_RENDER_CACHE_H_

View File

@ -0,0 +1,187 @@
// generated from `xb genspirv`
// source: line_quad_list.geom
const uint8_t line_quad_list_geom[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 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,
0x11, 0x00, 0x02, 0x00, 0x20, 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, 0x09, 0x00,
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x10, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x05, 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, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74,
0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00, 0x10, 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,
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43,
0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00,
0x05, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x69,
0x6E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x23, 0x00, 0x00, 0x00,
0x6F, 0x75, 0x74, 0x5F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x70, 0x6F, 0x6C,
0x61, 0x74, 0x6F, 0x72, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00,
0x26, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x69, 0x6E, 0x74, 0x65, 0x72,
0x70, 0x6F, 0x6C, 0x61, 0x74, 0x6F, 0x72, 0x73, 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,
0x47, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x26, 0x00, 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, 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, 0x05, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 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, 0x1E, 0x00, 0x05, 0x00,
0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00,
0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 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, 0x20, 0x00, 0x04, 0x00,
0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00,
0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00,
0x24, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x3B, 0x00, 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,
0x41, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x21, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0xDA, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x18, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00,
0x15, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x27, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x3A, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x23, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00,
0x41, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x3C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x3F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x41, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x21, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
0xDA, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, 0x00,
0x44, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x18, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x46, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x47, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x49, 0x00, 0x00, 0x00,
0x48, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00,
0x4B, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00, 0xDB, 0x00, 0x01, 0x00,
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};

View File

@ -0,0 +1,136 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 1
; Bound: 76
; Schema: 0
OpCapability Geometry
OpCapability GeometryPointSize
OpCapability ClipDistance
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Geometry %4 "main" %13 %20 %35 %38
OpExecutionMode %4 InputLinesAdjacency
OpExecutionMode %4 Invocations 1
OpExecutionMode %4 OutputLineStrip
OpExecutionMode %4 OutputVertices 5
OpSource GLSL 450
OpName %4 "main"
OpName %11 "gl_PerVertex"
OpMemberName %11 0 "gl_Position"
OpMemberName %11 1 "gl_PointSize"
OpMemberName %11 2 "gl_ClipDistance"
OpName %13 ""
OpName %16 "gl_PerVertex"
OpMemberName %16 0 "gl_Position"
OpMemberName %16 1 "gl_PointSize"
OpMemberName %16 2 "gl_ClipDistance"
OpName %20 "gl_in"
OpName %35 "out_interpolators"
OpName %38 "in_interpolators"
OpMemberDecorate %11 0 BuiltIn Position
OpMemberDecorate %11 1 BuiltIn PointSize
OpMemberDecorate %11 2 BuiltIn ClipDistance
OpDecorate %11 Block
OpMemberDecorate %16 0 BuiltIn Position
OpMemberDecorate %16 1 BuiltIn PointSize
OpMemberDecorate %16 2 BuiltIn ClipDistance
OpDecorate %16 Block
OpDecorate %35 Location 0
OpDecorate %38 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypeInt 32 0
%9 = OpConstant %8 1
%10 = OpTypeArray %6 %9
%11 = OpTypeStruct %7 %6 %10
%12 = OpTypePointer Output %11
%13 = OpVariable %12 Output
%14 = OpTypeInt 32 1
%15 = OpConstant %14 0
%16 = OpTypeStruct %7 %6 %10
%17 = OpConstant %8 4
%18 = OpTypeArray %16 %17
%19 = OpTypePointer Input %18
%20 = OpVariable %19 Input
%21 = OpTypePointer Input %7
%24 = OpTypePointer Output %7
%26 = OpConstant %14 1
%27 = OpTypePointer Input %6
%30 = OpTypePointer Output %6
%32 = OpConstant %8 16
%33 = OpTypeArray %7 %32
%34 = OpTypePointer Output %33
%35 = OpVariable %34 Output
%36 = OpTypeArray %33 %17
%37 = OpTypePointer Input %36
%38 = OpVariable %37 Input
%39 = OpTypePointer Input %33
%50 = OpConstant %14 2
%59 = OpConstant %14 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%22 = OpAccessChain %21 %20 %15 %15
%23 = OpLoad %7 %22
%25 = OpAccessChain %24 %13 %15
OpStore %25 %23
%28 = OpAccessChain %27 %20 %15 %26
%29 = OpLoad %6 %28
%31 = OpAccessChain %30 %13 %26
OpStore %31 %29
%40 = OpAccessChain %39 %38 %15
%41 = OpLoad %33 %40
OpStore %35 %41
OpEmitVertex
%42 = OpAccessChain %21 %20 %26 %15
%43 = OpLoad %7 %42
%44 = OpAccessChain %24 %13 %15
OpStore %44 %43
%45 = OpAccessChain %27 %20 %26 %26
%46 = OpLoad %6 %45
%47 = OpAccessChain %30 %13 %26
OpStore %47 %46
%48 = OpAccessChain %39 %38 %26
%49 = OpLoad %33 %48
OpStore %35 %49
OpEmitVertex
%51 = OpAccessChain %21 %20 %50 %15
%52 = OpLoad %7 %51
%53 = OpAccessChain %24 %13 %15
OpStore %53 %52
%54 = OpAccessChain %27 %20 %50 %26
%55 = OpLoad %6 %54
%56 = OpAccessChain %30 %13 %26
OpStore %56 %55
%57 = OpAccessChain %39 %38 %50
%58 = OpLoad %33 %57
OpStore %35 %58
OpEmitVertex
%60 = OpAccessChain %21 %20 %59 %15
%61 = OpLoad %7 %60
%62 = OpAccessChain %24 %13 %15
OpStore %62 %61
%63 = OpAccessChain %27 %20 %59 %26
%64 = OpLoad %6 %63
%65 = OpAccessChain %30 %13 %26
OpStore %65 %64
%66 = OpAccessChain %39 %38 %59
%67 = OpLoad %33 %66
OpStore %35 %67
OpEmitVertex
%68 = OpAccessChain %21 %20 %15 %15
%69 = OpLoad %7 %68
%70 = OpAccessChain %24 %13 %15
OpStore %70 %69
%71 = OpAccessChain %27 %20 %15 %26
%72 = OpLoad %6 %71
%73 = OpAccessChain %30 %13 %26
OpStore %73 %72
%74 = OpAccessChain %39 %38 %15
%75 = OpLoad %33 %74
OpStore %35 %75
OpEmitVertex
OpEndPrimitive
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,200 @@
// generated from `xb genspirv`
// source: point_list.geom
const uint8_t point_list_geom[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00,
0x11, 0x00, 0x02, 0x00, 0x20, 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, 0x09, 0x00,
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0x4C, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x10, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x04, 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, 0x03, 0x00,
0x09, 0x00, 0x00, 0x00, 0x70, 0x6F, 0x73, 0x00, 0x05, 0x00, 0x06, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65,
0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50,
0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00,
0x0D, 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, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61,
0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x17, 0x00, 0x00, 0x00, 0x70, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50,
0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00,
0x06, 0x00, 0x07, 0x00, 0x27, 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, 0x27, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44,
0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00,
0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x38, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x61, 0x62, 0x6C,
0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x4A, 0x00, 0x00, 0x00,
0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00,
0x06, 0x00, 0x04, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x6F, 0x75, 0x74, 0x5F, 0x76, 0x74, 0x78, 0x00, 0x05, 0x00, 0x05, 0x00,
0x4D, 0x00, 0x00, 0x00, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x44, 0x61,
0x74, 0x61, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x50, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x76, 0x74, 0x78, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 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,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x14, 0x00, 0x02, 0x00, 0x25, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00,
0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x28, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0xBF, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x2C, 0x00, 0x05, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x07, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x35, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x37, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x48, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x4A, 0x00, 0x00, 0x00,
0x49, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x03, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x4F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x4D, 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,
0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x1D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x19, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x17, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x1D, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x1E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00,
0xF6, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x22, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x22, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0xB1, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00,
0x26, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x4F, 0x00, 0x07, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x38, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x8E, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3F, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00,
0x3F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x43, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00,
0x07, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x46, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00,
0x29, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x47, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00,
0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x4C, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x21, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x54, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x11, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x55, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x01, 0x00,
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};

View File

@ -0,0 +1,148 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 1
; Bound: 86
; Schema: 0
OpCapability Geometry
OpCapability GeometryPointSize
OpCapability ClipDistance
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Geometry %4 "main" %16 %41 %76 %80
OpExecutionMode %4 InputPoints
OpExecutionMode %4 Invocations 1
OpExecutionMode %4 OutputTriangleStrip
OpExecutionMode %4 OutputVertices 4
OpSource GLSL 450
OpName %4 "main"
OpName %9 "pos"
OpName %13 "gl_PerVertex"
OpMemberName %13 0 "gl_Position"
OpMemberName %13 1 "gl_PointSize"
OpMemberName %13 2 "gl_ClipDistance"
OpName %16 "gl_in"
OpName %23 "psize"
OpName %29 "i"
OpName %39 "gl_PerVertex"
OpMemberName %39 0 "gl_Position"
OpMemberName %39 1 "gl_PointSize"
OpMemberName %39 2 "gl_ClipDistance"
OpName %41 ""
OpName %56 "indexable"
OpName %74 "VertexData"
OpMemberName %74 0 "o"
OpName %76 "out_vtx"
OpName %77 "VertexData"
OpMemberName %77 0 "o"
OpName %80 "in_vtx"
OpMemberDecorate %13 0 BuiltIn Position
OpMemberDecorate %13 1 BuiltIn PointSize
OpMemberDecorate %13 2 BuiltIn ClipDistance
OpDecorate %13 Block
OpMemberDecorate %39 0 BuiltIn Position
OpMemberDecorate %39 1 BuiltIn PointSize
OpMemberDecorate %39 2 BuiltIn ClipDistance
OpDecorate %39 Block
OpMemberDecorate %74 0 Location 0
OpMemberDecorate %77 0 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypePointer Function %7
%10 = OpTypeInt 32 0
%11 = OpConstant %10 1
%12 = OpTypeArray %6 %11
%13 = OpTypeStruct %7 %6 %12
%14 = OpTypeArray %13 %11
%15 = OpTypePointer Input %14
%16 = OpVariable %15 Input
%17 = OpTypeInt 32 1
%18 = OpConstant %17 0
%19 = OpTypePointer Input %7
%22 = OpTypePointer Function %6
%24 = OpConstant %17 1
%25 = OpTypePointer Input %6
%28 = OpTypePointer Function %17
%36 = OpConstant %17 4
%37 = OpTypeBool
%39 = OpTypeStruct %7 %6 %12
%40 = OpTypePointer Output %39
%41 = OpVariable %40 Output
%42 = OpTypeVector %6 2
%45 = OpConstant %10 4
%46 = OpTypeArray %42 %45
%47 = OpConstant %6 -1
%48 = OpConstant %6 1
%49 = OpConstantComposite %42 %47 %48
%50 = OpConstantComposite %42 %48 %48
%51 = OpConstantComposite %42 %47 %47
%52 = OpConstantComposite %42 %48 %47
%53 = OpConstantComposite %46 %49 %50 %51 %52
%55 = OpTypePointer Function %46
%57 = OpTypePointer Function %42
%70 = OpTypePointer Output %7
%72 = OpConstant %10 16
%73 = OpTypeArray %7 %72
%74 = OpTypeStruct %73
%75 = OpTypePointer Output %74
%76 = OpVariable %75 Output
%77 = OpTypeStruct %73
%78 = OpTypeArray %77 %11
%79 = OpTypePointer Input %78
%80 = OpVariable %79 Input
%81 = OpTypePointer Input %77
%4 = OpFunction %2 None %3
%5 = OpLabel
%9 = OpVariable %8 Function
%23 = OpVariable %22 Function
%29 = OpVariable %28 Function
%56 = OpVariable %55 Function
%20 = OpAccessChain %19 %16 %18 %18
%21 = OpLoad %7 %20
OpStore %9 %21
%26 = OpAccessChain %25 %16 %18 %24
%27 = OpLoad %6 %26
OpStore %23 %27
OpStore %29 %18
OpBranch %30
%30 = OpLabel
OpLoopMerge %32 %33 None
OpBranch %34
%34 = OpLabel
%35 = OpLoad %17 %29
%38 = OpSLessThan %37 %35 %36
OpBranchConditional %38 %31 %32
%31 = OpLabel
%43 = OpLoad %7 %9
%44 = OpVectorShuffle %42 %43 %43 0 1
%54 = OpLoad %17 %29
OpStore %56 %53
%58 = OpAccessChain %57 %56 %54
%59 = OpLoad %42 %58
%60 = OpLoad %6 %23
%61 = OpVectorTimesScalar %42 %59 %60
%62 = OpFAdd %42 %44 %61
%63 = OpLoad %7 %9
%64 = OpVectorShuffle %42 %63 %63 2 3
%65 = OpCompositeExtract %6 %62 0
%66 = OpCompositeExtract %6 %62 1
%67 = OpCompositeExtract %6 %64 0
%68 = OpCompositeExtract %6 %64 1
%69 = OpCompositeConstruct %7 %65 %66 %67 %68
%71 = OpAccessChain %70 %41 %18
OpStore %71 %69
%82 = OpAccessChain %81 %80 %18
%83 = OpLoad %77 %82
OpStore %76 %83
OpEmitVertex
OpBranch %33
%33 = OpLabel
%84 = OpLoad %17 %29
%85 = OpIAdd %17 %84 %24
OpStore %29 %85
OpBranch %30
%32 = OpLabel
OpEndPrimitive
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,165 @@
// generated from `xb genspirv`
// source: quad_list.geom
const uint8_t quad_list_geom[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00,
0x11, 0x00, 0x02, 0x00, 0x20, 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, 0x09, 0x00,
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x3A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x10, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x04, 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, 0x03, 0x00,
0x08, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x13, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x70, 0x75, 0x74, 0x5F, 0x69, 0x6E,
0x64, 0x65, 0x78, 0x00, 0x05, 0x00, 0x05, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x69, 0x6E, 0x64, 0x65, 0x78, 0x61, 0x62, 0x6C, 0x65, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x24, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50,
0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x06, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00,
0x06, 0x00, 0x07, 0x00, 0x24, 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, 0x24, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44,
0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00,
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x27, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65,
0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00,
0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50,
0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00,
0x27, 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, 0x27, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61,
0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00,
0x3A, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x69, 0x6E, 0x74, 0x65,
0x72, 0x70, 0x6F, 0x6C, 0x61, 0x74, 0x6F, 0x72, 0x73, 0x00, 0x00, 0x00,
0x05, 0x00, 0x07, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x69,
0x6E, 0x74, 0x65, 0x72, 0x70, 0x6F, 0x6C, 0x61, 0x74, 0x6F, 0x72, 0x73,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x24, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x24, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x27, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3A, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x3D, 0x00, 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, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x14, 0x00, 0x02, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x07, 0x00,
0x16, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00, 0x24, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x25, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00,
0x27, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00,
0x27, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x04, 0x00, 0x38, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x37, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x04, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x38, 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,
0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x1D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x0A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x0A, 0x00, 0x00, 0x00,
0xF6, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x0E, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0xB1, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00,
0x12, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x1D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x20, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x35, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
0x26, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x36, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x38, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0xDA, 0x00, 0x01, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x0D, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
0x42, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x08, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x0A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x00,
0xDB, 0x00, 0x01, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};

View File

@ -0,0 +1,125 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 1
; Bound: 68
; Schema: 0
OpCapability Geometry
OpCapability GeometryPointSize
OpCapability ClipDistance
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Geometry %4 "main" %38 %42 %58 %61
OpExecutionMode %4 InputLinesAdjacency
OpExecutionMode %4 Invocations 1
OpExecutionMode %4 OutputTriangleStrip
OpExecutionMode %4 OutputVertices 4
OpSource GLSL 450
OpName %4 "main"
OpName %8 "i"
OpName %19 "input_index"
OpName %29 "indexable"
OpName %36 "gl_PerVertex"
OpMemberName %36 0 "gl_Position"
OpMemberName %36 1 "gl_PointSize"
OpMemberName %36 2 "gl_ClipDistance"
OpName %38 ""
OpName %39 "gl_PerVertex"
OpMemberName %39 0 "gl_Position"
OpMemberName %39 1 "gl_PointSize"
OpMemberName %39 2 "gl_ClipDistance"
OpName %42 "gl_in"
OpName %58 "out_interpolators"
OpName %61 "in_interpolators"
OpMemberDecorate %36 0 BuiltIn Position
OpMemberDecorate %36 1 BuiltIn PointSize
OpMemberDecorate %36 2 BuiltIn ClipDistance
OpDecorate %36 Block
OpMemberDecorate %39 0 BuiltIn Position
OpMemberDecorate %39 1 BuiltIn PointSize
OpMemberDecorate %39 2 BuiltIn ClipDistance
OpDecorate %39 Block
OpDecorate %58 Location 0
OpDecorate %61 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 4
%17 = OpTypeBool
%20 = OpTypeInt 32 0
%21 = OpConstant %20 4
%22 = OpTypeArray %6 %21
%23 = OpConstant %6 1
%24 = OpConstant %6 3
%25 = OpConstant %6 2
%26 = OpConstantComposite %22 %9 %23 %24 %25
%28 = OpTypePointer Function %22
%32 = OpTypeFloat 32
%33 = OpTypeVector %32 4
%34 = OpConstant %20 1
%35 = OpTypeArray %32 %34
%36 = OpTypeStruct %33 %32 %35
%37 = OpTypePointer Output %36
%38 = OpVariable %37 Output
%39 = OpTypeStruct %33 %32 %35
%40 = OpTypeArray %39 %21
%41 = OpTypePointer Input %40
%42 = OpVariable %41 Input
%44 = OpTypePointer Input %33
%47 = OpTypePointer Output %33
%50 = OpTypePointer Input %32
%53 = OpTypePointer Output %32
%55 = OpConstant %20 16
%56 = OpTypeArray %33 %55
%57 = OpTypePointer Output %56
%58 = OpVariable %57 Output
%59 = OpTypeArray %56 %21
%60 = OpTypePointer Input %59
%61 = OpVariable %60 Input
%63 = OpTypePointer Input %56
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
%29 = OpVariable %28 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%27 = OpLoad %6 %8
OpStore %29 %26
%30 = OpAccessChain %7 %29 %27
%31 = OpLoad %6 %30
OpStore %19 %31
%43 = OpLoad %6 %19
%45 = OpAccessChain %44 %42 %43 %9
%46 = OpLoad %33 %45
%48 = OpAccessChain %47 %38 %9
OpStore %48 %46
%49 = OpLoad %6 %19
%51 = OpAccessChain %50 %42 %49 %23
%52 = OpLoad %32 %51
%54 = OpAccessChain %53 %38 %23
OpStore %54 %52
%62 = OpLoad %6 %19
%64 = OpAccessChain %63 %61 %62
%65 = OpLoad %56 %64
OpStore %58 %65
OpEmitVertex
OpBranch %13
%13 = OpLabel
%66 = OpLoad %6 %8
%67 = OpIAdd %6 %66 %23
OpStore %8 %67
OpBranch %10
%12 = OpLabel
OpEndPrimitive
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,423 @@
// generated from `xb genspirv`
// source: rect_list.geom
const uint8_t rect_list_geom[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x18, 0x00, 0x00, 0x00,
0x11, 0x00, 0x02, 0x00, 0x20, 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, 0x09, 0x00,
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00,
0x10, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x06, 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,
0x08, 0x00, 0x00, 0x00, 0x6C, 0x65, 0x66, 0x74, 0x5F, 0x61, 0x6C, 0x69,
0x67, 0x6E, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65,
0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50,
0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00,
0x0E, 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, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61,
0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x20, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50, 0x65, 0x72, 0x56, 0x65,
0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x50,
0x6F, 0x73, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x06, 0x00, 0x07, 0x00,
0x20, 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, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x67, 0x6C, 0x5F, 0x43, 0x6C, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61,
0x6E, 0x63, 0x65, 0x00, 0x05, 0x00, 0x03, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x30, 0x00, 0x00, 0x00,
0x6F, 0x75, 0x74, 0x5F, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x70, 0x6F, 0x6C,
0x61, 0x74, 0x6F, 0x72, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00,
0x33, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x69, 0x6E, 0x74, 0x65, 0x72,
0x70, 0x6F, 0x6C, 0x61, 0x74, 0x6F, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x33, 0x00, 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, 0x14, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00,
0x10, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x13, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x2F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x63, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00,
0x10, 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,
0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x63, 0x00, 0x00, 0x00,
0xB2, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x07, 0x00,
0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0x41, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00,
0x1A, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0xB4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x08, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0xF7, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x7D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00,
0x27, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x29, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x34, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
0xDA, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
0x37, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x26, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x39, 0x00, 0x00, 0x00,
0x38, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00,
0x3A, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3C, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x34, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x16, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x34, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00,
0xDB, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
0x47, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x26, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x49, 0x00, 0x00, 0x00,
0x48, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
0x4B, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x4B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x34, 0x00, 0x00, 0x00,
0x4D, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00,
0x4D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x4E, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x51, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x16, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x54, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x34, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x56, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00,
0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
0x57, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
0x59, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x5A, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
0x5A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
0x5C, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x5D, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00,
0x5D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00,
0x5F, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x62, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x65, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x65, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x67, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x69, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x69, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00,
0x64, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x6C, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x66, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00,
0x64, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00,
0x7F, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00,
0x70, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0x72, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x81, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00,
0x71, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x13, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
0x77, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x79, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x7A, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x68, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x68, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00,
0x64, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00,
0x7C, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x65, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x67, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00, 0xDB, 0x00, 0x01, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x7D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
0x7E, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x7F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x26, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00,
0x7F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00,
0x81, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x83, 0x00, 0x00, 0x00,
0x82, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x34, 0x00, 0x00, 0x00,
0x84, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00,
0x84, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x85, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x88, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x16, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x8B, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x34, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x8D, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00,
0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00,
0x8E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00,
0x90, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00,
0x91, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x93, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x34, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00,
0xDA, 0x00, 0x01, 0x00, 0xDB, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x98, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x16, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x9B, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x34, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x9D, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00,
0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00,
0x9E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00,
0xA0, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00,
0xA1, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x00, 0x00,
0xA3, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0xA3, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x34, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x2E, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00,
0xDA, 0x00, 0x01, 0x00, 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
0xA6, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0xA7, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00,
0x81, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00,
0xA7, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00,
0x83, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00,
0xAA, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x26, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xAE, 0x00, 0x00, 0x00,
0xAD, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00,
0xAF, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
0xB0, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x2B, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB1, 0x00, 0x00, 0x00,
0xB0, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB2, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xB3, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0xB3, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00,
0xB5, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0xB7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0xB8, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00,
0x6B, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xB9, 0x00, 0x00, 0x00,
0xB4, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0xBA, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x13, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00,
0xBC, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0xBE, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x23, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x0A, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00,
0x7F, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00,
0xC0, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00,
0xC2, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00,
0xB2, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
0xC4, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0xC3, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
0xC5, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00,
0x0A, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00,
0xC5, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00,
0xC7, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0xC7, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0xB6, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0xB6, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00,
0xC8, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x13, 0x00, 0x00, 0x00, 0xC9, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB2, 0x00, 0x00, 0x00,
0xC9, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xB3, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0xB5, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x01, 0x00,
0xDB, 0x00, 0x01, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x1F, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00,
};

View File

@ -0,0 +1,317 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 1
; Bound: 202
; Schema: 0
OpCapability Geometry
OpCapability GeometryPointSize
OpCapability ClipDistance
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Geometry %4 "main" %18 %34 %48 %51
OpExecutionMode %4 Triangles
OpExecutionMode %4 Invocations 1
OpExecutionMode %4 OutputTriangleStrip
OpExecutionMode %4 OutputVertices 6
OpSource GLSL 450
OpName %4 "main"
OpName %8 "left_aligned"
OpName %14 "gl_PerVertex"
OpMemberName %14 0 "gl_Position"
OpMemberName %14 1 "gl_PointSize"
OpMemberName %14 2 "gl_ClipDistance"
OpName %18 "gl_in"
OpName %32 "gl_PerVertex"
OpMemberName %32 0 "gl_Position"
OpMemberName %32 1 "gl_PointSize"
OpMemberName %32 2 "gl_ClipDistance"
OpName %34 ""
OpName %48 "out_interpolators"
OpName %51 "in_interpolators"
OpName %100 "i"
OpName %178 "i"
OpMemberDecorate %14 0 BuiltIn Position
OpMemberDecorate %14 1 BuiltIn PointSize
OpMemberDecorate %14 2 BuiltIn ClipDistance
OpDecorate %14 Block
OpMemberDecorate %32 0 BuiltIn Position
OpMemberDecorate %32 1 BuiltIn PointSize
OpMemberDecorate %32 2 BuiltIn ClipDistance
OpDecorate %32 Block
OpDecorate %48 Location 0
OpDecorate %51 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpTypePointer Function %6
%9 = OpTypeFloat 32
%10 = OpTypeVector %9 4
%11 = OpTypeInt 32 0
%12 = OpConstant %11 1
%13 = OpTypeArray %9 %12
%14 = OpTypeStruct %10 %9 %13
%15 = OpConstant %11 3
%16 = OpTypeArray %14 %15
%17 = OpTypePointer Input %16
%18 = OpVariable %17 Input
%19 = OpTypeInt 32 1
%20 = OpConstant %19 0
%21 = OpConstant %11 0
%22 = OpTypePointer Input %9
%25 = OpConstant %19 2
%32 = OpTypeStruct %10 %9 %13
%33 = OpTypePointer Output %32
%34 = OpVariable %33 Output
%35 = OpTypePointer Input %10
%38 = OpTypePointer Output %10
%40 = OpConstant %19 1
%43 = OpTypePointer Output %9
%45 = OpConstant %11 16
%46 = OpTypeArray %10 %45
%47 = OpTypePointer Output %46
%48 = OpVariable %47 Output
%49 = OpTypeArray %46 %15
%50 = OpTypePointer Input %49
%51 = OpVariable %50 Input
%52 = OpTypePointer Input %46
%99 = OpTypePointer Function %19
%107 = OpConstant %19 16
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%100 = OpVariable %99 Function
%178 = OpVariable %99 Function
%23 = OpAccessChain %22 %18 %20 %20 %21
%24 = OpLoad %9 %23
%26 = OpAccessChain %22 %18 %25 %20 %21
%27 = OpLoad %9 %26
%28 = OpFOrdEqual %6 %24 %27
OpStore %8 %28
%29 = OpLoad %6 %8
OpSelectionMerge %31 None
OpBranchConditional %29 %30 %125
%30 = OpLabel
%36 = OpAccessChain %35 %18 %20 %20
%37 = OpLoad %10 %36
%39 = OpAccessChain %38 %34 %20
OpStore %39 %37
%41 = OpAccessChain %22 %18 %20 %40
%42 = OpLoad %9 %41
%44 = OpAccessChain %43 %34 %40
OpStore %44 %42
%53 = OpAccessChain %52 %51 %20
%54 = OpLoad %46 %53
OpStore %48 %54
OpEmitVertex
%55 = OpAccessChain %35 %18 %40 %20
%56 = OpLoad %10 %55
%57 = OpAccessChain %38 %34 %20
OpStore %57 %56
%58 = OpAccessChain %22 %18 %40 %40
%59 = OpLoad %9 %58
%60 = OpAccessChain %43 %34 %40
OpStore %60 %59
%61 = OpAccessChain %52 %51 %40
%62 = OpLoad %46 %61
OpStore %48 %62
OpEmitVertex
%63 = OpAccessChain %35 %18 %25 %20
%64 = OpLoad %10 %63
%65 = OpAccessChain %38 %34 %20
OpStore %65 %64
%66 = OpAccessChain %22 %18 %25 %40
%67 = OpLoad %9 %66
%68 = OpAccessChain %43 %34 %40
OpStore %68 %67
%69 = OpAccessChain %52 %51 %25
%70 = OpLoad %46 %69
OpStore %48 %70
OpEmitVertex
OpEndPrimitive
%71 = OpAccessChain %35 %18 %25 %20
%72 = OpLoad %10 %71
%73 = OpAccessChain %38 %34 %20
OpStore %73 %72
%74 = OpAccessChain %22 %18 %25 %40
%75 = OpLoad %9 %74
%76 = OpAccessChain %43 %34 %40
OpStore %76 %75
%77 = OpAccessChain %52 %51 %25
%78 = OpLoad %46 %77
OpStore %48 %78
OpEmitVertex
%79 = OpAccessChain %35 %18 %40 %20
%80 = OpLoad %10 %79
%81 = OpAccessChain %38 %34 %20
OpStore %81 %80
%82 = OpAccessChain %22 %18 %40 %40
%83 = OpLoad %9 %82
%84 = OpAccessChain %43 %34 %40
OpStore %84 %83
%85 = OpAccessChain %52 %51 %40
%86 = OpLoad %46 %85
OpStore %48 %86
OpEmitVertex
%87 = OpAccessChain %35 %18 %40 %20
%88 = OpLoad %10 %87
%89 = OpAccessChain %35 %18 %25 %20
%90 = OpLoad %10 %89
%91 = OpFAdd %10 %88 %90
%92 = OpAccessChain %35 %18 %20 %20
%93 = OpLoad %10 %92
%94 = OpFSub %10 %91 %93
%95 = OpAccessChain %38 %34 %20
OpStore %95 %94
%96 = OpAccessChain %22 %18 %25 %40
%97 = OpLoad %9 %96
%98 = OpAccessChain %43 %34 %40
OpStore %98 %97
OpStore %100 %20
OpBranch %101
%101 = OpLabel
OpLoopMerge %103 %104 None
OpBranch %105
%105 = OpLabel
%106 = OpLoad %19 %100
%108 = OpSLessThan %6 %106 %107
OpBranchConditional %108 %102 %103
%102 = OpLabel
%109 = OpLoad %19 %100
%110 = OpLoad %19 %100
%111 = OpAccessChain %35 %51 %20 %110
%112 = OpLoad %10 %111
%113 = OpFNegate %10 %112
%114 = OpLoad %19 %100
%115 = OpAccessChain %35 %51 %40 %114
%116 = OpLoad %10 %115
%117 = OpFAdd %10 %113 %116
%118 = OpLoad %19 %100
%119 = OpAccessChain %35 %51 %25 %118
%120 = OpLoad %10 %119
%121 = OpFAdd %10 %117 %120
%122 = OpAccessChain %38 %48 %109
OpStore %122 %121
OpBranch %104
%104 = OpLabel
%123 = OpLoad %19 %100
%124 = OpIAdd %19 %123 %40
OpStore %100 %124
OpBranch %101
%103 = OpLabel
OpEmitVertex
OpEndPrimitive
OpBranch %31
%125 = OpLabel
%126 = OpAccessChain %35 %18 %20 %20
%127 = OpLoad %10 %126
%128 = OpAccessChain %38 %34 %20
OpStore %128 %127
%129 = OpAccessChain %22 %18 %20 %40
%130 = OpLoad %9 %129
%131 = OpAccessChain %43 %34 %40
OpStore %131 %130
%132 = OpAccessChain %52 %51 %20
%133 = OpLoad %46 %132
OpStore %48 %133
OpEmitVertex
%134 = OpAccessChain %35 %18 %40 %20
%135 = OpLoad %10 %134
%136 = OpAccessChain %38 %34 %20
OpStore %136 %135
%137 = OpAccessChain %22 %18 %40 %40
%138 = OpLoad %9 %137
%139 = OpAccessChain %43 %34 %40
OpStore %139 %138
%140 = OpAccessChain %52 %51 %40
%141 = OpLoad %46 %140
OpStore %48 %141
OpEmitVertex
%142 = OpAccessChain %35 %18 %25 %20
%143 = OpLoad %10 %142
%144 = OpAccessChain %38 %34 %20
OpStore %144 %143
%145 = OpAccessChain %22 %18 %25 %40
%146 = OpLoad %9 %145
%147 = OpAccessChain %43 %34 %40
OpStore %147 %146
%148 = OpAccessChain %52 %51 %25
%149 = OpLoad %46 %148
OpStore %48 %149
OpEmitVertex
OpEndPrimitive
%150 = OpAccessChain %35 %18 %20 %20
%151 = OpLoad %10 %150
%152 = OpAccessChain %38 %34 %20
OpStore %152 %151
%153 = OpAccessChain %22 %18 %20 %40
%154 = OpLoad %9 %153
%155 = OpAccessChain %43 %34 %40
OpStore %155 %154
%156 = OpAccessChain %52 %51 %20
%157 = OpLoad %46 %156
OpStore %48 %157
OpEmitVertex
%158 = OpAccessChain %35 %18 %25 %20
%159 = OpLoad %10 %158
%160 = OpAccessChain %38 %34 %20
OpStore %160 %159
%161 = OpAccessChain %22 %18 %25 %40
%162 = OpLoad %9 %161
%163 = OpAccessChain %43 %34 %40
OpStore %163 %162
%164 = OpAccessChain %52 %51 %25
%165 = OpLoad %46 %164
OpStore %48 %165
OpEmitVertex
%166 = OpAccessChain %35 %18 %20 %20
%167 = OpLoad %10 %166
%168 = OpAccessChain %35 %18 %25 %20
%169 = OpLoad %10 %168
%170 = OpFAdd %10 %167 %169
%171 = OpAccessChain %35 %18 %40 %20
%172 = OpLoad %10 %171
%173 = OpFSub %10 %170 %172
%174 = OpAccessChain %38 %34 %20
OpStore %174 %173
%175 = OpAccessChain %22 %18 %25 %40
%176 = OpLoad %9 %175
%177 = OpAccessChain %43 %34 %40
OpStore %177 %176
OpStore %178 %20
OpBranch %179
%179 = OpLabel
OpLoopMerge %181 %182 None
OpBranch %183
%183 = OpLabel
%184 = OpLoad %19 %178
%185 = OpSLessThan %6 %184 %107
OpBranchConditional %185 %180 %181
%180 = OpLabel
%186 = OpLoad %19 %178
%187 = OpLoad %19 %178
%188 = OpAccessChain %35 %51 %20 %187
%189 = OpLoad %10 %188
%190 = OpLoad %19 %178
%191 = OpAccessChain %35 %51 %40 %190
%192 = OpLoad %10 %191
%193 = OpFNegate %10 %192
%194 = OpFAdd %10 %189 %193
%195 = OpLoad %19 %178
%196 = OpAccessChain %35 %51 %25 %195
%197 = OpLoad %10 %196
%198 = OpFAdd %10 %194 %197
%199 = OpAccessChain %38 %48 %186
OpStore %199 %198
OpBranch %182
%182 = OpLabel
%200 = OpLoad %19 %178
%201 = OpIAdd %19 %200 %40
OpStore %178 %201
OpBranch %179
%181 = OpLabel
OpEmitVertex
OpEndPrimitive
OpBranch %31
%31 = OpLabel
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,46 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
#extension all : warn
in gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[];
out gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
};
layout(location = 0) in vec4 in_interpolators[][16];
layout(location = 0) out vec4 out_interpolators[16];
layout(lines_adjacency) in;
layout(line_strip, max_vertices = 5) out;
void main() {
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
out_interpolators = in_interpolators[0];
EmitVertex();
gl_Position = gl_in[1].gl_Position;
gl_PointSize = gl_in[1].gl_PointSize;
out_interpolators = in_interpolators[1];
EmitVertex();
gl_Position = gl_in[2].gl_Position;
gl_PointSize = gl_in[2].gl_PointSize;
out_interpolators = in_interpolators[2];
EmitVertex();
gl_Position = gl_in[3].gl_Position;
gl_PointSize = gl_in[3].gl_PointSize;
out_interpolators = in_interpolators[3];
EmitVertex();
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
out_interpolators = in_interpolators[0];
EmitVertex();
EndPrimitive();
}

View File

@ -0,0 +1,48 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
#extension all : warn
in gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[];
out gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
};
struct VertexData {
vec4 o[16];
};
layout(location = 0) in VertexData in_vtx[];
layout(location = 0) out VertexData out_vtx;
// TODO(benvanik): fetch default point size from register and use that if
// the VS doesn't write oPointSize.
// TODO(benvanik): clamp to min/max.
// TODO(benvanik): figure out how to see which interpolator gets adjusted.
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
void main() {
const vec2 offsets[4] = {
vec2(-1.0, 1.0),
vec2( 1.0, 1.0),
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
};
vec4 pos = gl_in[0].gl_Position;
float psize = gl_in[0].gl_PointSize;
for (int i = 0; i < 4; ++i) {
gl_Position = vec4(pos.xy + offsets[i] * psize, pos.zw);
out_vtx = in_vtx[0];
EmitVertex();
}
EndPrimitive();
}

View File

@ -0,0 +1,34 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
#extension all : warn
in gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[];
out gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
};
layout(location = 0) in vec4 in_interpolators[][16];
layout(location = 0) out vec4 out_interpolators[16];
layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 4) out;
void main() {
const int order[4] = { 0, 1, 3, 2 };
for (int i = 0; i < 4; ++i) {
int input_index = order[i];
gl_Position = gl_in[input_index].gl_Position;
gl_PointSize = gl_in[input_index].gl_PointSize;
out_interpolators = in_interpolators[input_index];
EmitVertex();
}
EndPrimitive();
}

View File

@ -0,0 +1,98 @@
// NOTE: This file is compiled and embedded into the exe.
// Use `xenia-build genspirv` and check in any changes under bin/.
#version 450 core
#extension all : warn
in gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[];
out gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
};
layout(location = 0) in vec4 in_interpolators[][16];
layout(location = 0) out vec4 out_interpolators[16];
layout(triangles) in;
layout(triangle_strip, max_vertices = 6) out;
void main() {
// Most games use the left-aligned form.
bool left_aligned = gl_in[0].gl_Position.x == gl_in[2].gl_Position.x;
if (left_aligned) {
// 0 ------ 1
// | - |
// | // |
// | - |
// 2 ----- [3]
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
out_interpolators = in_interpolators[0];
EmitVertex();
gl_Position = gl_in[1].gl_Position;
gl_PointSize = gl_in[1].gl_PointSize;
out_interpolators = in_interpolators[1];
EmitVertex();
gl_Position = gl_in[2].gl_Position;
gl_PointSize = gl_in[2].gl_PointSize;
out_interpolators = in_interpolators[2];
EmitVertex();
EndPrimitive();
gl_Position = gl_in[2].gl_Position;
gl_PointSize = gl_in[2].gl_PointSize;
out_interpolators = in_interpolators[2];
EmitVertex();
gl_Position = gl_in[1].gl_Position;
gl_PointSize = gl_in[1].gl_PointSize;
out_interpolators = in_interpolators[1];
EmitVertex();
gl_Position = (gl_in[1].gl_Position + gl_in[2].gl_Position) -
gl_in[0].gl_Position;
gl_PointSize = gl_in[2].gl_PointSize;
for (int i = 0; i < 16; ++i) {
out_interpolators[i] = -in_interpolators[0][i] + in_interpolators[1][i] + in_interpolators[2][i];
}
EmitVertex();
EndPrimitive();
} else {
// 0 ------ 1
// | - |
// | \\ |
// | - |
// [3] ----- 2
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
out_interpolators = in_interpolators[0];
EmitVertex();
gl_Position = gl_in[1].gl_Position;
gl_PointSize = gl_in[1].gl_PointSize;
out_interpolators = in_interpolators[1];
EmitVertex();
gl_Position = gl_in[2].gl_Position;
gl_PointSize = gl_in[2].gl_PointSize;
out_interpolators = in_interpolators[2];
EmitVertex();
EndPrimitive();
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
out_interpolators = in_interpolators[0];
EmitVertex();
gl_Position = gl_in[2].gl_Position;
gl_PointSize = gl_in[2].gl_PointSize;
out_interpolators = in_interpolators[2];
EmitVertex();
gl_Position = (gl_in[0].gl_Position + gl_in[2].gl_Position) -
gl_in[1].gl_Position;
gl_PointSize = gl_in[2].gl_PointSize;
for (int i = 0; i < 16; ++i) {
out_interpolators[i] = in_interpolators[0][i] + -in_interpolators[1][i] + in_interpolators[2][i];
}
EmitVertex();
EndPrimitive();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,207 @@
/**
******************************************************************************
* 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_GPU_VULKAN_TEXTURE_CACHE_H_
#define XENIA_GPU_VULKAN_TEXTURE_CACHE_H_
#include <unordered_map>
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/sampler_info.h"
#include "xenia/gpu/shader.h"
#include "xenia/gpu/texture_info.h"
#include "xenia/gpu/trace_writer.h"
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
#include "xenia/gpu/xenos.h"
#include "xenia/ui/vulkan/circular_buffer.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
namespace xe {
namespace gpu {
namespace vulkan {
//
class TextureCache {
public:
struct TextureView;
// This represents an uploaded Vulkan texture.
struct Texture {
TextureInfo texture_info;
std::vector<std::unique_ptr<TextureView>> views;
// True if we know all info about this texture, false otherwise.
// (e.g. we resolve to system memory and may not know the full details about
// this texture)
bool is_full_texture;
VkFormat format;
VkImage image;
VkImageLayout image_layout;
VkDeviceMemory image_memory;
VkDeviceSize memory_offset;
VkDeviceSize memory_size;
uintptr_t access_watch_handle;
bool pending_invalidation;
// Pointer to the latest usage fence.
std::shared_ptr<ui::vulkan::Fence> in_flight_fence;
};
struct TextureView {
Texture* texture;
VkImageView view;
union {
struct {
// FIXME: This only applies on little-endian platforms!
uint16_t swiz_x : 3;
uint16_t swiz_y : 3;
uint16_t swiz_z : 3;
uint16_t swiz_w : 3;
uint16_t : 4;
};
uint16_t swizzle;
};
};
TextureCache(Memory* memory, RegisterFile* register_file,
TraceWriter* trace_writer, ui::vulkan::VulkanDevice* device);
~TextureCache();
// Descriptor set layout containing all possible texture bindings.
// The set contains one descriptor for each texture sampler [0-31].
VkDescriptorSetLayout texture_descriptor_set_layout() const {
return texture_descriptor_set_layout_;
}
// Prepares a descriptor set containing the samplers and images for all
// bindings. The textures will be uploaded/converted/etc as needed.
// Requires a fence to be provided that will be signaled when finished
// using the returned descriptor set.
VkDescriptorSet PrepareTextureSet(
VkCommandBuffer setup_command_buffer,
std::shared_ptr<ui::vulkan::Fence> completion_fence,
const std::vector<Shader::TextureBinding>& vertex_bindings,
const std::vector<Shader::TextureBinding>& pixel_bindings);
// TODO(benvanik): UploadTexture.
// TODO(benvanik): Resolve.
// TODO(benvanik): ReadTexture.
// Looks for a texture either containing or matching these parameters.
// Caller is responsible for checking if the texture returned is an exact
// match or just contains the texture given by the parameters.
// If offset_x and offset_y are not null, this may return a texture that
// contains this address at an offset.
Texture* LookupAddress(uint32_t guest_address, uint32_t width,
uint32_t height, TextureFormat format,
VkOffset2D* out_offset = nullptr);
// Demands a texture for the purpose of resolving from EDRAM. This either
// creates a new texture or returns a previously created texture. texture_info
// is not required to be completely filled out, just guest_address and all
// sizes.
//
// It's possible that this may return an image that is larger than the
// requested size (e.g. resolving into a bigger texture) or an image that
// must have an offset applied. If so, the caller must handle this.
// At the very least, it's guaranteed that the image will be large enough to
// hold the requested size.
Texture* DemandResolveTexture(const TextureInfo& texture_info,
TextureFormat format, VkOffset2D* out_offset);
// Clears all cached content.
void ClearCache();
// Frees any unused resources
void Scavenge();
private:
struct UpdateSetInfo;
// Cached Vulkan sampler.
struct Sampler {
SamplerInfo sampler_info;
VkSampler sampler;
};
// Allocates a new texture and memory to back it on the GPU.
Texture* AllocateTexture(const TextureInfo& texture_info);
bool FreeTexture(Texture* texture);
// Demands a texture. If command_buffer is null and the texture hasn't been
// uploaded to graphics memory already, we will return null and bail.
Texture* Demand(
const TextureInfo& texture_info, VkCommandBuffer command_buffer = nullptr,
std::shared_ptr<ui::vulkan::Fence> completion_fence = nullptr);
TextureView* DemandView(Texture* texture, uint16_t swizzle);
Sampler* Demand(const SamplerInfo& sampler_info);
// Queues commands to upload a texture from system memory, applying any
// conversions necessary. This may flush the command buffer to the GPU if we
// run out of staging memory.
bool UploadTexture2D(VkCommandBuffer command_buffer,
std::shared_ptr<ui::vulkan::Fence> completion_fence,
Texture* dest, TextureInfo src);
bool SetupTextureBindings(
VkCommandBuffer command_buffer,
std::shared_ptr<ui::vulkan::Fence> completion_fence,
UpdateSetInfo* update_set_info,
const std::vector<Shader::TextureBinding>& bindings);
bool SetupTextureBinding(VkCommandBuffer command_buffer,
std::shared_ptr<ui::vulkan::Fence> completion_fence,
UpdateSetInfo* update_set_info,
const Shader::TextureBinding& binding);
Memory* memory_ = nullptr;
RegisterFile* register_file_ = nullptr;
TraceWriter* trace_writer_ = nullptr;
ui::vulkan::VulkanDevice* device_ = nullptr;
VkDescriptorPool descriptor_pool_ = nullptr;
VkDescriptorSetLayout texture_descriptor_set_layout_ = nullptr;
std::list<std::pair<VkDescriptorSet, std::shared_ptr<ui::vulkan::Fence>>>
in_flight_sets_;
ui::vulkan::CircularBuffer staging_buffer_;
std::unordered_map<uint64_t, Texture*> textures_;
std::unordered_map<uint64_t, Sampler*> samplers_;
std::vector<Texture*> resolve_textures_;
std::list<Texture*> pending_delete_textures_;
std::mutex invalidated_textures_mutex_;
std::vector<Texture*>* invalidated_textures_;
std::vector<Texture*> invalidated_textures_sets_[2];
std::mutex invalidated_resolve_textures_mutex_;
std::vector<Texture*> invalidated_resolve_textures_;
struct UpdateSetInfo {
// Bitmap of all 32 fetch constants and whether they have been setup yet.
// This prevents duplication across the vertex and pixel shader.
uint32_t has_setup_fetch_mask;
uint32_t image_write_count = 0;
struct ImageSetInfo {
Dimension dimension;
uint32_t tf_binding;
VkDescriptorImageInfo info;
} image_infos[32];
} update_set_info_;
};
} // namespace vulkan
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_VULKAN_TEXTURE_CACHE_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,126 @@
/**
******************************************************************************
* 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_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_
#define XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_
#include <atomic>
#include <cstring>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <unordered_map>
#include <vector>
#include "xenia/base/threading.h"
#include "xenia/gpu/command_processor.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/vulkan/buffer_cache.h"
#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_shader.h"
#include "xenia/gpu/xenos.h"
#include "xenia/kernel/xthread.h"
#include "xenia/memory.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_util.h"
namespace xe {
namespace gpu {
namespace vulkan {
class VulkanGraphicsSystem;
class TextureCache;
class VulkanCommandProcessor : public CommandProcessor {
public:
VulkanCommandProcessor(VulkanGraphicsSystem* graphics_system,
kernel::KernelState* kernel_state);
~VulkanCommandProcessor() override;
virtual void RequestFrameTrace(const std::wstring& root_path) override;
void ClearCaches() override;
RenderCache* render_cache() { return render_cache_.get(); }
private:
bool SetupContext() override;
void ShutdownContext() override;
void MakeCoherent() override;
void PrepareForWait() override;
void ReturnFromWait() override;
void CreateSwapImages(VkCommandBuffer setup_buffer, VkExtent2D extents);
void DestroySwapImages();
void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
uint32_t frontbuffer_height) override;
Shader* LoadShader(ShaderType shader_type, uint32_t guest_address,
const uint32_t* host_address,
uint32_t dword_count) override;
bool IssueDraw(PrimitiveType primitive_type, uint32_t index_count,
IndexBufferInfo* index_buffer_info) override;
bool PopulateConstants(VkCommandBuffer command_buffer,
VulkanShader* vertex_shader,
VulkanShader* pixel_shader);
bool PopulateIndexBuffer(VkCommandBuffer command_buffer,
IndexBufferInfo* index_buffer_info);
bool PopulateVertexBuffers(VkCommandBuffer command_buffer,
VulkanShader* vertex_shader);
bool PopulateSamplers(VkCommandBuffer command_buffer,
VkCommandBuffer setup_buffer,
VulkanShader* vertex_shader,
VulkanShader* pixel_shader);
bool IssueCopy() override;
xe::ui::vulkan::VulkanDevice* device_ = nullptr;
// front buffer / back buffer memory
VkDeviceMemory fb_memory = nullptr;
VkDeviceMemory bb_memory = nullptr;
// 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;
bool capturing_ = false;
bool trace_requested_ = false;
std::unique_ptr<BufferCache> buffer_cache_;
std::unique_ptr<PipelineCache> pipeline_cache_;
std::unique_ptr<RenderCache> render_cache_;
std::unique_ptr<TextureCache> texture_cache_;
std::unique_ptr<ui::vulkan::CommandBufferPool> command_buffer_pool_;
const RenderState* current_render_state_ = nullptr;
VkCommandBuffer current_command_buffer_ = nullptr;
VkCommandBuffer current_setup_buffer_ = nullptr;
std::shared_ptr<ui::vulkan::Fence> current_batch_fence_;
};
} // namespace vulkan
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_VULKAN_VULKAN_COMMAND_PROCESSOR_H_

View File

@ -0,0 +1,16 @@
/**
******************************************************************************
* 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/gpu/vulkan/vulkan_gpu_flags.h"
DEFINE_bool(vulkan_renderdoc_capture_all, false,
"Capture everything with RenderDoc.");
DEFINE_bool(vulkan_native_msaa, false, "Use native MSAA");
DEFINE_bool(vulkan_dump_disasm, false,
"Dump shader disassembly. NVIDIA only supported.");

View File

@ -0,0 +1,21 @@
/**
******************************************************************************
* 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_GPU_VULKAN_VULKAN_GPU_FLAGS_H_
#define XENIA_GPU_VULKAN_VULKAN_GPU_FLAGS_H_
#include <gflags/gflags.h>
#define FINE_GRAINED_DRAW_SCOPES 1
DECLARE_bool(vulkan_renderdoc_capture_all);
DECLARE_bool(vulkan_native_msaa);
DECLARE_bool(vulkan_dump_disasm);
#endif // XENIA_GPU_VULKAN_VULKAN_GPU_FLAGS_H_

View File

@ -0,0 +1,116 @@
/**
******************************************************************************
* 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/gpu/vulkan/vulkan_graphics_system.h"
#include <algorithm>
#include <cstring>
#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/window.h"
namespace xe {
namespace gpu {
namespace vulkan {
VulkanGraphicsSystem::VulkanGraphicsSystem() {}
VulkanGraphicsSystem::~VulkanGraphicsSystem() = default;
X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor,
kernel::KernelState* kernel_state,
ui::Window* target_window) {
// Must create the provider so we can create contexts.
provider_ = xe::ui::vulkan::VulkanProvider::Create(target_window);
auto result = GraphicsSystem::Setup(processor, kernel_state, target_window);
if (result) {
return result;
}
display_context_ = reinterpret_cast<xe::ui::vulkan::VulkanContext*>(
target_window->context());
return X_STATUS_SUCCESS;
}
void VulkanGraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); }
std::unique_ptr<CommandProcessor>
VulkanGraphicsSystem::CreateCommandProcessor() {
return std::unique_ptr<CommandProcessor>(
new VulkanCommandProcessor(this, kernel_state_));
}
void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {
if (!command_processor_) {
return;
}
// Check for pending swap.
auto& swap_state = command_processor_->swap_state();
{
std::lock_guard<std::mutex> lock(swap_state.mutex);
if (swap_state.pending) {
swap_state.pending = false;
std::swap(swap_state.front_buffer_texture,
swap_state.back_buffer_texture);
}
}
if (!swap_state.front_buffer_texture) {
// Not yet ready.
return;
}
auto swap_chain = display_context_->swap_chain();
auto copy_cmd_buffer = swap_chain->copy_cmd_buffer();
auto front_buffer =
reinterpret_cast<VkImage>(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};
vkCmdPipelineBarrier(copy_cmd_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_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<int32_t>(swap_state.width),
static_cast<int32_t>(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<int32_t>(swap_chain->surface_width()),
static_cast<int32_t>(swap_chain->surface_height()),
1};
vkCmdBlitImage(copy_cmd_buffer, front_buffer, VK_IMAGE_LAYOUT_GENERAL,
swap_chain->surface_image(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region,
VK_FILTER_LINEAR);
}
} // namespace vulkan
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,43 @@
/**
******************************************************************************
* 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_GPU_VULKAN_VULKAN_GRAPHICS_SYSTEM_H_
#define XENIA_GPU_VULKAN_VULKAN_GRAPHICS_SYSTEM_H_
#include <memory>
#include "xenia/gpu/graphics_system.h"
#include "xenia/ui/vulkan/vulkan_context.h"
namespace xe {
namespace gpu {
namespace vulkan {
class VulkanGraphicsSystem : public GraphicsSystem {
public:
VulkanGraphicsSystem();
~VulkanGraphicsSystem() override;
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
ui::Window* target_window) override;
void Shutdown() override;
private:
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
void Swap(xe::ui::UIEvent* e) override;
xe::ui::vulkan::VulkanContext* display_context_ = nullptr;
};
} // namespace vulkan
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_VULKAN_VULKAN_GRAPHICS_SYSTEM_H_

View File

@ -0,0 +1,56 @@
/**
******************************************************************************
* 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/gpu/vulkan/vulkan_shader.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
namespace gpu {
namespace vulkan {
using xe::ui::vulkan::CheckResult;
VulkanShader::VulkanShader(VkDevice device, 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) {}
VulkanShader::~VulkanShader() {
if (shader_module_) {
vkDestroyShaderModule(device_, shader_module_, nullptr);
shader_module_ = nullptr;
}
}
bool VulkanShader::Prepare() {
assert_null(shader_module_);
assert_true(is_valid());
// Create the shader module.
VkShaderModuleCreateInfo shader_info;
shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shader_info.pNext = nullptr;
shader_info.flags = 0;
shader_info.codeSize = translated_binary_.size();
shader_info.pCode =
reinterpret_cast<const uint32_t*>(translated_binary_.data());
auto status =
vkCreateShaderModule(device_, &shader_info, nullptr, &shader_module_);
CheckResult(status, "vkCreateShaderModule");
return status == VK_SUCCESS;
}
} // namespace vulkan
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,42 @@
/**
******************************************************************************
* 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_GPU_VULKAN_VULKAN_SHADER_H_
#define XENIA_GPU_VULKAN_VULKAN_SHADER_H_
#include <string>
#include "xenia/gpu/shader.h"
#include "xenia/ui/vulkan/vulkan_context.h"
namespace xe {
namespace gpu {
namespace vulkan {
class VulkanShader : public Shader {
public:
VulkanShader(VkDevice device, ShaderType shader_type, uint64_t data_hash,
const uint32_t* dword_ptr, uint32_t dword_count);
~VulkanShader() override;
// Available only if the shader is_valid and has been prepared.
VkShaderModule shader_module() const { return shader_module_; }
bool Prepare();
private:
VkDevice device_ = nullptr;
VkShaderModule shader_module_ = nullptr;
};
} // namespace vulkan
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_VULKAN_VULKAN_SHADER_H_

View File

@ -0,0 +1,76 @@
/**
******************************************************************************
* 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/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/gpu/trace_dump.h"
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
#include "xenia/gpu/vulkan/vulkan_graphics_system.h"
namespace xe {
namespace gpu {
namespace vulkan {
using namespace xe::gpu::xenos;
class VulkanTraceDump : public TraceDump {
public:
std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() override {
return std::unique_ptr<gpu::GraphicsSystem>(new VulkanGraphicsSystem());
}
uintptr_t GetColorRenderTarget(uint32_t pitch, MsaaSamples samples,
uint32_t base,
ColorRenderTargetFormat format) override {
auto command_processor = static_cast<VulkanCommandProcessor*>(
graphics_system_->command_processor());
// return command_processor->GetColorRenderTarget(pitch, samples, base,
// format);
return 0;
}
uintptr_t GetDepthRenderTarget(uint32_t pitch, MsaaSamples samples,
uint32_t base,
DepthRenderTargetFormat format) override {
auto command_processor = static_cast<VulkanCommandProcessor*>(
graphics_system_->command_processor());
// return command_processor->GetDepthRenderTarget(pitch, samples, base,
// format);
return 0;
}
uintptr_t GetTextureEntry(const TextureInfo& texture_info,
const SamplerInfo& sampler_info) override {
auto command_processor = static_cast<VulkanCommandProcessor*>(
graphics_system_->command_processor());
// auto entry_view =
// command_processor->texture_cache()->Demand(texture_info,
// sampler_info);
// if (!entry_view) {
// return 0;
//}
// auto texture = entry_view->texture;
// return static_cast<uintptr_t>(texture->handle);
return 0;
}
};
int trace_dump_main(const std::vector<std::wstring>& args) {
VulkanTraceDump trace_dump;
return trace_dump.Main(args);
}
} // namespace vulkan
} // namespace gpu
} // namespace xe
DEFINE_ENTRY_POINT(L"xenia-gpu-vulkan-trace-dump",
L"xenia-gpu-vulkan-trace-dump some.trace",
xe::gpu::vulkan::trace_dump_main);

View File

@ -0,0 +1,76 @@
/**
******************************************************************************
* 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/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/gpu/trace_viewer.h"
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
#include "xenia/gpu/vulkan/vulkan_graphics_system.h"
namespace xe {
namespace gpu {
namespace vulkan {
using namespace xe::gpu::xenos;
class VulkanTraceViewer : public TraceViewer {
public:
std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() override {
return std::unique_ptr<gpu::GraphicsSystem>(new VulkanGraphicsSystem());
}
uintptr_t GetColorRenderTarget(uint32_t pitch, MsaaSamples samples,
uint32_t base,
ColorRenderTargetFormat format) override {
auto command_processor = static_cast<VulkanCommandProcessor*>(
graphics_system_->command_processor());
// return command_processor->GetColorRenderTarget(pitch, samples, base,
// format);
return 0;
}
uintptr_t GetDepthRenderTarget(uint32_t pitch, MsaaSamples samples,
uint32_t base,
DepthRenderTargetFormat format) override {
auto command_processor = static_cast<VulkanCommandProcessor*>(
graphics_system_->command_processor());
// return command_processor->GetDepthRenderTarget(pitch, samples, base,
// format);
return 0;
}
uintptr_t GetTextureEntry(const TextureInfo& texture_info,
const SamplerInfo& sampler_info) override {
auto command_processor = static_cast<VulkanCommandProcessor*>(
graphics_system_->command_processor());
// auto entry_view =
// command_processor->texture_cache()->Demand(texture_info,
// sampler_info);
// if (!entry_view) {
// return 0;
//}
// auto texture = entry_view->texture;
// return static_cast<uintptr_t>(texture->handle);
return 0;
}
};
int trace_viewer_main(const std::vector<std::wstring>& args) {
VulkanTraceViewer trace_viewer;
return trace_viewer.Main(args);
}
} // namespace vulkan
} // namespace gpu
} // namespace xe
DEFINE_ENTRY_POINT(L"xenia-gpu-vulkan-trace-viewer",
L"xenia-gpu-vulkan-trace-viewer some.trace",
xe::gpu::vulkan::trace_viewer_main);

View File

@ -49,6 +49,7 @@ enum class PrimitiveType : uint32_t {
kLineLoop = 0x0C, kLineLoop = 0x0C,
kQuadList = 0x0D, kQuadList = 0x0D,
kQuadStrip = 0x0E, kQuadStrip = 0x0E,
kUnknown0x11 = 0x11,
}; };
enum class Dimension : uint32_t { enum class Dimension : uint32_t {
@ -382,7 +383,7 @@ XEPACKEDUNION(xe_gpu_vertex_fetch_t, {
uint32_t type : 2; uint32_t type : 2;
uint32_t address : 30; uint32_t address : 30;
uint32_t endian : 2; uint32_t endian : 2;
uint32_t size : 24; uint32_t size : 24; // size in words
uint32_t unk1 : 6; uint32_t unk1 : 6;
}); });
XEPACKEDSTRUCTANONYMOUS({ XEPACKEDSTRUCTANONYMOUS({
@ -486,6 +487,46 @@ XEPACKEDUNION(xe_gpu_fetch_group_t, {
}); });
}); });
enum Event {
SAMPLE_STREAMOUTSTATS1 = (1 << 0),
SAMPLE_STREAMOUTSTATS2 = (2 << 0),
SAMPLE_STREAMOUTSTATS3 = (3 << 0),
CACHE_FLUSH_TS = (4 << 0),
CACHE_FLUSH = (6 << 0),
CS_PARTIAL_FLUSH = (7 << 0),
VGT_STREAMOUT_RESET = (10 << 0),
END_OF_PIPE_INCR_DE = (11 << 0),
END_OF_PIPE_IB_END = (12 << 0),
RST_PIX_CNT = (13 << 0),
VS_PARTIAL_FLUSH = (15 << 0),
PS_PARTIAL_FLUSH = (16 << 0),
CACHE_FLUSH_AND_INV_TS_EVENT = (20 << 0),
ZPASS_DONE = (21 << 0),
CACHE_FLUSH_AND_INV_EVENT = (22 << 0),
PERFCOUNTER_START = (23 << 0),
PERFCOUNTER_STOP = (24 << 0),
PIPELINESTAT_START = (25 << 0),
PIPELINESTAT_STOP = (26 << 0),
PERFCOUNTER_SAMPLE = (27 << 0),
SAMPLE_PIPELINESTAT = (30 << 0),
SAMPLE_STREAMOUTSTATS = (32 << 0),
RESET_VTX_CNT = (33 << 0),
VGT_FLUSH = (36 << 0),
BOTTOM_OF_PIPE_TS = (40 << 0),
DB_CACHE_FLUSH_AND_INV = (42 << 0),
FLUSH_AND_INV_DB_DATA_TS = (43 << 0),
FLUSH_AND_INV_DB_META = (44 << 0),
FLUSH_AND_INV_CB_DATA_TS = (45 << 0),
FLUSH_AND_INV_CB_META = (46 << 0),
CS_DONE = (47 << 0),
PS_DONE = (48 << 0),
FLUSH_AND_INV_CB_PIXEL_DATA = (49 << 0),
THREAD_TRACE_START = (51 << 0),
THREAD_TRACE_STOP = (52 << 0),
THREAD_TRACE_FLUSH = (54 << 0),
THREAD_TRACE_FINISH = (55 << 0),
};
// Opcodes (IT_OPCODE) for Type-3 commands in the ringbuffer. // Opcodes (IT_OPCODE) for Type-3 commands in the ringbuffer.
// https://github.com/freedreno/amd-gpu/blob/master/include/api/gsl_pm4types.h // https://github.com/freedreno/amd-gpu/blob/master/include/api/gsl_pm4types.h
// Not sure if all of these are used. // Not sure if all of these are used.
@ -501,7 +542,7 @@ enum Type3Opcode {
PM4_WAIT_FOR_IDLE = 0x26, // wait for the IDLE state of the engine PM4_WAIT_FOR_IDLE = 0x26, // wait for the IDLE state of the engine
PM4_WAIT_REG_MEM = 0x3c, // wait until a register or memory location is a specific value PM4_WAIT_REG_MEM = 0x3c, // wait until a register or memory location is a specific value
PM4_WAIT_REG_EQ = 0x52, // wait until a register location is equal to a specific value PM4_WAIT_REG_EQ = 0x52, // wait until a register location is equal to a specific value
PM4_WAT_REG_GTE = 0x53, // wait until a register location is >= a specific value PM4_WAIT_REG_GTE = 0x53, // wait until a register location is >= a specific value
PM4_WAIT_UNTIL_READ = 0x5c, // wait until a read completes PM4_WAIT_UNTIL_READ = 0x5c, // wait until a read completes
PM4_WAIT_IB_PFD_COMPLETE = 0x5d, // wait until all base/size writes from an IB_PFD packet have completed PM4_WAIT_IB_PFD_COMPLETE = 0x5d, // wait until all base/size writes from an IB_PFD packet have completed

View File

@ -343,8 +343,7 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer
lpunknown_t unk8, unknown_t unk9) { lpunknown_t unk8, unknown_t unk9) {
gpu::xenos::xe_gpu_texture_fetch_t fetch; gpu::xenos::xe_gpu_texture_fetch_t fetch;
xe::copy_and_swap_32_unaligned( xe::copy_and_swap_32_unaligned(
reinterpret_cast<uint32_t*>(&fetch), &fetch, reinterpret_cast<uint32_t*>(fetch_ptr.host_address()), 6);
reinterpret_cast<uint32_t*>(fetch_ptr.host_address()), 6);
auto color_format = gpu::ColorFormat(color_format_ptr.value()); auto color_format = gpu::ColorFormat(color_format_ptr.value());
auto color_space = *color_space_ptr; auto color_space = *color_space_ptr;
@ -367,7 +366,7 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer
auto dwords = buffer_ptr.as_array<uint32_t>(); auto dwords = buffer_ptr.as_array<uint32_t>();
dwords[0] = xenos::MakePacketType3<xenos::PM4_XE_SWAP, 63>(); dwords[0] = xenos::MakePacketType3<xenos::PM4_XE_SWAP, 63>();
dwords[1] = 'SWAP'; dwords[1] = 'SWAP';
dwords[2] = *frontbuffer_ptr; dwords[2] = (*frontbuffer_ptr) & 0x1FFFFFFF;
// Set by VdCallGraphicsNotificationRoutines. // Set by VdCallGraphicsNotificationRoutines.
dwords[3] = last_frontbuffer_width_; dwords[3] = last_frontbuffer_width_;

View File

@ -376,17 +376,19 @@ cpu::MMIORange* Memory::LookupVirtualMappedRange(uint32_t virtual_address) {
return mmio_handler_->LookupRange(virtual_address); return mmio_handler_->LookupRange(virtual_address);
} }
uintptr_t Memory::AddPhysicalWriteWatch(uint32_t physical_address, uintptr_t Memory::AddPhysicalAccessWatch(uint32_t physical_address,
uint32_t length, uint32_t length,
cpu::WriteWatchCallback callback, cpu::MMIOHandler::WatchType type,
cpu::AccessWatchCallback callback,
void* callback_context, void* callback_context,
void* callback_data) { void* callback_data) {
return mmio_handler_->AddPhysicalWriteWatch( return mmio_handler_->AddPhysicalAccessWatch(physical_address, length, type,
physical_address, length, callback, callback_context, callback_data); callback, callback_context,
callback_data);
} }
void Memory::CancelWriteWatch(uintptr_t watch_handle) { void Memory::CancelAccessWatch(uintptr_t watch_handle) {
mmio_handler_->CancelWriteWatch(watch_handle); mmio_handler_->CancelAccessWatch(watch_handle);
} }
uint32_t Memory::SystemHeapAlloc(uint32_t size, uint32_t alignment, uint32_t Memory::SystemHeapAlloc(uint32_t size, uint32_t alignment,
@ -453,6 +455,7 @@ bool Memory::Save(ByteStream* stream) {
} }
bool Memory::Restore(ByteStream* stream) { bool Memory::Restore(ByteStream* stream) {
XELOGD("Restoring memory...");
heaps_.v00000000.Restore(stream); heaps_.v00000000.Restore(stream);
heaps_.v40000000.Restore(stream); heaps_.v40000000.Restore(stream);
heaps_.v80000000.Restore(stream); heaps_.v80000000.Restore(stream);
@ -577,6 +580,8 @@ bool BaseHeap::Save(ByteStream* stream) {
} }
bool BaseHeap::Restore(ByteStream* stream) { bool BaseHeap::Restore(ByteStream* stream) {
XELOGD("Heap %.8X-%.8X", heap_base_, heap_base_ + heap_size_);
for (size_t i = 0; i < page_table_.size(); i++) { for (size_t i = 0; i < page_table_.size(); i++) {
auto& page = page_table_[i]; auto& page = page_table_[i];
page.qword = stream->Read<uint64_t>(); page.qword = stream->Read<uint64_t>();
@ -897,7 +902,7 @@ bool BaseHeap::Release(uint32_t base_address, uint32_t* out_region_size) {
auto base_page_entry = page_table_[base_page_number]; auto base_page_entry = page_table_[base_page_number];
if (base_page_entry.base_address != base_page_number) { if (base_page_entry.base_address != base_page_number) {
XELOGE("BaseHeap::Release failed because address is not a region start"); XELOGE("BaseHeap::Release failed because address is not a region start");
// return false; return false;
} }
if (out_region_size) { if (out_region_size) {

View File

@ -303,12 +303,13 @@ class Memory {
// //
// This has a significant performance penalty for writes in in the range or // This has a significant performance penalty for writes in in the range or
// nearby (sharing 64KiB pages). // nearby (sharing 64KiB pages).
uintptr_t AddPhysicalWriteWatch(uint32_t physical_address, uint32_t length, uintptr_t AddPhysicalAccessWatch(uint32_t physical_address, uint32_t length,
cpu::WriteWatchCallback callback, cpu::MMIOHandler::WatchType type,
cpu::AccessWatchCallback callback,
void* callback_context, void* callback_data); void* callback_context, void* callback_data);
// Cancels a write watch requested with AddPhysicalWriteWatch. // Cancels a write watch requested with AddPhysicalAccessWatch.
void CancelWriteWatch(uintptr_t watch_handle); void CancelAccessWatch(uintptr_t watch_handle);
// Allocates virtual memory from the 'system' heap. // Allocates virtual memory from the 'system' heap.
// System memory is kept separate from game memory but is still accessible // System memory is kept separate from game memory but is still accessible

View File

@ -208,7 +208,7 @@ void ImGuiDrawer::RenderDrawLists(ImDrawData* data) {
draw.count = cmd.ElemCount; draw.count = cmd.ElemCount;
draw.index_offset = index_offset; draw.index_offset = index_offset;
draw.texture_handle = draw.texture_handle =
reinterpret_cast<uintptr_t>(cmd.TextureId) & 0xFFFFFFFF; reinterpret_cast<uintptr_t>(cmd.TextureId) & ~kIgnoreAlpha;
draw.alpha_blend = draw.alpha_blend =
reinterpret_cast<uintptr_t>(cmd.TextureId) & kIgnoreAlpha ? false reinterpret_cast<uintptr_t>(cmd.TextureId) & kIgnoreAlpha ? false
: true; : true;

View File

@ -35,7 +35,7 @@ class ImGuiDrawer : public WindowListener {
ImGuiIO& GetIO(); ImGuiIO& GetIO();
static const uint64_t kIgnoreAlpha = (1ull << 32); static const uint64_t kIgnoreAlpha = (1ull << 63);
protected: protected:
void Initialize(); void Initialize();

View File

@ -7,6 +7,7 @@ project("xenia-ui-spirv")
kind("StaticLib") kind("StaticLib")
language("C++") language("C++")
links({ links({
"glslang-spirv",
"spirv-tools", "spirv-tools",
"xenia-base", "xenia-base",
}) })

View File

@ -9,7 +9,7 @@
#include "xenia/ui/spirv/spirv_assembler.h" #include "xenia/ui/spirv/spirv_assembler.h"
#include "third_party/spirv-tools/include/libspirv/libspirv.h" #include "third_party/spirv-tools/include/spirv-tools/libspirv.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
namespace xe { namespace xe {

View File

@ -9,7 +9,7 @@
#include "xenia/ui/spirv/spirv_disassembler.h" #include "xenia/ui/spirv/spirv_disassembler.h"
#include "third_party/spirv-tools/include/libspirv/libspirv.h" #include "third_party/spirv-tools/include/spirv-tools/libspirv.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
namespace xe { namespace xe {

File diff suppressed because it is too large Load Diff

View File

@ -1,731 +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. *
******************************************************************************
*/
// Contents originally forked from:
// https://github.com/KhronosGroup/glslang/
//
// Copyright (C) 2014 LunarG, Inc.
//
// 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 3Dlabs Inc. Ltd. 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 AND CONTRIBUTORS
// "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 HOLDERS 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.
#ifndef XENIA_UI_SPIRV_SPIRV_EMITTER_H_
#define XENIA_UI_SPIRV_SPIRV_EMITTER_H_
#include <algorithm>
#include <map>
#include <stack>
#include <vector>
#include "xenia/base/assert.h"
#include "xenia/ui/spirv/spirv_ir.h"
#include "xenia/ui/spirv/spirv_util.h"
namespace xe {
namespace ui {
namespace spirv {
class SpirvEmitter {
public:
SpirvEmitter();
~SpirvEmitter();
// Document what source language and text this module was translated from.
void SetSourceLanguage(spv::SourceLanguage language, int version) {
source_language_ = language;
source_version_ = version;
}
// Document an extension to the source language. Informational only.
void AddSourceExtension(const char* ext) {
source_extensions_.push_back(ext);
}
// Set addressing model and memory model for the entire module.
void SetMemoryModel(spv::AddressingModel addressing_model,
spv::MemoryModel memory_model) {
addressing_model_ = addressing_model;
memory_model_ = memory_model;
}
// Declare a capability used by this module.
void DeclareCapability(spv::Capability cap) { capabilities_.push_back(cap); }
// Import an extended set of instructions that can be later referenced by the
// returned id.
Id ImportExtendedInstructions(const char* name);
// For creating new types (will return old type if the requested one was
// already made).
Id MakeVoidType();
Id MakeBoolType();
Id MakePointer(spv::StorageClass storage_class, Id pointee);
Id MakeIntegerType(int bit_width, bool is_signed);
Id MakeIntType(int bit_width) { return MakeIntegerType(bit_width, true); }
Id MakeUintType(int bit_width) { return MakeIntegerType(bit_width, false); }
Id MakeFloatType(int bit_width);
Id MakeStructType(std::initializer_list<Id> members, const char* name);
Id MakePairStructType(Id type0, Id type1);
Id MakeVectorType(Id component_type, int component_count);
Id MakeMatrix2DType(Id component_type, int cols, int rows);
Id MakeArrayType(Id element_type, int length);
Id MakeRuntimeArray(Id element_type);
Id MakeFunctionType(Id return_type, std::initializer_list<Id> param_types);
Id MakeImageType(Id sampled_type, spv::Dim dim, bool has_depth,
bool is_arrayed, bool is_multisampled, int sampled,
spv::ImageFormat format);
Id MakeSamplerType();
Id MakeSampledImageType(Id image_type);
// For querying about types.
Id GetTypeId(Id result_id) const { return module_.type_id(result_id); }
Id GetDerefTypeId(Id result_id) const;
Op GetOpcode(Id id) const { return module_.instruction(id)->opcode(); }
Op GetTypeClass(Id type_id) const { return GetOpcode(type_id); }
Op GetMostBasicTypeClass(Id type_id) const;
int GetComponentCount(Id result_id) const {
return GetTypeComponentCount(GetTypeId(result_id));
}
int GetTypeComponentCount(Id type_id) const;
Id GetScalarTypeId(Id type_id) const;
Id GetContainedTypeId(Id type_id) const;
Id GetContainedTypeId(Id type_id, int member) const;
spv::StorageClass GetTypeStorageClass(Id type_id) const {
return module_.storage_class(type_id);
}
bool IsPointer(Id result_id) const {
return IsPointerType(GetTypeId(result_id));
}
bool IsScalar(Id result_id) const {
return IsScalarType(GetTypeId(result_id));
}
bool IsVector(Id result_id) const {
return IsVectorType(GetTypeId(result_id));
}
bool IsMatrix(Id result_id) const {
return IsMatrixType(GetTypeId(result_id));
}
bool IsAggregate(Id result_id) const {
return IsAggregateType(GetTypeId(result_id));
}
bool IsBoolType(Id type_id) const {
return grouped_types_[static_cast<int>(spv::Op::OpTypeBool)].size() > 0 &&
type_id ==
grouped_types_[static_cast<int>(spv::Op::OpTypeBool)]
.back()
->result_id();
}
bool IsPointerType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypePointer;
}
bool IsScalarType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypeFloat ||
GetTypeClass(type_id) == spv::Op::OpTypeInt ||
GetTypeClass(type_id) == spv::Op::OpTypeBool;
}
bool IsVectorType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypeVector;
}
bool IsMatrixType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypeMatrix;
}
bool IsStructType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypeStruct;
}
bool IsArrayType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypeArray;
}
bool IsAggregateType(Id type_id) const {
return IsArrayType(type_id) || IsStructType(type_id);
}
bool IsImageType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypeImage;
}
bool IsSamplerType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypeSampler;
}
bool IsSampledImageType(Id type_id) const {
return GetTypeClass(type_id) == spv::Op::OpTypeSampledImage;
}
bool IsConstantOpCode(Op opcode) const;
bool IsConstant(Id result_id) const {
return IsConstantOpCode(GetOpcode(result_id));
}
bool IsConstantScalar(Id result_id) const {
return GetOpcode(result_id) == spv::Op::OpConstant;
}
uint32_t GetConstantScalar(Id result_id) const {
return module_.instruction(result_id)->immediate_operand(0);
}
spv::StorageClass GetStorageClass(Id result_id) const {
return GetTypeStorageClass(GetTypeId(result_id));
}
int GetTypeColumnCount(Id type_id) const {
assert(IsMatrixType(type_id));
return GetTypeComponentCount(type_id);
}
int GetColumnCount(Id result_id) const {
return GetTypeColumnCount(GetTypeId(result_id));
}
int GetTypeRowCount(Id type_id) const {
assert(IsMatrixType(type_id));
return GetTypeComponentCount(GetContainedTypeId(type_id));
}
int GetRowCount(Id result_id) const {
return GetTypeRowCount(GetTypeId(result_id));
}
spv::Dim GetTypeDimensionality(Id type_id) const {
assert(IsImageType(type_id));
return static_cast<spv::Dim>(
module_.instruction(type_id)->immediate_operand(1));
}
Id GetImageType(Id result_id) const {
Id type_id = GetTypeId(result_id);
assert(IsImageType(type_id) || IsSampledImageType(type_id));
return IsSampledImageType(type_id)
? module_.instruction(type_id)->id_operand(0)
: type_id;
}
bool IsArrayedImageType(Id type_id) const {
assert(IsImageType(type_id));
return module_.instruction(type_id)->immediate_operand(3) != 0;
}
// For making new constants (will return old constant if the requested one was
// already made).
Id MakeBoolConstant(bool value, bool is_spec_constant = false);
Id MakeIntConstant(int value, bool is_spec_constant = false) {
return MakeIntegerConstant(MakeIntType(32), static_cast<uint32_t>(value),
is_spec_constant);
}
Id MakeUintConstant(uint32_t value, bool is_spec_constant = false) {
return MakeIntegerConstant(MakeUintType(32), value, is_spec_constant);
}
template <typename T>
Id MakeUintConstant(T value, bool is_spec_constant = false) {
static_assert(sizeof(T) == sizeof(uint32_t), "Invalid type");
return MakeIntegerConstant(MakeUintType(32), static_cast<uint32_t>(value),
is_spec_constant);
}
Id MakeFloatConstant(float value, bool is_spec_constant = false);
Id MakeDoubleConstant(double value, bool is_spec_constant = false);
// Turns the array of constants into a proper constant of the requested type.
Id MakeCompositeConstant(Id type, std::initializer_list<Id> components);
// Declares an entry point and its execution model.
Instruction* AddEntryPoint(spv::ExecutionModel execution_model,
Function* entry_point, const char* name);
void AddExecutionMode(Function* entry_point,
spv::ExecutionMode execution_mode, int value1 = -1,
int value2 = -1, int value3 = -1);
void AddName(Id target_id, const char* name);
void AddMemberName(Id target_id, int member, const char* name);
void AddLine(Id target_id, Id file_name, int line_number, int column_number);
void AddDecoration(Id target_id, spv::Decoration decoration, int num = -1);
void AddMemberDecoration(Id target_id, int member, spv::Decoration,
int num = -1);
// At the end of what block do the next create*() instructions go?
Block* build_point() const { return build_point_; }
void set_build_point(Block* build_point) { build_point_ = build_point; }
// Makes the main function.
Function* MakeMainEntry();
// Makes a shader-style function, and create its entry block if entry is
// non-zero.
// Return the function, pass back the entry.
Function* MakeFunctionEntry(Id return_type, const char* name,
std::initializer_list<Id> param_types,
Block** entry = 0);
// Creates a return statement.
// An 'implicit' return is one not appearing in the source code. In the case
// of an implicit return, no post-return block is inserted.
void MakeReturn(bool implicit, Id return_value = 0);
// Generates all the code needed to finish up a function.
void LeaveFunction();
// Creates a fragment-shader discard (kill).
void MakeDiscard();
// Creates a global or function local or IO variable.
Id CreateVariable(spv::StorageClass storage_class, Id type,
const char* name = 0);
// Creates an intermediate object whose value is undefined.
Id CreateUndefined(Id type);
// Stores the given value into the specified pointer.
void CreateStore(Id pointer_id, Id value_id);
// Loads the value from the given pointer.
Id CreateLoad(Id pointer_id);
// Creates a pointer into a composite object that can be used with OpLoad and
// OpStore.
Id CreateAccessChain(spv::StorageClass storage_class, Id base_id,
std::vector<Id> index_ids);
// Queries the length of a run-time array.
Id CreateArrayLength(Id struct_id, int array_member);
Id CreateCompositeExtract(Id composite, Id type_id, uint32_t index);
Id CreateCompositeExtract(Id composite, Id type_id,
std::vector<uint32_t> indexes);
Id CreateCompositeInsert(Id object, Id composite, Id type_id, uint32_t index);
Id CreateCompositeInsert(Id object, Id composite, Id type_id,
std::vector<uint32_t> indexes);
Id CreateVectorExtractDynamic(Id vector, Id type_id, Id component_index);
Id CreateVectorInsertDynamic(Id vector, Id type_id, Id component,
Id component_index);
// Does nothing.
void CreateNop();
// Waits for other invocations of this module to reach the current point of
// execution.
void CreateControlBarrier(spv::Scope execution_scope, spv::Scope memory_scope,
spv::MemorySemanticsMask memory_semantics);
// Controls the order that memory accesses are observed.
void CreateMemoryBarrier(spv::Scope execution_scope,
spv::MemorySemanticsMask memory_semantics);
Id CreateUnaryOp(Op opcode, Id type_id, Id operand);
Id CreateBinOp(Op opcode, Id type_id, Id operand1, Id operand2);
Id CreateTriOp(Op opcode, Id type_id, Id operand1, Id operand2, Id operand3);
Id CreateOp(Op opcode, Id type_id, const std::vector<Id>& operands);
Id CreateFunctionCall(Function* function, std::vector<spv::Id> args);
// Takes an rvalue (source) and a set of channels to extract from it to
// make a new rvalue.
Id CreateSwizzle(Id type_id, Id source, std::vector<uint32_t> channels);
// Takes a copy of an lvalue (target) and a source of components, and sets the
// source components into the lvalue where the 'channels' say to put them.
Id CreateLvalueSwizzle(Id type_id, Id target, Id source,
std::vector<uint32_t> channels);
// If the value passed in is an instruction and the precision is not EMpNone,
// it gets tagged with the requested precision.
void SetPrecision(Id value, spv::Decoration precision) {
CheckNotImplemented("setPrecision");
}
// Smears a scalar to a vector for the following forms:
// - PromoteScalar(scalar, vector) // smear scalar to width of vector
// - PromoteScalar(vector, scalar) // smear scalar to width of vector
// - PromoteScalar(pointer, scalar) // smear scalar to width of what pointer
// points to
// - PromoteScalar(scalar, scalar) // do nothing
// Other forms are not allowed.
//
// Note: One of the arguments will change, with the result coming back that
// way rather than through the return value.
void PromoteScalar(spv::Decoration precision, Id& left, Id& right);
// Makes a value by smearing the scalar to fill the type.
Id SmearScalar(spv::Decoration precision, Id scalar_value, Id vector_type_id);
// Executes an instruction in an imported set of extended instructions.
Id CreateExtendedInstructionCall(spv::Decoration precision, Id result_type,
Id instruction_set, int instruction_ordinal,
std::initializer_list<Id> args);
// Executes an instruction from the extended GLSL set.
Id CreateGlslStd450InstructionCall(spv::Decoration precision, Id result_type,
spv::GLSLstd450 instruction_ordinal,
std::initializer_list<Id> args);
// List of parameters used to create a texture operation
struct TextureParameters {
Id sampler;
Id coords;
Id bias;
Id lod;
Id depth_ref;
Id offset;
Id offsets;
Id grad_x;
Id grad_y;
Id sample;
Id comp;
};
// Selects the correct texture operation based on all inputs, and emit the
// correct instruction.
Id CreateTextureCall(spv::Decoration precision, Id result_type, bool fetch,
bool proj, bool gather,
const TextureParameters& parameters);
// Emits the OpTextureQuery* instruction that was passed in and figures out
// the right return value and type.
Id CreateTextureQueryCall(Op opcode, const TextureParameters& parameters);
Id CreateSamplePositionCall(spv::Decoration precision, Id, Id);
Id CreateBitFieldExtractCall(spv::Decoration precision, Id, Id, Id,
bool isSigned);
Id CreateBitFieldInsertCall(spv::Decoration precision, Id, Id, Id, Id);
// Reduction comparision for composites: For equal and not-equal resulting in
// a scalar.
Id CreateCompare(spv::Decoration precision, Id value1, Id value2,
bool is_equal);
// OpCompositeConstruct
Id CreateCompositeConstruct(Id type_id, std::vector<Id> constituent_ids);
// vector or scalar constructor
Id CreateConstructor(spv::Decoration precision, std::vector<Id> source_ids,
Id result_type_id);
// matrix constructor
Id CreateMatrixConstructor(spv::Decoration precision, std::vector<Id> sources,
Id constructee);
// Helper to use for building nested control flow with if-then-else.
class If {
public:
If(SpirvEmitter& emitter, Id condition);
~If() = default;
void MakeBeginElse();
void MakeEndIf();
private:
If(const If&) = delete;
If& operator=(If&) = delete;
SpirvEmitter& emitter_;
Id condition_;
Function* function_ = nullptr;
Block* header_block_ = nullptr;
Block* then_block_ = nullptr;
Block* else_block_ = nullptr;
Block* merge_block_ = nullptr;
};
// Makes a switch statement.
// A switch has 'numSegments' of pieces of code, not containing any
// case/default labels, all separated by one or more case/default labels.
// Each possible case value v is a jump to the caseValues[v] segment. The
// defaultSegment is also in this number space. How to compute the value is
// given by 'condition', as in switch(condition).
//
// The SPIR-V Builder will maintain the stack of post-switch merge blocks for
// nested switches.
//
// Use a defaultSegment < 0 if there is no default segment (to branch to post
// switch).
//
// Returns the right set of basic blocks to start each code segment with, so
// that the caller's recursion stack can hold the memory for it.
void MakeSwitch(Id condition, int segment_count, std::vector<int> case_values,
std::vector<int> value_index_to_segment, int default_segment,
std::vector<Block*>& segment_blocks);
// Adds a branch to the innermost switch's merge block.
void AddSwitchBreak();
// Move sto the next code segment, passing in the return argument in
// MakeSwitch().
void NextSwitchSegment(std::vector<Block*>& segment_block, int next_segment);
// Finishes off the innermost switch.
void EndSwitch(std::vector<Block*>& segment_block);
// Starts the beginning of a new loop, and prepare the builder to
// generate code for the loop test.
// The test_first parameter is true when the loop test executes before
// the body (it is false for do-while loops).
void MakeNewLoop(bool test_first);
// Adds the branch for the loop test, based on the given condition.
// The true branch goes to the first block in the loop body, and
// the false branch goes to the loop's merge block. The builder insertion
// point will be placed at the start of the body.
void CreateLoopTestBranch(Id condition);
// Generates an unconditional branch to the loop body.
// The builder insertion point will be placed at the start of the body.
// Use this when there is no loop test.
void CreateBranchToBody();
// Adds a branch to the test of the current (innermost) loop.
// The way we generate code, that's also the loop header.
void CreateLoopContinue();
// Adds an exit (e.g. "break") for the innermost loop that you're in.
void CreateLoopExit();
// Close the innermost loop that you're in.
void CloseLoop();
// Access chain design for an R-Value vs. L-Value:
//
// There is a single access chain the builder is building at
// any particular time. Such a chain can be used to either to a load or
// a store, when desired.
//
// Expressions can be r-values, l-values, or both, or only r-values:
// a[b.c].d = .... // l-value
// ... = a[b.c].d; // r-value, that also looks like an l-value
// ++a[b.c].d; // r-value and l-value
// (x + y)[2]; // r-value only, can't possibly be l-value
//
// Computing an r-value means generating code. Hence,
// r-values should only be computed when they are needed, not speculatively.
//
// Computing an l-value means saving away information for later use in the
// compiler,
// no code is generated until the l-value is later dereferenced. It is okay
// to speculatively generate an l-value, just not okay to speculatively
// dereference it.
//
// The base of the access chain (the left-most variable or expression
// from which everything is based) can be set either as an l-value
// or as an r-value. Most efficient would be to set an l-value if one
// is available. If an expression was evaluated, the resulting r-value
// can be set as the chain base.
//
// The users of this single access chain can save and restore if they
// want to nest or manage multiple chains.
//
struct AccessChain {
Id base; // for l-values, pointer to the base object, for r-values, the
// base object
std::vector<Id> index_chain;
Id instr; // cache the instruction that generates this access chain
std::vector<uint32_t> swizzle; // each std::vector element selects the next
// GLSL component number
Id component; // a dynamic component index, can coexist with a swizzle,
// done after the swizzle, NoResult if not present
Id pre_swizzle_base_type; // dereferenced type, before swizzle or component
// is
// applied; NoType unless a swizzle or component is
// present
bool is_rvalue; // true if 'base' is an r-value, otherwise, base is an
// l-value
};
//
// the SPIR-V builder maintains a single active chain that
// the following methods operated on
//
// for external save and restore
AccessChain access_chain() { return access_chain_; }
void set_access_chain(AccessChain new_chain) { access_chain_ = new_chain; }
void ClearAccessChain();
// set new base as an l-value base
void set_access_chain_lvalue(Id lvalue) {
assert(IsPointer(lvalue));
access_chain_.base = lvalue;
}
// set new base value as an r-value
void set_access_chain_rvalue(Id rvalue) {
access_chain_.is_rvalue = true;
access_chain_.base = rvalue;
}
// push offset onto the end of the chain
void PushAccessChainOffset(Id offset) {
access_chain_.index_chain.push_back(offset);
}
// push new swizzle onto the end of any existing swizzle, merging into a
// single swizzle
void PushAccessChainSwizzle(std::vector<uint32_t> swizzle,
Id pre_swizzle_base_type);
// push a variable component selection onto the access chain; supporting only
// one, so unsided
void PushAccessChainComponent(Id component, Id pre_swizzle_base_type) {
access_chain_.component = component;
if (access_chain_.pre_swizzle_base_type == NoType) {
access_chain_.pre_swizzle_base_type = pre_swizzle_base_type;
}
}
// use accessChain and swizzle to store value
void CreateAccessChainStore(Id rvalue);
// use accessChain and swizzle to load an r-value
Id CreateAccessChainLoad(Id result_type_id);
// get the direct pointer for an l-value
Id CreateAccessChainLValue();
void Serialize(std::vector<uint32_t>& out) const;
private:
// Maximum dimension for column/row in a matrix.
static const int kMaxMatrixSize = 4;
// Allocates a new <id>.
Id AllocateUniqueId() { return ++unique_id_; }
// Allocates a contiguous sequence of <id>s.
Id AllocateUniqueIds(int count) {
Id id = unique_id_ + 1;
unique_id_ += count;
return id;
}
Id MakeIntegerConstant(Id type_id, uint32_t value, bool is_spec_constant);
Id FindScalarConstant(Op type_class, Op opcode, Id type_id,
uint32_t value) const;
Id FindScalarConstant(Op type_class, Op opcode, Id type_id, uint32_t v1,
uint32_t v2) const;
Id FindCompositeConstant(Op type_class,
std::initializer_list<Id> components) const;
Id CollapseAccessChain();
void SimplifyAccessChainSwizzle();
void TransferAccessChainSwizzle(bool dynamic);
void SerializeInstructions(
std::vector<uint32_t>& out,
const std::vector<Instruction*>& instructions) const;
void CreateAndSetNoPredecessorBlock(const char* name);
void CreateBranch(Block* block);
void CreateSelectionMerge(Block* merge_block,
spv::SelectionControlMask control);
void CreateLoopMerge(Block* merge_block, Block* continueBlock,
spv::LoopControlMask control);
void CreateConditionalBranch(Id condition, Block* then_block,
Block* else_block);
struct Loop; // Defined below.
void CreateBranchToLoopHeaderFromInside(const Loop& loop);
// Asserts on unimplemented functionality.
void CheckNotImplemented(const char* message);
spv::SourceLanguage source_language_ = spv::SourceLanguage::Unknown;
int source_version_ = 0;
std::vector<const char*> source_extensions_;
spv::AddressingModel addressing_model_ = spv::AddressingModel::Logical;
spv::MemoryModel memory_model_ = spv::MemoryModel::GLSL450;
std::vector<spv::Capability> capabilities_;
int builder_number_ = 0;
Module module_;
Block* build_point_ = nullptr;
Id unique_id_ = 0;
Function* main_function_ = nullptr;
AccessChain access_chain_;
Id glsl_std_450_instruction_set_ = 0;
// special blocks of instructions for output
std::vector<Instruction*> imports_;
std::vector<Instruction*> entry_points_;
std::vector<Instruction*> execution_modes_;
std::vector<Instruction*> names_;
std::vector<Instruction*> lines_;
std::vector<Instruction*> decorations_;
std::vector<Instruction*> constants_types_globals_;
std::vector<Instruction*> externals_;
// not output, internally used for quick & dirty canonical (unique) creation
// All types appear before OpConstant.
std::vector<Instruction*>
grouped_constants_[static_cast<int>(spv::Op::OpConstant)];
std::vector<Instruction*>
grouped_types_[static_cast<int>(spv::Op::OpConstant)];
// Stack of switches.
std::stack<Block*> switch_merges_;
// Data that needs to be kept in order to properly handle loops.
struct Loop {
// Constructs a default Loop structure containing new header, merge, and
// body blocks for the current function.
// The test_first argument indicates whether the loop test executes at
// the top of the loop rather than at the bottom. In the latter case,
// also create a phi instruction whose value indicates whether we're on
// the first iteration of the loop. The phi instruction is initialized
// with no values or predecessor operands.
Loop(SpirvEmitter& emitter, bool test_first);
// The function containing the loop.
Function* const function;
// The header is the first block generated for the loop.
// It dominates all the blocks in the loop, i.e. it is always
// executed before any others.
// If the loop test is executed before the body (as in "while" and
// "for" loops), then the header begins with the test code.
// Otherwise, the loop is a "do-while" loop and the header contains the
// start of the body of the loop (if the body exists).
Block* const header;
// The merge block marks the end of the loop. Control is transferred
// to the merge block when either the loop test fails, or when a
// nested "break" is encountered.
Block* const merge;
// The body block is the first basic block in the body of the loop, i.e.
// the code that is to be repeatedly executed, aside from loop control.
// This member is null until we generate code that references the loop
// body block.
Block* const body;
// True when the loop test executes before the body.
const bool test_first;
// When the test executes after the body, this is defined as the phi
// instruction that tells us whether we are on the first iteration of
// the loop. Otherwise this is null. This is non-const because
// it has to be initialized outside of the initializer-list.
Instruction* is_first_iteration;
};
// Our loop stack.
std::stack<Loop> loops_;
};
} // namespace spirv
} // namespace ui
} // namespace xe
#endif // XENIA_UI_SPIRV_SPIRV_EMITTER_H_

View File

@ -1,421 +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. *
******************************************************************************
*/
// Contents originally forked from:
// https://github.com/KhronosGroup/glslang/
//
// Copyright (C) 2014 LunarG, Inc.
//
// 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 3Dlabs Inc. Ltd. 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 AND CONTRIBUTORS
// "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 HOLDERS 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.
// SPIRV-IR
//
// Simple in-memory representation (IR) of SPIRV. Just for holding
// Each function's CFG of blocks. Has this hierarchy:
// - Module, which is a list of
// - Function, which is a list of
// - Block, which is a list of
// - Instruction
//
#ifndef XENIA_UI_SPIRV_SPIRV_IR_H_
#define XENIA_UI_SPIRV_SPIRV_IR_H_
#include <iostream>
#include <vector>
#include "xenia/ui/spirv/spirv_util.h"
namespace xe {
namespace ui {
namespace spirv {
using spv::Id;
using spv::Op;
class Function;
class Module;
const Id NoResult = 0;
const Id NoType = 0;
const uint32_t BadValue = 0xFFFFFFFF;
const spv::Decoration NoPrecision = static_cast<spv::Decoration>(BadValue);
const spv::MemorySemanticsMask MemorySemanticsAllMemory =
static_cast<spv::MemorySemanticsMask>(0x3FF);
class Instruction {
public:
Instruction(Id result_id, Id type_id, Op opcode)
: result_id_(result_id), type_id_(type_id), opcode_(opcode) {}
explicit Instruction(Op opcode) : opcode_(opcode) {}
~Instruction() = default;
void AddIdOperand(Id id) { operands_.push_back(id); }
void AddIdOperands(const std::vector<Id>& ids) {
for (auto id : ids) {
operands_.push_back(id);
}
}
void AddIdOperands(std::initializer_list<Id> ids) {
for (auto id : ids) {
operands_.push_back(id);
}
}
void AddImmediateOperand(uint32_t immediate) {
operands_.push_back(immediate);
}
template <typename T>
void AddImmediateOperand(T immediate) {
static_assert(sizeof(T) == sizeof(uint32_t), "Invalid operand size");
operands_.push_back(static_cast<uint32_t>(immediate));
}
void AddImmediateOperands(const std::vector<uint32_t>& immediates) {
for (auto immediate : immediates) {
operands_.push_back(immediate);
}
}
void AddImmediateOperands(std::initializer_list<uint32_t> immediates) {
for (auto immediate : immediates) {
operands_.push_back(immediate);
}
}
void AddStringOperand(const char* str) {
original_string_ = str;
uint32_t word;
char* word_string = reinterpret_cast<char*>(&word);
char* word_ptr = word_string;
int char_count = 0;
char c;
do {
c = *(str++);
*(word_ptr++) = c;
++char_count;
if (char_count == 4) {
AddImmediateOperand(word);
word_ptr = word_string;
char_count = 0;
}
} while (c != 0);
// deal with partial last word
if (char_count > 0) {
// pad with 0s
for (; char_count < 4; ++char_count) {
*(word_ptr++) = 0;
}
AddImmediateOperand(word);
}
}
Op opcode() const { return opcode_; }
int operand_count() const { return static_cast<int>(operands_.size()); }
Id result_id() const { return result_id_; }
Id type_id() const { return type_id_; }
Id id_operand(int op) const { return operands_[op]; }
uint32_t immediate_operand(int op) const { return operands_[op]; }
const char* string_operand() const { return original_string_.c_str(); }
// Write out the binary form.
void Serialize(std::vector<uint32_t>& out) const {
uint32_t word_count = 1;
if (type_id_) {
++word_count;
}
if (result_id_) {
++word_count;
}
word_count += static_cast<uint32_t>(operands_.size());
out.push_back((word_count << spv::WordCountShift) |
static_cast<uint32_t>(opcode_));
if (type_id_) {
out.push_back(type_id_);
}
if (result_id_) {
out.push_back(result_id_);
}
for (auto operand : operands_) {
out.push_back(operand);
}
}
private:
Instruction(const Instruction&) = delete;
Id result_id_ = NoResult;
Id type_id_ = NoType;
Op opcode_;
std::vector<Id> operands_;
std::string original_string_; // could be optimized away; convenience for
// getting string operand
};
class Block {
public:
Block(Id id, Function& parent);
~Block() {
for (size_t i = 0; i < instructions_.size(); ++i) {
delete instructions_[i];
}
for (size_t i = 0; i < local_variables_.size(); ++i) {
delete local_variables_[i];
}
}
Id id() { return instructions_.front()->result_id(); }
Function& parent() const { return parent_; }
void AddInstruction(Instruction* instr);
void AddLocalVariable(Instruction* instr) {
local_variables_.push_back(instr);
}
void AddPredecessor(Block* predecessor) {
predecessors_.push_back(predecessor);
}
int predecessor_count() const {
return static_cast<int>(predecessors_.size());
}
bool is_unreachable() const { return unreachable_; }
void set_unreachable(bool value) { unreachable_ = value; }
bool is_terminated() const {
switch (instructions_.back()->opcode()) {
case spv::Op::OpBranch:
case spv::Op::OpBranchConditional:
case spv::Op::OpSwitch:
case spv::Op::OpKill:
case spv::Op::OpReturn:
case spv::Op::OpReturnValue:
return true;
default:
return false;
}
}
void Serialize(std::vector<uint32_t>& out) const {
// skip the degenerate unreachable blocks
// TODO: code gen: skip all unreachable blocks (transitive closure)
// (but, until that's done safer to keep non-degenerate
// unreachable blocks, in case others depend on something)
if (unreachable_ && instructions_.size() <= 2) {
return;
}
instructions_[0]->Serialize(out);
for (auto variable : local_variables_) {
variable->Serialize(out);
}
for (int i = 1; i < instructions_.size(); ++i) {
instructions_[i]->Serialize(out);
}
}
private:
Block(const Block&) = delete;
Block& operator=(Block&) = delete;
// To enforce keeping parent and ownership in sync:
friend Function;
std::vector<Instruction*> instructions_;
std::vector<Block*> predecessors_;
std::vector<Instruction*> local_variables_;
Function& parent_;
// track whether this block is known to be uncreachable (not necessarily
// true for all unreachable blocks, but should be set at least
// for the extraneous ones introduced by the builder).
bool unreachable_;
};
class Function {
public:
Function(Id id, Id resultType, Id functionType, Id firstParam,
Module& parent);
~Function() {
for (size_t i = 0; i < parameter_instructions_.size(); ++i) {
delete parameter_instructions_[i];
}
for (size_t i = 0; i < blocks_.size(); ++i) {
delete blocks_[i];
}
}
Id id() const { return function_instruction_.result_id(); }
Id param_id(int p) { return parameter_instructions_[p]->result_id(); }
void push_block(Block* block) { blocks_.push_back(block); }
void pop_block(Block* block) { blocks_.pop_back(); }
Module& parent() const { return parent_; }
Block* entry_block() const { return blocks_.front(); }
Block* last_block() const { return blocks_.back(); }
void AddLocalVariable(Instruction* instr);
Id return_type() const { return function_instruction_.type_id(); }
void Serialize(std::vector<uint32_t>& out) const {
// OpFunction
function_instruction_.Serialize(out);
// OpFunctionParameter
for (auto instruction : parameter_instructions_) {
instruction->Serialize(out);
}
// Blocks
for (auto block : blocks_) {
block->Serialize(out);
}
Instruction end(0, 0, spv::Op::OpFunctionEnd);
end.Serialize(out);
}
private:
Function(const Function&) = delete;
Function& operator=(Function&) = delete;
Module& parent_;
Instruction function_instruction_;
std::vector<Instruction*> parameter_instructions_;
std::vector<Block*> blocks_;
};
class Module {
public:
Module() = default;
~Module() {
for (size_t i = 0; i < functions_.size(); ++i) {
delete functions_[i];
}
}
void AddFunction(Function* function) { functions_.push_back(function); }
void MapInstruction(Instruction* instr) {
spv::Id result_id = instr->result_id();
// Map the instruction's result id.
if (result_id >= id_to_instruction_.size()) {
id_to_instruction_.resize(result_id + 16);
}
id_to_instruction_[result_id] = instr;
}
Instruction* instruction(Id id) const { return id_to_instruction_[id]; }
spv::Id type_id(Id result_id) const {
return id_to_instruction_[result_id]->type_id();
}
spv::StorageClass storage_class(Id type_id) const {
return (spv::StorageClass)id_to_instruction_[type_id]->immediate_operand(0);
}
void Serialize(std::vector<uint32_t>& out) const {
for (auto function : functions_) {
function->Serialize(out);
}
}
private:
Module(const Module&) = delete;
std::vector<Function*> functions_;
// Maps from result id to instruction having that result id.
std::vector<Instruction*> id_to_instruction_;
};
inline Function::Function(Id id, Id result_type_id, Id function_type_id,
Id first_param_id, Module& parent)
: parent_(parent),
function_instruction_(id, result_type_id, spv::Op::OpFunction) {
// OpFunction
function_instruction_.AddImmediateOperand(
static_cast<uint32_t>(spv::FunctionControlMask::MaskNone));
function_instruction_.AddIdOperand(function_type_id);
parent.MapInstruction(&function_instruction_);
parent.AddFunction(this);
// OpFunctionParameter
Instruction* type_instr = parent.instruction(function_type_id);
int param_count = type_instr->operand_count() - 1;
for (int p = 0; p < param_count; ++p) {
auto param =
new Instruction(first_param_id + p, type_instr->id_operand(p + 1),
spv::Op::OpFunctionParameter);
parent.MapInstruction(param);
parameter_instructions_.push_back(param);
}
}
inline void Function::AddLocalVariable(Instruction* instr) {
blocks_[0]->AddLocalVariable(instr);
parent_.MapInstruction(instr);
}
inline Block::Block(Id id, Function& parent)
: parent_(parent), unreachable_(false) {
instructions_.push_back(new Instruction(id, NoType, spv::Op::OpLabel));
}
inline void Block::AddInstruction(Instruction* inst) {
instructions_.push_back(inst);
if (inst->result_id()) {
parent_.parent().MapInstruction(inst);
}
}
} // namespace spirv
} // namespace ui
} // namespace xe
#endif // XENIA_UI_SPIRV_SPIRV_IR_H_

View File

@ -10,8 +10,8 @@
#ifndef XENIA_UI_SPIRV_SPIRV_UTIL_H_ #ifndef XENIA_UI_SPIRV_SPIRV_UTIL_H_
#define XENIA_UI_SPIRV_SPIRV_UTIL_H_ #define XENIA_UI_SPIRV_SPIRV_UTIL_H_
#include "third_party/spirv/GLSL.std.450.h" #include "third_party/spirv/GLSL.std.450.hpp11"
#include "third_party/spirv/spirv.h" #include "third_party/spirv/spirv.hpp11"
// Forward declarations from SPIRV-Tools so we don't pollute /so/ much. // Forward declarations from SPIRV-Tools so we don't pollute /so/ much.
struct spv_binary_t; struct spv_binary_t;

View File

@ -0,0 +1,80 @@
/**
******************************************************************************
* 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/spirv/spirv_validator.h"
#include "third_party/spirv-tools/include/spirv-tools/libspirv.h"
#include "xenia/base/logging.h"
namespace xe {
namespace ui {
namespace spirv {
SpirvValidator::Result::Result(spv_text text, spv_diagnostic diagnostic)
: text_(text), diagnostic_(diagnostic) {}
SpirvValidator::Result::~Result() {
if (text_) {
spvTextDestroy(text_);
}
if (diagnostic_) {
spvDiagnosticDestroy(diagnostic_);
}
}
bool SpirvValidator::Result::has_error() const { return !!diagnostic_; }
size_t SpirvValidator::Result::error_word_index() const {
return diagnostic_ ? diagnostic_->position.index : 0;
}
const char* SpirvValidator::Result::error_string() const {
return diagnostic_ ? diagnostic_->error : "";
}
const char* SpirvValidator::Result::text() const {
return text_ ? text_->str : "";
}
std::string SpirvValidator::Result::to_string() const {
return text_ ? std::string(text_->str, text_->length) : "";
}
void SpirvValidator::Result::AppendText(StringBuffer* target_buffer) const {
if (text_) {
target_buffer->AppendBytes(reinterpret_cast<const uint8_t*>(text_->str),
text_->length);
}
}
SpirvValidator::SpirvValidator() : spv_context_(spvContextCreate()) {}
SpirvValidator::~SpirvValidator() { spvContextDestroy(spv_context_); }
std::unique_ptr<SpirvValidator::Result> SpirvValidator::Validate(
const uint32_t* words, size_t word_count) {
spv_text text = nullptr;
spv_diagnostic diagnostic = nullptr;
spv_const_binary_t binary = {words, word_count};
auto result_code =
spvValidate(spv_context_, &binary, SPV_VALIDATE_ALL, &diagnostic);
std::unique_ptr<Result> result(new Result(text, diagnostic));
if (result_code) {
XELOGE("Failed to validate spv: %d", result_code);
if (result->has_error()) {
return result;
} else {
return nullptr;
}
}
return result;
}
} // namespace spirv
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,66 @@
/**
******************************************************************************
* 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_SPIRV_SPIRV_VALIDATOR_H_
#define XENIA_UI_SPIRV_SPIRV_VALIDATOR_H_
#include <memory>
#include <string>
#include "xenia/base/string_buffer.h"
#include "xenia/ui/spirv/spirv_util.h"
namespace xe {
namespace ui {
namespace spirv {
class SpirvValidator {
public:
class Result {
public:
Result(spv_text text, spv_diagnostic diagnostic);
~Result();
// True if the result has an error associated with it.
bool has_error() const;
// Index of the error in the provided binary word data.
size_t error_word_index() const;
// Human-readable description of the error.
const char* error_string() const;
// Disassembled source text.
// Returned pointer lifetime is tied to this Result instance.
const char* text() const;
// Converts the disassembled source text to a string.
std::string to_string() const;
// Appends the disassembled source text to the given buffer.
void AppendText(StringBuffer* target_buffer) const;
private:
spv_text text_ = nullptr;
spv_diagnostic diagnostic_ = nullptr;
};
SpirvValidator();
~SpirvValidator();
// Validates the given SPIRV binary.
// The return will be nullptr if validation fails due to a library error.
// The return may have an error set on it if the SPIRV binary is malformed.
std::unique_ptr<Result> Validate(const uint32_t* words, size_t word_count);
private:
spv_context spv_context_ = nullptr;
};
} // namespace spirv
} // namespace ui
} // namespace xe
#endif // XENIA_UI_SPIRV_SPIRV_VALIDATOR_H_

View File

@ -0,0 +1,227 @@
/**
******************************************************************************
* 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 <algorithm>
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/vulkan/circular_buffer.h"
namespace xe {
namespace ui {
namespace vulkan {
CircularBuffer::CircularBuffer(VulkanDevice* device) : device_(device) {}
CircularBuffer::~CircularBuffer() { Shutdown(); }
bool CircularBuffer::Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage,
VkDeviceSize alignment) {
VkResult status = VK_SUCCESS;
capacity = xe::round_up(capacity, alignment);
// Create our internal buffer.
VkBufferCreateInfo buffer_info;
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.pNext = nullptr;
buffer_info.flags = 0;
buffer_info.size = capacity;
buffer_info.usage = usage;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_info.queueFamilyIndexCount = 0;
buffer_info.pQueueFamilyIndices = nullptr;
status = vkCreateBuffer(*device_, &buffer_info, nullptr, &gpu_buffer_);
CheckResult(status, "vkCreateBuffer");
if (status != VK_SUCCESS) {
return false;
}
VkMemoryRequirements reqs;
vkGetBufferMemoryRequirements(*device_, gpu_buffer_, &reqs);
// Allocate memory from the device to back the buffer.
assert_true(reqs.size == capacity);
reqs.alignment = std::max(alignment, reqs.alignment);
gpu_memory_ = device_->AllocateMemory(reqs);
if (!gpu_memory_) {
XELOGE("CircularBuffer::Initialize - Failed to allocate memory!");
Shutdown();
return false;
}
alignment_ = reqs.alignment;
capacity_ = reqs.size;
gpu_base_ = 0;
// Bind the buffer to its backing memory.
status = vkBindBufferMemory(*device_, gpu_buffer_, gpu_memory_, gpu_base_);
CheckResult(status, "vkBindBufferMemory");
if (status != VK_SUCCESS) {
XELOGE("CircularBuffer::Initialize - Failed to bind memory!");
Shutdown();
return false;
}
// Map the memory so we can access it.
status = vkMapMemory(*device_, gpu_memory_, gpu_base_, capacity_, 0,
reinterpret_cast<void**>(&host_base_));
CheckResult(status, "vkMapMemory");
if (status != VK_SUCCESS) {
XELOGE("CircularBuffer::Initialize - Failed to map memory!");
Shutdown();
return false;
}
return true;
}
void CircularBuffer::Shutdown() {
Clear();
if (host_base_) {
vkUnmapMemory(*device_, gpu_memory_);
host_base_ = nullptr;
}
if (gpu_buffer_) {
vkDestroyBuffer(*device_, gpu_buffer_, nullptr);
gpu_buffer_ = nullptr;
}
if (gpu_memory_) {
vkFreeMemory(*device_, gpu_memory_, nullptr);
gpu_memory_ = nullptr;
}
}
bool CircularBuffer::CanAcquire(VkDeviceSize length) {
// Make sure the length is aligned.
length = xe::round_up(length, alignment_);
if (allocations_.empty()) {
// Read head has caught up to write head (entire buffer available for write)
assert_true(read_head_ == write_head_);
return capacity_ >= length;
} else if (write_head_ < read_head_) {
// Write head wrapped around and is behind read head.
// | write |---- read ----|
return (read_head_ - write_head_) >= length;
} else if (write_head_ > read_head_) {
// Read head behind write head.
// 1. Check if there's enough room from write -> capacity
// | |---- read ----| write |
if ((capacity_ - write_head_) >= length) {
return true;
}
// 2. Check if there's enough room from 0 -> read
// | write |---- read ----| |
if ((read_head_ - 0) >= length) {
return true;
}
}
return false;
}
CircularBuffer::Allocation* CircularBuffer::Acquire(
VkDeviceSize length, std::shared_ptr<Fence> fence) {
VkDeviceSize aligned_length = xe::round_up(length, alignment_);
if (!CanAcquire(aligned_length)) {
return nullptr;
}
assert_true(write_head_ % alignment_ == 0);
if (write_head_ < read_head_) {
// Write head behind read head.
assert_true(read_head_ - write_head_ >= aligned_length);
auto alloc = new Allocation();
alloc->host_ptr = host_base_ + write_head_;
alloc->gpu_memory = gpu_memory_;
alloc->offset = gpu_base_ + write_head_;
alloc->length = length;
alloc->aligned_length = aligned_length;
alloc->fence = fence;
write_head_ += aligned_length;
allocations_.push_back(alloc);
return alloc;
} else {
// Write head equal to/after read head
if (capacity_ - write_head_ >= aligned_length) {
// Free space from write -> capacity
auto alloc = new Allocation();
alloc->host_ptr = host_base_ + write_head_;
alloc->gpu_memory = gpu_memory_;
alloc->offset = gpu_base_ + write_head_;
alloc->length = length;
alloc->aligned_length = aligned_length;
alloc->fence = fence;
write_head_ += aligned_length;
allocations_.push_back(alloc);
return alloc;
} else if ((read_head_ - 0) >= aligned_length) {
// Free space from begin -> read
auto alloc = new Allocation();
alloc->host_ptr = host_base_ + 0;
alloc->gpu_memory = gpu_memory_;
alloc->offset = gpu_base_ + 0;
alloc->length = length;
alloc->aligned_length = aligned_length;
alloc->fence = fence;
write_head_ = aligned_length;
allocations_.push_back(alloc);
return alloc;
}
}
return nullptr;
}
void CircularBuffer::Flush(Allocation* allocation) {
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;
vkFlushMappedMemoryRanges(*device_, 1, &range);
}
void CircularBuffer::Clear() {
for (auto alloc : allocations_) {
delete alloc;
}
allocations_.clear();
write_head_ = read_head_ = 0;
}
void CircularBuffer::Scavenge() {
for (auto it = allocations_.begin(); it != allocations_.end();) {
if ((*it)->fence->status() != VK_SUCCESS) {
// Don't bother freeing following allocations to ensure proper ordering.
break;
}
if (capacity_ - read_head_ < (*it)->aligned_length) {
// This allocation is stored at the beginning of the buffer.
read_head_ = (*it)->aligned_length;
} else {
read_head_ += (*it)->aligned_length;
}
delete *it;
it = allocations_.erase(it);
}
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,87 @@
/**
******************************************************************************
* 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_VULKAN_CIRCULAR_BUFFER_H_
#define XENIA_UI_VULKAN_CIRCULAR_BUFFER_H_
#include <list>
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_device.h"
namespace xe {
namespace ui {
namespace vulkan {
// A circular buffer, intended to hold (fairly) temporary memory that will be
// released when a fence is signaled. Best used when allocations are taken
// in-order with command buffer submission.
//
// Allocations loop around the buffer in circles (but are not fragmented at the
// ends of the buffer), where trailing older allocations are freed after use.
class CircularBuffer {
public:
CircularBuffer(VulkanDevice* device);
~CircularBuffer();
struct Allocation {
void* host_ptr;
VkDeviceMemory gpu_memory;
VkDeviceSize offset;
VkDeviceSize length;
VkDeviceSize aligned_length;
// Allocation usage fence. This allocation will be deleted when the fence
// becomes signaled.
std::shared_ptr<Fence> fence;
};
bool Initialize(VkDeviceSize capacity, VkBufferUsageFlags usage,
VkDeviceSize alignment = 256);
void Shutdown();
VkDeviceSize alignment() const { return alignment_; }
VkDeviceSize capacity() const { return capacity_; }
VkBuffer gpu_buffer() const { return gpu_buffer_; }
VkDeviceMemory gpu_memory() const { return gpu_memory_; }
uint8_t* host_base() const { return host_base_; }
bool CanAcquire(VkDeviceSize length);
// Acquires space to hold memory. This allocation is only freed when the fence
// reaches the signaled state.
Allocation* Acquire(VkDeviceSize length, std::shared_ptr<Fence> fence);
void Flush(Allocation* allocation);
// Clears all allocations, regardless of whether they've been consumed or not.
void Clear();
// Frees any allocations whose fences have been signaled.
void Scavenge();
private:
VkDeviceSize capacity_ = 0;
VkDeviceSize alignment_ = 0;
VkDeviceSize write_head_ = 0;
VkDeviceSize read_head_ = 0;
VulkanDevice* device_;
VkBuffer gpu_buffer_ = nullptr;
VkDeviceMemory gpu_memory_ = nullptr;
VkDeviceSize gpu_base_ = 0;
uint8_t* host_base_ = nullptr;
std::list<Allocation*> allocations_;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_GL_CIRCULAR_BUFFER_H_

View File

@ -0,0 +1,82 @@
/**
******************************************************************************
* 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/fenced_pools.h"
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
namespace ui {
namespace vulkan {
using xe::ui::vulkan::CheckResult;
CommandBufferPool::CommandBufferPool(VkDevice device,
uint32_t queue_family_index,
VkCommandBufferLevel level)
: BaseFencedPool(device), level_(level) {
// Create the pool used for allocating buffers.
// They are marked as transient (short-lived) and cycled frequently.
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_TRANSIENT_BIT |
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
cmd_pool_info.queueFamilyIndex = queue_family_index;
auto err =
vkCreateCommandPool(device_, &cmd_pool_info, nullptr, &command_pool_);
CheckResult(err, "vkCreateCommandPool");
// Allocate a bunch of command buffers to start.
constexpr uint32_t kDefaultCount = 32;
VkCommandBufferAllocateInfo command_buffer_info;
command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
command_buffer_info.pNext = nullptr;
command_buffer_info.commandPool = command_pool_;
command_buffer_info.level = level;
command_buffer_info.commandBufferCount = kDefaultCount;
VkCommandBuffer command_buffers[kDefaultCount];
err =
vkAllocateCommandBuffers(device_, &command_buffer_info, command_buffers);
CheckResult(err, "vkCreateCommandBuffer");
for (size_t i = 0; i < xe::countof(command_buffers); ++i) {
PushEntry(command_buffers[i]);
}
}
CommandBufferPool::~CommandBufferPool() {
FreeAllEntries();
vkDestroyCommandPool(device_, command_pool_, nullptr);
command_pool_ = nullptr;
}
VkCommandBuffer CommandBufferPool::AllocateEntry() {
// TODO(benvanik): allocate a bunch at once?
VkCommandBufferAllocateInfo command_buffer_info;
command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
command_buffer_info.pNext = nullptr;
command_buffer_info.commandPool = command_pool_;
command_buffer_info.level = level_;
command_buffer_info.commandBufferCount = 1;
VkCommandBuffer command_buffer;
auto err =
vkAllocateCommandBuffers(device_, &command_buffer_info, &command_buffer);
CheckResult(err, "vkCreateCommandBuffer");
return command_buffer;
}
void CommandBufferPool::FreeEntry(VkCommandBuffer handle) {
vkFreeCommandBuffers(device_, command_pool_, 1, &handle);
}
} // namespace vulkan
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,231 @@
/**
******************************************************************************
* 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_FENCED_POOLS_H_
#define XENIA_UI_VULKAN_FENCED_POOLS_H_
#include <memory>
#include "xenia/base/assert.h"
#include "xenia/ui/vulkan/vulkan.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
namespace ui {
namespace vulkan {
// Simple pool for Vulkan homogenous objects that cannot be reused while
// in-flight.
// It batches pooled objects into groups and uses a vkQueueSubmit fence to
// indicate their availability. If no objects are free when one is requested
// the caller is expected to create them.
template <typename T, typename HANDLE>
class BaseFencedPool {
public:
BaseFencedPool(VkDevice device) : device_(device) {}
virtual ~BaseFencedPool() {
// TODO(benvanik): wait on fence until done.
assert_null(pending_batch_list_head_);
// Subclasses must call FreeAllEntries() to properly clean up things.
assert_null(free_batch_list_head_);
assert_null(free_entry_list_head_);
}
// True if one or more batches are still pending on the GPU.
bool has_pending() const { return pending_batch_list_head_ != nullptr; }
// True if a batch is open.
bool has_open_batch() const { return open_batch_ != nullptr; }
// Checks all pending batches for completion and scavenges their entries.
// This should be called as frequently as reasonable.
void Scavenge() {
while (pending_batch_list_head_) {
auto batch = pending_batch_list_head_;
if (vkGetFenceStatus(device_, *batch->fence) == VK_SUCCESS) {
// Batch has completed. Reclaim.
pending_batch_list_head_ = batch->next;
if (batch == pending_batch_list_tail_) {
pending_batch_list_tail_ = nullptr;
}
batch->next = free_batch_list_head_;
free_batch_list_head_ = batch;
batch->entry_list_tail->next = free_entry_list_head_;
free_entry_list_head_ = batch->entry_list_head;
batch->entry_list_head = nullptr;
batch->entry_list_tail = nullptr;
} else {
// Batch is still in-flight. Since batches are executed in order we know
// no others after it could have completed, so early-exit.
return;
}
}
}
// Begins a new batch.
// All entries acquired within this batch will be marked as in-use until
// the fence specified in EndBatch is signalled.
void BeginBatch() {
assert_null(open_batch_);
Batch* batch = nullptr;
if (free_batch_list_head_) {
// Reuse a batch.
batch = free_batch_list_head_;
free_batch_list_head_ = batch->next;
batch->next = nullptr;
} else {
// Allocate new batch.
batch = new Batch();
batch->next = nullptr;
}
batch->entry_list_head = nullptr;
batch->entry_list_tail = nullptr;
batch->fence = nullptr;
open_batch_ = batch;
}
// Cancels an open batch, and releases all entries acquired within.
void CancelBatch() {
assert_not_null(open_batch_);
auto batch = open_batch_;
open_batch_ = nullptr;
// Relink the batch back into the free batch list.
batch->next = free_batch_list_head_;
free_batch_list_head_ = batch;
// Relink entries back into free entries list.
batch->entry_list_tail->next = free_entry_list_head_;
free_entry_list_head_ = batch->entry_list_head;
batch->entry_list_head = nullptr;
batch->entry_list_tail = nullptr;
}
// Attempts to acquire an entry from the pool in the current batch.
// If none are available a new one will be allocated.
HANDLE AcquireEntry() {
Entry* entry = nullptr;
if (free_entry_list_head_) {
// Slice off an entry from the free list.
entry = free_entry_list_head_;
free_entry_list_head_ = entry->next;
} else {
// No entry available; allocate new.
entry = new Entry();
entry->handle = static_cast<T*>(this)->AllocateEntry();
}
entry->next = nullptr;
if (!open_batch_->entry_list_head) {
open_batch_->entry_list_head = entry;
}
if (open_batch_->entry_list_tail) {
open_batch_->entry_list_tail->next = entry;
}
open_batch_->entry_list_tail = entry;
return entry->handle;
}
// Ends the current batch using the given fence to indicate when the batch
// has completed execution on the GPU.
void EndBatch(std::shared_ptr<Fence> fence) {
assert_not_null(open_batch_);
// Close and see if we have anything.
auto batch = open_batch_;
open_batch_ = nullptr;
if (!batch->entry_list_head) {
// Nothing to do.
batch->next = free_batch_list_head_;
free_batch_list_head_ = batch;
return;
}
// Track the fence.
batch->fence = fence;
// Append to the end of the batch list.
batch->next = nullptr;
if (!pending_batch_list_head_) {
pending_batch_list_head_ = batch;
}
if (pending_batch_list_tail_) {
pending_batch_list_tail_->next = batch;
pending_batch_list_tail_ = batch;
} else {
pending_batch_list_tail_ = batch;
}
}
protected:
void PushEntry(HANDLE handle) {
auto entry = new Entry();
entry->next = free_entry_list_head_;
entry->handle = handle;
free_entry_list_head_ = entry;
}
void FreeAllEntries() {
// Run down free lists.
while (free_batch_list_head_) {
auto batch = free_batch_list_head_;
free_batch_list_head_ = batch->next;
delete batch;
}
while (free_entry_list_head_) {
auto entry = free_entry_list_head_;
free_entry_list_head_ = entry->next;
static_cast<T*>(this)->FreeEntry(entry->handle);
delete entry;
}
}
VkDevice device_ = nullptr;
private:
struct Entry {
Entry* next;
HANDLE handle;
};
struct Batch {
Batch* next;
Entry* entry_list_head;
Entry* entry_list_tail;
std::shared_ptr<Fence> fence;
};
Batch* free_batch_list_head_ = nullptr;
Entry* free_entry_list_head_ = nullptr;
Batch* pending_batch_list_head_ = nullptr;
Batch* pending_batch_list_tail_ = nullptr;
Batch* open_batch_ = nullptr;
};
class CommandBufferPool
: public BaseFencedPool<CommandBufferPool, VkCommandBuffer> {
public:
CommandBufferPool(VkDevice device, uint32_t queue_family_index,
VkCommandBufferLevel level);
~CommandBufferPool() override;
protected:
friend class BaseFencedPool<CommandBufferPool, VkCommandBuffer>;
VkCommandBuffer AllocateEntry();
void FreeEntry(VkCommandBuffer handle);
VkCommandPool command_pool_ = nullptr;
VkCommandBufferLevel level_ = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
};
} // namespace vulkan
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VULKAN_FENCED_POOLS_H_

View File

@ -0,0 +1,56 @@
project_root = "../../../.."
include(project_root.."/tools/build")
group("src")
project("xenia-ui-vulkan")
uuid("4933d81e-1c2c-4d5d-b104-3c0eb9dc2f00")
kind("StaticLib")
language("C++")
links({
"xenia-base",
"xenia-ui",
"xenia-ui-spirv",
})
defines({
})
includedirs({
project_root.."/third_party/gflags/src",
project_root.."/third_party/vulkan/",
})
local_platform_files()
files({
"shaders/bin/*.h",
})
removefiles({"*_demo.cc"})
group("demos")
project("xenia-ui-window-vulkan-demo")
uuid("97598f13-3177-454c-8e58-c59e2b6ede27")
kind("WindowedApp")
language("C++")
links({
"gflags",
"imgui",
"vulkan-loader",
"xenia-base",
"xenia-ui",
"xenia-ui-spirv",
"xenia-ui-vulkan",
})
flags({
"WinMain", -- Use WinMain instead of main.
})
defines({
})
includedirs({
project_root.."/third_party/gflags/src",
project_root.."/third_party/vulkan/",
})
files({
"../window_demo.cc",
"vulkan_window_demo.cc",
project_root.."/src/xenia/base/main_"..platform_suffix..".cc",
})
resincludedirs({
project_root,
})

View File

@ -0,0 +1,125 @@
// generated from `xb genspirv`
// source: immediate.frag
const uint8_t immediate_frag[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x35, 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, 0x1E, 0x00, 0x00, 0x00,
0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 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, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00,
0x6F, 0x75, 0x74, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F,
0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x10, 0x00, 0x00, 0x00, 0x50, 0x75, 0x73, 0x68, 0x43, 0x6F, 0x6E, 0x73,
0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00,
0x10, 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, 0x06, 0x00, 0x0A, 0x00, 0x10, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74,
0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x61, 0x6D,
0x70, 0x6C, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x12, 0x00, 0x00, 0x00, 0x70, 0x75, 0x73, 0x68, 0x5F, 0x63, 0x6F, 0x6E,
0x73, 0x74, 0x61, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x76, 0x74, 0x78, 0x5F, 0x75, 0x76, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x5F,
0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F,
0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x72, 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, 0x48, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2E, 0x00, 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, 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,
0x14, 0x00, 0x02, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x11, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x20, 0x00, 0x04, 0x00,
0x29, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x19, 0x00, 0x09, 0x00, 0x2B, 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, 0x2C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x2E, 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,
0x3B, 0x00, 0x04, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x07, 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, 0x41, 0x00, 0x05, 0x00,
0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x05, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00,
0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00,
0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x05, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0xF7, 0x00, 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x27, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x2E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x09, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x28, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x28, 0x00, 0x00, 0x00,
0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};

Binary file not shown.

View File

@ -0,0 +1,93 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 1
; Bound: 53
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %9 %11 %30
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %9 "out_color"
OpName %11 "vtx_color"
OpName %16 "PushConstants"
OpMemberName %16 0 "projection_matrix"
OpMemberName %16 1 "restrict_texture_samples"
OpName %18 "push_constants"
OpName %30 "vtx_uv"
OpName %42 "tex_color"
OpName %46 "texture_sampler"
OpDecorate %9 Location 0
OpDecorate %11 Location 1
OpMemberDecorate %16 0 ColMajor
OpMemberDecorate %16 0 Offset 0
OpMemberDecorate %16 0 MatrixStride 16
OpMemberDecorate %16 1 Offset 64
OpDecorate %16 Block
OpDecorate %30 Location 0
OpDecorate %46 DescriptorSet 0
OpDecorate %46 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypePointer Output %7
%9 = OpVariable %8 Output
%10 = OpTypePointer Input %7
%11 = OpVariable %10 Input
%13 = OpTypeBool
%14 = OpTypeMatrix %7 4
%15 = OpTypeInt 32 1
%16 = OpTypeStruct %14 %15
%17 = OpTypePointer PushConstant %16
%18 = OpVariable %17 PushConstant
%19 = OpConstant %15 1
%20 = OpTypePointer PushConstant %15
%23 = OpConstant %15 0
%28 = OpTypeVector %6 2
%29 = OpTypePointer Input %28
%30 = OpVariable %29 Input
%31 = OpTypeInt 32 0
%32 = OpConstant %31 0
%33 = OpTypePointer Input %6
%36 = OpConstant %6 1
%41 = OpTypePointer Function %7
%43 = OpTypeImage %6 2D 0 0 0 1 Unknown
%44 = OpTypeSampledImage %43
%45 = OpTypePointer UniformConstant %44
%46 = OpVariable %45 UniformConstant
%4 = OpFunction %2 None %3
%5 = OpLabel
%42 = OpVariable %41 Function
%12 = OpLoad %7 %11
OpStore %9 %12
%21 = OpAccessChain %20 %18 %19
%22 = OpLoad %15 %21
%24 = OpIEqual %13 %22 %23
%25 = OpLogicalNot %13 %24
OpSelectionMerge %27 None
OpBranchConditional %25 %26 %27
%26 = OpLabel
%34 = OpAccessChain %33 %30 %32
%35 = OpLoad %6 %34
%37 = OpFOrdLessThanEqual %13 %35 %36
OpBranch %27
%27 = OpLabel
%38 = OpPhi %13 %24 %5 %37 %26
OpSelectionMerge %40 None
OpBranchConditional %38 %39 %40
%39 = OpLabel
%47 = OpLoad %44 %46
%48 = OpLoad %28 %30
%49 = OpImageSampleImplicitLod %7 %47 %48
OpStore %42 %49
%50 = OpLoad %7 %42
%51 = OpLoad %7 %9
%52 = OpFMul %7 %51 %50
OpStore %9 %52
OpBranch %40
%40 = OpLabel
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,137 @@
// generated from `xb genspirv`
// source: immediate.vert
const uint8_t immediate_vert[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00,
0x11, 0x00, 0x02, 0x00, 0x21, 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, 0x06, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74,
0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x61, 0x6D,
0x70, 0x6C, 0x65, 0x73, 0x00, 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, 0x48, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x40, 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, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x0E, 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, 0x41, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00,
0x27, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x27, 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,
};

Binary file not shown.

View File

@ -0,0 +1,95 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 1
; Bound: 48
; Schema: 0
OpCapability Shader
OpCapability ClipDistance
OpCapability CullDistance
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %4 "main" %13 %25 %41 %42 %44 %46
OpSource GLSL 450
OpName %4 "main"
OpName %11 "gl_PerVertex"
OpMemberName %11 0 "gl_Position"
OpMemberName %11 1 "gl_PointSize"
OpMemberName %11 2 "gl_ClipDistance"
OpMemberName %11 3 "gl_CullDistance"
OpName %13 ""
OpName %17 "PushConstants"
OpMemberName %17 0 "projection_matrix"
OpMemberName %17 1 "restrict_texture_samples"
OpName %19 "push_constants"
OpName %25 "in_pos"
OpName %41 "vtx_uv"
OpName %42 "in_uv"
OpName %44 "vtx_color"
OpName %46 "in_color"
OpMemberDecorate %11 0 BuiltIn Position
OpMemberDecorate %11 1 BuiltIn PointSize
OpMemberDecorate %11 2 BuiltIn ClipDistance
OpMemberDecorate %11 3 BuiltIn CullDistance
OpDecorate %11 Block
OpMemberDecorate %17 0 ColMajor
OpMemberDecorate %17 0 Offset 0
OpMemberDecorate %17 0 MatrixStride 16
OpMemberDecorate %17 1 Offset 64
OpDecorate %17 Block
OpDecorate %25 Location 0
OpDecorate %41 Location 0
OpDecorate %42 Location 1
OpDecorate %44 Location 1
OpDecorate %46 Location 2
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%8 = OpTypeInt 32 0
%9 = OpConstant %8 1
%10 = OpTypeArray %6 %9
%11 = OpTypeStruct %7 %6 %10 %10
%12 = OpTypePointer Output %11
%13 = OpVariable %12 Output
%14 = OpTypeInt 32 1
%15 = OpConstant %14 0
%16 = OpTypeMatrix %7 4
%17 = OpTypeStruct %16 %14
%18 = OpTypePointer PushConstant %17
%19 = OpVariable %18 PushConstant
%20 = OpTypePointer PushConstant %16
%23 = OpTypeVector %6 2
%24 = OpTypePointer Input %23
%25 = OpVariable %24 Input
%27 = OpConstant %6 0
%28 = OpConstant %6 1
%33 = OpTypePointer Output %7
%35 = OpTypePointer Output %6
%40 = OpTypePointer Output %23
%41 = OpVariable %40 Output
%42 = OpVariable %24 Input
%44 = OpVariable %33 Output
%45 = OpTypePointer Input %7
%46 = OpVariable %45 Input
%4 = OpFunction %2 None %3
%5 = OpLabel
%21 = OpAccessChain %20 %19 %15
%22 = OpLoad %16 %21
%26 = OpLoad %23 %25
%29 = OpCompositeExtract %6 %26 0
%30 = OpCompositeExtract %6 %26 1
%31 = OpCompositeConstruct %7 %29 %30 %27 %28
%32 = OpMatrixTimesVector %7 %22 %31
%34 = OpAccessChain %33 %13 %15
OpStore %34 %32
%36 = OpAccessChain %35 %13 %15 %9
%37 = OpLoad %6 %36
%38 = OpFNegate %6 %37
%39 = OpAccessChain %35 %13 %15 %9
OpStore %39 %38
%43 = OpLoad %23 %42
OpStore %41 %43
%47 = OpLoad %7 %46
OpStore %44 %47
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,26 @@
// 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 {
mat4 projection_matrix;
int restrict_texture_samples;
} push_constants;
layout(set = 0, binding = 0) uniform sampler2D texture_sampler;
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.
}
}

View File

@ -0,0 +1,24 @@
// 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 {
mat4 projection_matrix;
int restrict_texture_samples;
} 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;
}

View File

@ -2,21 +2,15 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/ui/spirv/spirv_optimizer.h" #include "xenia/ui/vulkan/vulkan.h"
namespace xe { DEFINE_bool(vulkan_validation, false, "Enable Vulkan validation layers.");
namespace ui {
namespace spirv {
SpirvOptimizer::SpirvOptimizer() = default; DEFINE_bool(vulkan_primary_queue_only, false,
"Force the use of the primary queue, ignoring any additional that "
SpirvOptimizer::~SpirvOptimizer() = default; "may be present.");
} // namespace spirv
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,35 @@
/**
******************************************************************************
* 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 <gflags/gflags.h>
#include "xenia/base/platform.h"
#if XE_PLATFORM_WIN32
#define VK_USE_PLATFORM_WIN32_KHR 1
#else
#error Platform not yet supported.
#endif // XE_PLATFORM_WIN32
// We are statically linked with the loader, so use function prototypes.
#define VK_PROTOTYPES
#include "third_party/vulkan/vulkan.h"
// NOTE: header order matters here, unfortunately:
#include "third_party/vulkan/vk_lunarg_debug_marker.h"
#define XELOGVK XELOGI
DECLARE_bool(vulkan_validation);
DECLARE_bool(vulkan_primary_queue_only);
#endif // XENIA_UI_VULKAN_VULKAN_H_

View File

@ -0,0 +1,151 @@
/**
******************************************************************************
* 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_context.h"
#include <mutex>
#include <string>
#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"
namespace xe {
namespace ui {
namespace vulkan {
VulkanContext::VulkanContext(VulkanProvider* provider, Window* target_window)
: GraphicsContext(provider, target_window) {}
VulkanContext::~VulkanContext() {
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
{
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
vkQueueWaitIdle(device->primary_queue());
}
immediate_drawer_.reset();
swap_chain_.reset();
}
bool VulkanContext::Initialize() {
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
if (target_window_) {
// Create swap chain used to present to the window.
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<HINSTANCE>(target_window_->native_platform_handle());
create_info.hwnd = static_cast<HWND>(target_window_->native_handle());
auto err = vkCreateWin32SurfaceKHR(*provider->instance(), &create_info,
nullptr, &surface);
CheckResult(err, "vkCreateWin32SurfaceKHR");
#else
#error Platform not yet implemented.
#endif // XE_PLATFORM_WIN32
swap_chain_ = std::make_unique<VulkanSwapChain>(provider->instance(),
provider->device());
if (!swap_chain_->Initialize(surface)) {
XELOGE("Unable to initialize swap chain");
vkDestroySurfaceKHR(*provider->instance(), surface, nullptr);
return false;
}
// Only initialize immediate mode drawer if we are not an offscreen context.
immediate_drawer_ = std::make_unique<VulkanImmediateDrawer>(this);
}
return true;
}
ImmediateDrawer* VulkanContext::immediate_drawer() {
return immediate_drawer_.get();
}
VulkanInstance* VulkanContext::instance() const {
return static_cast<VulkanProvider*>(provider_)->instance();
}
VulkanDevice* VulkanContext::device() const {
return static_cast<VulkanProvider*>(provider_)->device();
}
bool VulkanContext::is_current() { return false; }
bool VulkanContext::MakeCurrent() {
SCOPE_profile_cpu_f("gpu");
return true;
}
void VulkanContext::ClearCurrent() {}
void VulkanContext::BeginSwap() {
SCOPE_profile_cpu_f("gpu");
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
// 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_->width() != swap_chain_->surface_width() ||
target_window_->height() != swap_chain_->surface_height()) {
// Resized!
swap_chain_->Reinitialize();
}
}
// Acquire the next image and set it up for use.
swap_chain_->Begin();
// TODO(benvanik): use a fence instead? May not be possible with target image.
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
auto err = vkQueueWaitIdle(device->primary_queue());
CheckResult(err, "vkQueueWaitIdle");
}
void VulkanContext::EndSwap() {
SCOPE_profile_cpu_f("gpu");
auto provider = static_cast<VulkanProvider*>(provider_);
auto device = provider->device();
// Notify the presentation engine the image is ready.
// The contents must be in a coherent state.
swap_chain_->End();
// Wait until the queue is idle.
// TODO(benvanik): is this required?
std::lock_guard<std::mutex> queue_lock(device->primary_queue_mutex());
auto err = vkQueueWaitIdle(device->primary_queue());
CheckResult(err, "vkQueueWaitIdle");
}
std::unique_ptr<RawImage> VulkanContext::Capture() {
// TODO(benvanik): read back swap chain front buffer.
return nullptr;
}
} // namespace vulkan
} // namespace ui
} // namespace xe

Some files were not shown because too many files have changed in this diff Show More