Merge branch 'vulkan'
This commit is contained in:
commit
841f6fe239
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
@ -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_
|
|
@ -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
|
@ -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_
|
|
@ -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,
|
||||||
|
};
|
|
@ -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
|
|
@ -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,
|
||||||
|
};
|
|
@ -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
|
|
@ -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,
|
||||||
|
};
|
|
@ -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
|
|
@ -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,
|
||||||
|
};
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
@ -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
|
@ -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_
|
|
@ -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.");
|
|
@ -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_
|
|
@ -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, ®ion,
|
||||||
|
VK_FILTER_LINEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vulkan
|
||||||
|
} // namespace gpu
|
||||||
|
} // namespace xe
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
|
@ -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);
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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",
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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,
|
||||||
|
})
|
|
@ -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.
|
@ -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
|
|
@ -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.
|
@ -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
|
|
@ -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.
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
|
@ -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_
|
|
@ -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
Loading…
Reference in New Issue