diff options
Diffstat (limited to 'src')
75 files changed, 1556 insertions, 8175 deletions
diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index a999512c4..3b4b4b9c6 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -17,7 +17,7 @@ project("xenia-app") "libavutil", "snappy", "spirv-tools", - "vulkan-loader", + "volk", "xenia-apu", "xenia-apu-nop", "xenia-base", @@ -26,14 +26,12 @@ project("xenia-app") "xenia-cpu-backend-x64", "xenia-debug-ui", "xenia-gpu", - "xenia-gpu-gl4", "xenia-gpu-null", "xenia-gpu-vulkan", "xenia-hid", "xenia-hid-nop", "xenia-kernel", "xenia-ui", - "xenia-ui-gl", "xenia-ui-spirv", "xenia-ui-vulkan", "xenia-vfs", @@ -60,6 +58,15 @@ project("xenia-app") project_root, }) + filter("platforms:Linux") + links({ + "X11", + "xcb", + "X11-xcb", + "GL", + "vulkan", + }) + filter("platforms:Windows") links({ "xenia-apu-xaudio2", diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 7b20892a7..be2f3d34b 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -26,7 +26,6 @@ #endif // XE_PLATFORM_WIN32 // Available graphics systems: -#include "xenia/gpu/gl4/gl4_graphics_system.h" #include "xenia/gpu/null/null_graphics_system.h" #include "xenia/gpu/vulkan/vulkan_graphics_system.h" @@ -38,7 +37,7 @@ #endif // XE_PLATFORM_WIN32 DEFINE_string(apu, "any", "Audio system. Use: [any, nop, xaudio2]"); -DEFINE_string(gpu, "any", "Graphics system. Use: [any, gl4, vulkan, null]"); +DEFINE_string(gpu, "any", "Graphics system. Use: [any, vulkan, null]"); DEFINE_string(hid, "any", "Input system. Use: [any, nop, winkey, xinput]"); DEFINE_string(target, "", "Specifies the target .xex or .iso to execute."); @@ -71,10 +70,7 @@ std::unique_ptr<apu::AudioSystem> CreateAudioSystem(cpu::Processor* processor) { } std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() { - if (FLAGS_gpu.compare("gl4") == 0) { - return std::unique_ptr<gpu::GraphicsSystem>( - new xe::gpu::gl4::GL4GraphicsSystem()); - } else if (FLAGS_gpu.compare("vulkan") == 0) { + if (FLAGS_gpu.compare("vulkan") == 0) { return std::unique_ptr<gpu::GraphicsSystem>( new xe::gpu::vulkan::VulkanGraphicsSystem()); } else if (FLAGS_gpu.compare("null") == 0) { diff --git a/src/xenia/base/bit_field.h b/src/xenia/base/bit_field.h index 034f43d9e..98a8bfbf8 100644 --- a/src/xenia/base/bit_field.h +++ b/src/xenia/base/bit_field.h @@ -19,20 +19,28 @@ namespace xe { // Bitfield, where position starts at the LSB. template <typename T, size_t position, size_t n_bits> struct bf { + // For enum values, we strip them down to an underlying type. + typedef + typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>, + std::remove_reference<T>>::type::type + value_type; + bf() = default; inline operator T() const { return value(); } inline T value() const { - return static_cast<T>((storage & mask()) >> position); + auto value = (storage & mask()) >> position; + if (std::is_signed<value_type>::value) { + // If the value is signed, sign-extend it. + value_type sign_mask = value_type(1) << (n_bits - 1); + value = (sign_mask ^ value) - sign_mask; + } + + return static_cast<T>(value); } - // For enum values, we strip them down to an underlying type. - typedef - typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>, - std::remove_reference<T>>::type::type - value_type; inline value_type mask() const { - return (((value_type)~0) >> (8 * sizeof(value_type) - n_bits)) << position; + return ((value_type(1) << n_bits) - 1) << position; } value_type storage; diff --git a/src/xenia/base/memory.cc b/src/xenia/base/memory.cc index bdc0ea7f1..1f9d654a1 100644 --- a/src/xenia/base/memory.cc +++ b/src/xenia/base/memory.cc @@ -26,7 +26,9 @@ void copy_128_aligned(void* dest, const void* src, size_t count) { #if XE_ARCH_AMD64 void copy_and_swap_16_aligned(void* dest_ptr, const void* src_ptr, size_t count) { - assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0x1); + assert_zero(reinterpret_cast<uintptr_t>(dest_ptr) & 0xF); + assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0xF); + auto dest = reinterpret_cast<uint16_t*>(dest_ptr); auto src = reinterpret_cast<const uint16_t*>(src_ptr); __m128i shufmask = @@ -34,12 +36,7 @@ void copy_and_swap_16_aligned(void* dest_ptr, const void* src_ptr, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01); size_t i = 0; - size_t unaligned_words = (reinterpret_cast<uintptr_t>(src_ptr) & 0xF) / 2; - for (; unaligned_words > 0 && i < count; unaligned_words--, i++) { - // Copy up to 16 byte alignment. - dest[i] = byte_swap(src[i]); - } - for (; i + 8 <= count; i += 8) { + for (i = 0; i + 8 <= count; i += 8) { __m128i input = _mm_load_si128(reinterpret_cast<const __m128i*>(&src[i])); __m128i output = _mm_shuffle_epi8(input, shufmask); _mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output); @@ -70,20 +67,17 @@ void copy_and_swap_16_unaligned(void* dest_ptr, const void* src_ptr, void copy_and_swap_32_aligned(void* dest_ptr, const void* src_ptr, size_t count) { - assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0x3); + assert_zero(reinterpret_cast<uintptr_t>(dest_ptr) & 0xF); + assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0xF); + auto dest = reinterpret_cast<uint32_t*>(dest_ptr); auto src = reinterpret_cast<const uint32_t*>(src_ptr); __m128i shufmask = _mm_set_epi8(0x0C, 0x0D, 0x0E, 0x0F, 0x08, 0x09, 0x0A, 0x0B, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03); - size_t i = 0; - size_t unaligned_dwords = (reinterpret_cast<uintptr_t>(src_ptr) & 0xF) / 4; - for (; unaligned_dwords > 0 && i < count; unaligned_dwords--, i++) { - // Copy up to 16 byte alignment. - dest[i] = byte_swap(src[i]); - } - for (; i + 4 <= count; i += 4) { + size_t i; + for (i = 0; i + 4 <= count; i += 4) { __m128i input = _mm_load_si128(reinterpret_cast<const __m128i*>(&src[i])); __m128i output = _mm_shuffle_epi8(input, shufmask); _mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output); @@ -114,20 +108,17 @@ void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr, void copy_and_swap_64_aligned(void* dest_ptr, const void* src_ptr, size_t count) { - assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0x7); + assert_zero(reinterpret_cast<uintptr_t>(dest_ptr) & 0xF); + assert_zero(reinterpret_cast<uintptr_t>(src_ptr) & 0xF); + auto dest = reinterpret_cast<uint64_t*>(dest_ptr); auto src = reinterpret_cast<const uint64_t*>(src_ptr); __m128i shufmask = _mm_set_epi8(0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07); - size_t i = 0; - size_t unaligned_qwords = (reinterpret_cast<uintptr_t>(src_ptr) & 0xF) / 8; - for (; unaligned_qwords > 0 && i < count; unaligned_qwords--, i++) { - // Copy up to 16 byte alignment. - dest[i] = byte_swap(src[i]); - } - for (; i + 2 <= count; i += 2) { + size_t i; + for (i = 0; i + 2 <= count; i += 2) { __m128i input = _mm_load_si128(reinterpret_cast<const __m128i*>(&src[i])); __m128i output = _mm_shuffle_epi8(input, shufmask); _mm_store_si128(reinterpret_cast<__m128i*>(&dest[i]), output); diff --git a/src/xenia/base/memory_test.cc b/src/xenia/base/memory_test.cc deleted file mode 100644 index 427f132a3..000000000 --- a/src/xenia/base/memory_test.cc +++ /dev/null @@ -1,17 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/base/memory.h" - -#include "third_party/catch/include/catch.hpp" - -TEST_CASE("copy_and_swap_16_aligned", "Copy and Swap") { - // TODO(benvanik): tests. - REQUIRE(true == true); -} diff --git a/src/xenia/base/platform_linux.h b/src/xenia/base/platform_linux.h index 345be94ab..ac64879e5 100644 --- a/src/xenia/base/platform_linux.h +++ b/src/xenia/base/platform_linux.h @@ -13,6 +13,7 @@ // NOTE: if you're including this file it means you are explicitly depending // on Linux headers. Including this file outside of linux platform specific // source code will break portability +#include <cstddef> #include "xenia/base/platform.h" diff --git a/src/xenia/base/premake5.lua b/src/xenia/base/premake5.lua index 29a0799ca..960a6500c 100644 --- a/src/xenia/base/premake5.lua +++ b/src/xenia/base/premake5.lua @@ -16,11 +16,4 @@ project("xenia-base") "debug_visualizers.natvis", }) -test_suite("xenia-base-tests", project_root, ".", { - includedirs = { - project_root.."/third_party/gflags/src", - }, - links = { - "xenia-base", - }, -}) +include("testing") diff --git a/src/xenia/base/testing/memory_test.cc b/src/xenia/base/testing/memory_test.cc new file mode 100644 index 000000000..5433dc118 --- /dev/null +++ b/src/xenia/base/testing/memory_test.cc @@ -0,0 +1,419 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/memory.h" + +#include "third_party/catch/include/catch.hpp" + +namespace xe { +namespace base { +namespace test { + +TEST_CASE("copy_128_aligned", "Copy and Swap") { + alignas(128) uint8_t src[256], dest[256]; + for (uint8_t i = 0; i < 255; ++i) { + src[i] = 255 - i; + } + std::memset(dest, 0, sizeof(dest)); + copy_128_aligned(dest, src, 1); + REQUIRE(std::memcmp(dest, src, 128)); + REQUIRE(dest[128] == 0); + + std::memset(dest, 0, sizeof(dest)); + copy_128_aligned(dest, src, 2); + REQUIRE(std::memcmp(dest, src, 256)); + + std::memset(dest, 0, sizeof(dest)); + copy_128_aligned(dest, src + 1, 1); + REQUIRE(std::memcmp(dest, src + 1, 128)); +} + +TEST_CASE("copy_and_swap_16_aligned", "Copy and Swap") { + alignas(16) uint16_t a = 0x1111, b = 0xABCD; + copy_and_swap_16_aligned(&a, &b, 1); + REQUIRE(a == 0xCDAB); + REQUIRE(b == 0xABCD); + + alignas(16) uint16_t c[] = {0x0000, 0x0000, 0x0000, 0x0000}; + alignas(16) uint16_t d[] = {0x0123, 0x4567, 0x89AB, 0xCDEF}; + copy_and_swap_16_aligned(c, d, 1); + REQUIRE(c[0] == 0x2301); + REQUIRE(c[1] == 0x0000); + REQUIRE(c[2] == 0x0000); + REQUIRE(c[3] == 0x0000); + + copy_and_swap_16_aligned(c, d, 3); + REQUIRE(c[0] == 0x2301); + REQUIRE(c[1] == 0x6745); + REQUIRE(c[2] == 0xAB89); + REQUIRE(c[3] == 0x0000); + + copy_and_swap_16_aligned(c, d, 4); + REQUIRE(c[0] == 0x2301); + REQUIRE(c[1] == 0x6745); + REQUIRE(c[2] == 0xAB89); + REQUIRE(c[3] == 0xEFCD); + + alignas(16) uint64_t e; + copy_and_swap_16_aligned(&e, d, 4); + REQUIRE(e == 0xEFCDAB8967452301); + + alignas(16) char f[85] = {0x00}; + alignas(16) char g[] = + "This is a 85 byte long string... " + "It's supposed to be longer than standard alignment."; + copy_and_swap_16_aligned(f, g, 42); + REQUIRE(std::strcmp(f, + "hTsii s a58b ty eolgns rtni.g..I 't susppsodet oebl " + "noeg rhtnas atdnra dlagimnne.t") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_16_aligned(f, g + 16, 34); + REQUIRE(std::strcmp(f, + " eolgns rtni.g..I 't susppsodet oebl " + "noeg rhtnas atdnra dlagimnne.t") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_16_aligned(f, g + 32, 26); + REQUIRE(std::strcmp(f, + "I 't susppsodet oebl " + "noeg rhtnas atdnra dlagimnne.t") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_16_aligned(f, g + 64, 10); + REQUIRE(std::strcmp(f, "s atdnra dlagimnne.t") == 0); +} + +TEST_CASE("copy_and_swap_16_unaligned", "Copy and Swap") { + uint16_t a = 0x1111, b = 0xABCD; + copy_and_swap_16_unaligned(&a, &b, 1); + REQUIRE(a == 0xCDAB); + REQUIRE(b == 0xABCD); + + uint16_t c[] = {0x0000, 0x0000, 0x0000, 0x0000}; + uint16_t d[] = {0x0123, 0x4567, 0x89AB, 0xCDEF}; + copy_and_swap_16_unaligned(c, d, 1); + REQUIRE(c[0] == 0x2301); + REQUIRE(c[1] == 0x0000); + REQUIRE(c[2] == 0x0000); + REQUIRE(c[3] == 0x0000); + + copy_and_swap_16_unaligned(c, d, 4); + REQUIRE(c[0] == 0x2301); + REQUIRE(c[1] == 0x6745); + REQUIRE(c[2] == 0xAB89); + REQUIRE(c[3] == 0xEFCD); + + uint64_t e; + copy_and_swap_16_unaligned(&e, d, 4); + REQUIRE(e == 0xEFCDAB8967452301); + + char f[85] = {0x00}; + char g[] = + "This is a 85 byte long string... " + "It's supposed to be longer than standard alignment."; + copy_and_swap_16_unaligned(f, g, 42); + REQUIRE(std::strcmp(f, + "hTsii s a58b ty eolgns rtni.g..I 't susppsodet oebl " + "noeg rhtnas atdnra dlagimnne.t") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_16_unaligned(f, g + 1, 41); + REQUIRE(std::strcmp(f, + "ih ssia 8 5ybetl no gtsirgn.. .tIs's puopes dotb " + "eolgnret ah ntsnaaddra ilngemtn") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_16_unaligned(f, g + 2, 41); + REQUIRE(std::strcmp(f, + "sii s a58b ty eolgns rtni.g..I 't susppsodet oebl " + "noeg rhtnas atdnra dlagimnne.t") == 0); +} + +TEST_CASE("copy_and_swap_32_aligned", "Copy and Swap") { + alignas(32) uint32_t a = 0x11111111, b = 0x89ABCDEF; + copy_and_swap_32_aligned(&a, &b, 1); + REQUIRE(a == 0xEFCDAB89); + REQUIRE(b == 0x89ABCDEF); + + alignas(32) uint32_t c[] = {0x00000000, 0x00000000, 0x00000000, 0x00000000}; + alignas(32) uint32_t d[] = {0x01234567, 0x89ABCDEF, 0xE887EEED, 0xD8514199}; + copy_and_swap_32_aligned(c, d, 1); + REQUIRE(c[0] == 0x67452301); + REQUIRE(c[1] == 0x00000000); + REQUIRE(c[2] == 0x00000000); + REQUIRE(c[3] == 0x00000000); + + copy_and_swap_32_aligned(c, d, 3); + REQUIRE(c[0] == 0x67452301); + REQUIRE(c[1] == 0xEFCDAB89); + REQUIRE(c[2] == 0xEDEE87E8); + REQUIRE(c[3] == 0x00000000); + + copy_and_swap_32_aligned(c, d, 4); + REQUIRE(c[0] == 0x67452301); + REQUIRE(c[1] == 0xEFCDAB89); + REQUIRE(c[2] == 0xEDEE87E8); + REQUIRE(c[3] == 0x994151D8); + + alignas(32) uint64_t e; + copy_and_swap_32_aligned(&e, d, 2); + REQUIRE(e == 0xEFCDAB8967452301); + + alignas(32) char f[85] = {0x00}; + alignas(32) char g[] = + "This is a 85 byte long string... " + "It's supposed to be longer than standard alignment."; + copy_and_swap_32_aligned(f, g, 21); + REQUIRE(std::strcmp(f, + "sihT si 58 atyb ol es gnnirt...g'tI us ssoppt deeb " + "onol regnahtats radnla dmngi.tne") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_32_aligned(f, g + 16, 17); + REQUIRE(std::strcmp(f, + "ol es gnnirt...g'tI us ssoppt deeb " + "onol regnahtats radnla dmngi.tne") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_32_aligned(f, g + 32, 13); + REQUIRE(std::strcmp(f, + "'tI us ssoppt deeb " + "onol regnahtats radnla dmngi.tne") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_32_aligned(f, g + 64, 5); + REQUIRE(std::strcmp(f, "ats radnla dmngi.tne") == 0); +} + +TEST_CASE("copy_and_swap_32_unaligned", "Copy and Swap") { + uint32_t a = 0x11111111, b = 0x89ABCDEF; + copy_and_swap_32_unaligned(&a, &b, 1); + REQUIRE(a == 0xEFCDAB89); + REQUIRE(b == 0x89ABCDEF); + + uint32_t c[] = {0x00000000, 0x00000000, 0x00000000, 0x00000000}; + uint32_t d[] = {0x01234567, 0x89ABCDEF, 0xE887EEED, 0xD8514199}; + copy_and_swap_32_unaligned(c, d, 1); + REQUIRE(c[0] == 0x67452301); + REQUIRE(c[1] == 0x00000000); + REQUIRE(c[2] == 0x00000000); + REQUIRE(c[3] == 0x00000000); + + copy_and_swap_32_unaligned(c, d, 3); + REQUIRE(c[0] == 0x67452301); + REQUIRE(c[1] == 0xEFCDAB89); + REQUIRE(c[2] == 0xEDEE87E8); + REQUIRE(c[3] == 0x00000000); + + copy_and_swap_32_unaligned(c, d, 4); + REQUIRE(c[0] == 0x67452301); + REQUIRE(c[1] == 0xEFCDAB89); + REQUIRE(c[2] == 0xEDEE87E8); + REQUIRE(c[3] == 0x994151D8); + + uint64_t e; + copy_and_swap_32_unaligned(&e, d, 2); + REQUIRE(e == 0xEFCDAB8967452301); + + char f[85] = {0x00}; + char g[] = + "This is a 85 byte long string... " + "It's supposed to be longer than standard alignment."; + copy_and_swap_32_unaligned(f, g, 21); + REQUIRE(std::strcmp(f, + "sihT si 58 atyb ol es gnnirt...g'tI us ssoppt deeb " + "onol regnahtats radnla dmngi.tne") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_32_unaligned(f, g + 1, 20); + REQUIRE(std::strcmp(f, + " siha si 58 etybnol ts ggnir ...s'tIpus esopot d eb " + "gnolt re nahnatsdradila emng") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_32_unaligned(f, g + 2, 20); + REQUIRE(std::strcmp(f, + "i si a sb 58 etygnolrts .gniI .. s'tppusdeso ot l " + "ebegnoht rs nadnat dragilanemn") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_32_unaligned(f, g + 3, 20); + REQUIRE(std::strcmp(f, + "si s8 a yb 5l et gnoirts..gntI .s s'oppu desb otol " + "eregnaht ts nadnaa drngiltnem") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_32_unaligned(f, g + 4, 20); + REQUIRE(std::strcmp(f, + " si 58 atyb ol es gnnirt...g'tI us ssoppt deeb onol " + "regnahtats radnla dmngi.tne") == 0); +} + +TEST_CASE("copy_and_swap_64_aligned", "Copy and Swap") { + alignas(64) uint64_t a = 0x1111111111111111, b = 0x0123456789ABCDEF; + copy_and_swap_64_aligned(&a, &b, 1); + REQUIRE(a == 0xEFCDAB8967452301); + REQUIRE(b == 0x0123456789ABCDEF); + + alignas(64) uint64_t c[] = {0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000}; + alignas(64) uint64_t d[] = {0x0123456789ABCDEF, 0xE887EEEDD8514199, + 0x21D4745A1D4A7706, 0xA4174FED675766E3}; + copy_and_swap_64_aligned(c, d, 1); + REQUIRE(c[0] == 0xEFCDAB8967452301); + REQUIRE(c[1] == 0x0000000000000000); + REQUIRE(c[2] == 0x0000000000000000); + REQUIRE(c[3] == 0x0000000000000000); + + copy_and_swap_64_aligned(c, d, 3); + REQUIRE(c[0] == 0xEFCDAB8967452301); + REQUIRE(c[1] == 0x994151D8EDEE87E8); + REQUIRE(c[2] == 0x06774A1D5A74D421); + REQUIRE(c[3] == 0x0000000000000000); + + copy_and_swap_64_aligned(c, d, 4); + REQUIRE(c[0] == 0xEFCDAB8967452301); + REQUIRE(c[1] == 0x994151D8EDEE87E8); + REQUIRE(c[2] == 0x06774A1D5A74D421); + REQUIRE(c[3] == 0xE3665767ED4F17A4); + + alignas(64) uint64_t e; + copy_and_swap_64_aligned(&e, d, 1); + REQUIRE(e == 0xEFCDAB8967452301); + + alignas(64) char f[85] = {0x00}; + alignas(64) char g[] = + "This is a 85 byte long string... " + "It's supposed to be longer than standard alignment."; + copy_and_swap_64_aligned(f, g, 10); + REQUIRE(std::strcmp(f, + " si sihTtyb 58 as gnol e...gnirtus s'tI t desoppnol eb " + "onaht regradnats mngila d") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_aligned(f, g + 16, 8); + REQUIRE(std::strcmp(f, + "s gnol e...gnirtus s'tI t desoppnol eb " + "onaht regradnats mngila d") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_aligned(f, g + 32, 6); + REQUIRE(std::strcmp(f, + "us s'tI t desoppnol eb " + "onaht regradnats mngila d") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_aligned(f, g + 64, 2); + REQUIRE(std::strcmp(f, "radnats mngila d") == 0); +} + +TEST_CASE("copy_and_swap_64_unaligned", "Copy and Swap") { + uint64_t a = 0x1111111111111111, b = 0x0123456789ABCDEF; + copy_and_swap_64_unaligned(&a, &b, 1); + REQUIRE(a == 0xEFCDAB8967452301); + REQUIRE(b == 0x0123456789ABCDEF); + + uint64_t c[] = {0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000}; + uint64_t d[] = {0x0123456789ABCDEF, 0xE887EEEDD8514199, 0x21D4745A1D4A7706, + 0xA4174FED675766E3}; + copy_and_swap_64_unaligned(c, d, 1); + REQUIRE(c[0] == 0xEFCDAB8967452301); + REQUIRE(c[1] == 0x0000000000000000); + REQUIRE(c[2] == 0x0000000000000000); + REQUIRE(c[3] == 0x0000000000000000); + + copy_and_swap_64_unaligned(c, d, 3); + REQUIRE(c[0] == 0xEFCDAB8967452301); + REQUIRE(c[1] == 0x994151D8EDEE87E8); + REQUIRE(c[2] == 0x06774A1D5A74D421); + REQUIRE(c[3] == 0x0000000000000000); + + copy_and_swap_64_unaligned(c, d, 4); + REQUIRE(c[0] == 0xEFCDAB8967452301); + REQUIRE(c[1] == 0x994151D8EDEE87E8); + REQUIRE(c[2] == 0x06774A1D5A74D421); + REQUIRE(c[3] == 0xE3665767ED4F17A4); + + uint64_t e; + copy_and_swap_64_unaligned(&e, d, 1); + REQUIRE(e == 0xEFCDAB8967452301); + + char f[85] = {0x00}; + char g[] = + "This is a 85 byte long string... " + "It's supposed to be longer than standard alignment."; + copy_and_swap_64_unaligned(f, g, 10); + REQUIRE(std::strcmp(f, + " si sihTtyb 58 as gnol e...gnirtus s'tI t desoppnol eb " + "onaht regradnats mngila d") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_unaligned(f, g + 1, 10); + REQUIRE(std::strcmp(f, + "a si sihetyb 58 ts gnol ...gnirpus s'tIot desopgnol " + "eb naht redradnatsemngila ") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_unaligned(f, g + 2, 10); + REQUIRE(std::strcmp(f, + " a si si etyb 58rts gnolI ...gnippus s't ot desoegnol " + "ebs naht r dradnatnemngila") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_unaligned(f, g + 3, 10); + REQUIRE(std::strcmp(f, + "8 a si sl etyb 5irts gnotI ...gnoppus s'b ot desregnol " + "ets naht a dradnatnemngil") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_unaligned(f, g + 4, 10); + REQUIRE(std::strcmp(f, + "58 a si ol etyb nirts gn'tI ...gsoppus seb ot de " + "regnol ats nahtla dradn.tnemngi") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_unaligned(f, g + 5, 9); + REQUIRE(std::strcmp(f, + " 58 a sinol etybgnirts gs'tI ...esoppus eb ot dt " + "regnolnats nahila drad") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_unaligned(f, g + 6, 9); + REQUIRE(std::strcmp(f, + "b 58 a sgnol ety.gnirts s'tI ..desoppusl eb ot ht " + "regnodnats nagila dra") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_unaligned(f, g + 7, 9); + REQUIRE(std::strcmp(f, + "yb 58 a gnol et..gnirtss s'tI . desoppuol eb otaht " + "regnadnats nngila dr") == 0); + + std::memset(f, 0, sizeof(f)); + copy_and_swap_64_unaligned(f, g + 8, 9); + REQUIRE(std::strcmp(f, + "tyb 58 as gnol e...gnirtus s'tI t desoppnol eb onaht " + "regradnats mngila d") == 0); +} + +TEST_CASE("copy_and_swap_16_in_32_aligned", "Copy and Swap") { + // TODO(bwrsandman): test once properly understood. + REQUIRE(true == true); +} + +TEST_CASE("copy_and_swap_16_in_32_unaligned", "Copy and Swap") { + // TODO(bwrsandman): test once properly understood. + REQUIRE(true == true); +} + +} // namespace test +} // namespace base +} // namespace xe diff --git a/src/xenia/base/testing/premake5.lua b/src/xenia/base/testing/premake5.lua new file mode 100644 index 000000000..e432d7ec4 --- /dev/null +++ b/src/xenia/base/testing/premake5.lua @@ -0,0 +1,11 @@ +project_root = "../../../.." +include(project_root.."/tools/build") + +test_suite("xenia-base-tests", project_root, ".", { + includedirs = { + project_root.."/third_party/gflags/src", + }, + links = { + "xenia-base", + }, +}) diff --git a/src/xenia/cpu/backend/x64/x64_backend.cc b/src/xenia/cpu/backend/x64/x64_backend.cc index 7739c9519..3390869e5 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.cc +++ b/src/xenia/cpu/backend/x64/x64_backend.cc @@ -13,7 +13,9 @@ #include "third_party/capstone/include/capstone.h" #include "third_party/capstone/include/x86.h" + #include "xenia/base/exception_handler.h" +#include "xenia/base/logging.h" #include "xenia/cpu/backend/x64/x64_assembler.h" #include "xenia/cpu/backend/x64/x64_code_cache.h" #include "xenia/cpu/backend/x64/x64_emitter.h" @@ -65,11 +67,16 @@ bool X64Backend::Initialize(Processor* processor) { return false; } + Xbyak::util::Cpu cpu; + if (!cpu.has(Xbyak::util::Cpu::tAVX)) { + XELOGE("This CPU does not support AVX. The emulator will now crash."); + return false; + } + RegisterSequences(); // Need movbe to do advanced LOAD/STORE tricks. if (FLAGS_enable_haswell_instructions) { - Xbyak::util::Cpu cpu; machine_info_.supports_extended_load_store = cpu.has(Xbyak::util::Cpu::tMOVBE); } else { diff --git a/src/xenia/cpu/ppc/testing/ppc_testing_native_main.cc b/src/xenia/cpu/ppc/testing/ppc_testing_native_main.cc index 28ba3887b..25934f252 100644 --- a/src/xenia/cpu/ppc/testing/ppc_testing_native_main.cc +++ b/src/xenia/cpu/ppc/testing/ppc_testing_native_main.cc @@ -189,7 +189,7 @@ class TestRunner { ~TestRunner() { memory::DeallocFixed(memory_, memory_size_, - memory::DeallocationType::kDecommitRelease); + memory::DeallocationType::kRelease); memory::AlignedFree(context_); } diff --git a/src/xenia/gpu/command_processor.cc b/src/xenia/gpu/command_processor.cc index d957f432c..37f416646 100644 --- a/src/xenia/gpu/command_processor.cc +++ b/src/xenia/gpu/command_processor.cc @@ -236,7 +236,7 @@ void CommandProcessor::ShutdownContext() { context_.reset(); } void CommandProcessor::InitializeRingBuffer(uint32_t ptr, uint32_t log2_size) { read_ptr_index_ = 0; primary_buffer_ptr_ = ptr; - primary_buffer_size_ = uint32_t(std::pow(2u, log2_size)); + primary_buffer_size_ = 1 << log2_size; } void CommandProcessor::EnableReadPointerWriteBack(uint32_t ptr, @@ -695,7 +695,11 @@ bool CommandProcessor::ExecutePacketType3_ME_INIT(RingBuffer* reader, uint32_t packet, uint32_t count) { // initialize CP's micro-engine - reader->AdvanceRead(count * sizeof(uint32_t)); + me_bin_.clear(); + for (uint32_t i = 0; i < count; i++) { + me_bin_.push_back(reader->ReadAndSwap<uint32_t>()); + } + return true; } @@ -1047,8 +1051,8 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_EXT(RingBuffer* reader, 1, // max z }; assert_true(endianness == Endian::k8in16); - xe::copy_and_swap_16_aligned(memory_->TranslatePhysical(address), extents, - xe::countof(extents)); + xe::copy_and_swap_16_unaligned(memory_->TranslatePhysical(address), extents, + xe::countof(extents)); trace_writer_.WriteMemoryWrite(CpuToGpu(address), sizeof(extents)); return true; } diff --git a/src/xenia/gpu/command_processor.h b/src/xenia/gpu/command_processor.h index a418dd683..7cfedb0f9 100644 --- a/src/xenia/gpu/command_processor.h +++ b/src/xenia/gpu/command_processor.h @@ -215,6 +215,9 @@ class CommandProcessor { std::function<void()> swap_request_handler_; std::queue<std::function<void()>> pending_fns_; + // MicroEngine binary from PM4_ME_INIT + std::vector<uint32_t> me_bin_; + uint32_t counter_ = 0; uint32_t primary_buffer_ptr_ = 0; diff --git a/src/xenia/gpu/gl4/draw_batcher.cc b/src/xenia/gpu/gl4/draw_batcher.cc deleted file mode 100644 index fa2368884..000000000 --- a/src/xenia/gpu/gl4/draw_batcher.cc +++ /dev/null @@ -1,535 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/gpu/gl4/draw_batcher.h" - -#include <cstring> - -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/gpu/gl4/gl4_gpu_flags.h" -#include "xenia/gpu/gpu_flags.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -using namespace xe::gpu::xenos; - -const size_t kCommandBufferCapacity = 16 * (1024 * 1024); -const size_t kCommandBufferAlignment = 4; -const size_t kStateBufferCapacity = 64 * (1024 * 1024); -const size_t kStateBufferAlignment = 256; - -DrawBatcher::DrawBatcher(RegisterFile* register_file) - : register_file_(register_file), - command_buffer_(kCommandBufferCapacity, kCommandBufferAlignment), - state_buffer_(kStateBufferCapacity, kStateBufferAlignment), - array_data_buffer_(nullptr), - draw_open_(false) { - std::memset(&batch_state_, 0, sizeof(batch_state_)); - batch_state_.needs_reconfigure = true; - batch_state_.command_range_start = batch_state_.state_range_start = - UINTPTR_MAX; - std::memset(&active_draw_, 0, sizeof(active_draw_)); -} - -bool DrawBatcher::Initialize(CircularBuffer* array_data_buffer) { - array_data_buffer_ = array_data_buffer; - if (!command_buffer_.Initialize()) { - return false; - } - if (!state_buffer_.Initialize()) { - return false; - } - if (!InitializeTFB()) { - return false; - } - - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, command_buffer_.handle()); - return true; -} - -// Initializes a transform feedback object -// We use this to capture vertex data straight from the vertex/geometry shader. -bool DrawBatcher::InitializeTFB() { - glCreateBuffers(1, &tfvbo_); - if (!tfvbo_) { - return false; - } - - glCreateTransformFeedbacks(1, &tfbo_); - if (!tfbo_) { - return false; - } - - glCreateQueries(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 1, &tfqo_); - if (!tfqo_) { - return false; - } - - // TODO(DrChat): Calculate this based on the number of primitives drawn. - glNamedBufferData(tfvbo_, 16384 * 4, nullptr, GL_STATIC_READ); - - return true; -} - -void DrawBatcher::ShutdownTFB() { - glDeleteBuffers(1, &tfvbo_); - glDeleteTransformFeedbacks(1, &tfbo_); - glDeleteQueries(1, &tfqo_); - - tfvbo_ = 0; - tfbo_ = 0; - tfqo_ = 0; -} - -size_t DrawBatcher::QueryTFBSize() { - if (!tfb_enabled_) { - return 0; - } - - size_t size = 0; - switch (tfb_prim_type_gl_) { - case GL_POINTS: - size = tfb_prim_count_ * 1 * 4 * 4; - break; - case GL_LINES: - size = tfb_prim_count_ * 2 * 4 * 4; - break; - case GL_TRIANGLES: - size = tfb_prim_count_ * 3 * 4 * 4; - break; - } - - return size; -} - -bool DrawBatcher::ReadbackTFB(void* buffer, size_t size) { - if (!tfb_enabled_) { - XELOGW("DrawBatcher::ReadbackTFB called when TFB was disabled!"); - return false; - } - - void* data = glMapNamedBufferRange(tfvbo_, 0, size, GL_MAP_READ_BIT); - std::memcpy(buffer, data, size); - glUnmapNamedBuffer(tfvbo_); - - return true; -} - -void DrawBatcher::Shutdown() { - command_buffer_.Shutdown(); - state_buffer_.Shutdown(); - ShutdownTFB(); -} - -bool DrawBatcher::ReconfigurePipeline(GL4Shader* vertex_shader, - GL4Shader* pixel_shader, - GLuint pipeline) { - if (batch_state_.pipeline == pipeline) { - // No-op. - return true; - } - if (!Flush(FlushMode::kReconfigure)) { - return false; - } - - batch_state_.vertex_shader = vertex_shader; - batch_state_.pixel_shader = pixel_shader; - batch_state_.pipeline = pipeline; - - return true; -} - -bool DrawBatcher::BeginDrawArrays(PrimitiveType prim_type, - uint32_t index_count) { - assert_false(draw_open_); - if (batch_state_.prim_type != prim_type || batch_state_.indexed) { - if (!Flush(FlushMode::kReconfigure)) { - return false; - } - } - batch_state_.prim_type = prim_type; - batch_state_.indexed = false; - - if (!BeginDraw()) { - return false; - } - - auto cmd = active_draw_.draw_arrays_cmd; - cmd->base_instance = 0; - cmd->instance_count = 1; - cmd->count = index_count; - cmd->first_index = 0; - - return true; -} - -bool DrawBatcher::BeginDrawElements(PrimitiveType prim_type, - uint32_t index_count, - IndexFormat index_format) { - assert_false(draw_open_); - GLenum index_type = - index_format == IndexFormat::kInt32 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; - if (batch_state_.prim_type != prim_type || !batch_state_.indexed || - batch_state_.index_type != index_type) { - if (!Flush(FlushMode::kReconfigure)) { - return false; - } - } - batch_state_.prim_type = prim_type; - batch_state_.indexed = true; - batch_state_.index_type = index_type; - - if (!BeginDraw()) { - return false; - } - - uint32_t start_index = register_file_->values[XE_GPU_REG_VGT_INDX_OFFSET].u32; - assert_zero(start_index); - - auto cmd = active_draw_.draw_elements_cmd; - cmd->base_instance = 0; - cmd->instance_count = 1; - cmd->count = index_count; - cmd->first_index = start_index; - cmd->base_vertex = 0; - - return true; -} - -bool DrawBatcher::BeginDraw() { - draw_open_ = true; - - if (batch_state_.needs_reconfigure) { - batch_state_.needs_reconfigure = false; - // Have been reconfigured since last draw - need to compute state size. - // Layout: - // [draw command] - // [common header] - // [consts] - - // Padded to max. - GLsizei command_size = 0; - if (batch_state_.indexed) { - command_size = sizeof(DrawElementsIndirectCommand); - } else { - command_size = sizeof(DrawArraysIndirectCommand); - } - batch_state_.command_stride = - xe::round_up(command_size, GLsizei(kCommandBufferAlignment)); - - GLsizei header_size = sizeof(CommonHeader); - - // TODO(benvanik): consts sizing. - // GLsizei float_consts_size = sizeof(float4) * 512; - // GLsizei bool_consts_size = sizeof(uint32_t) * 8; - // GLsizei loop_consts_size = sizeof(uint32_t) * 32; - // GLsizei consts_size = - // float_consts_size + bool_consts_size + loop_consts_size; - // batch_state_.float_consts_offset = batch_state_.header_offset + - // header_size; - // batch_state_.bool_consts_offset = - // batch_state_.float_consts_offset + float_consts_size; - // batch_state_.loop_consts_offset = - // batch_state_.bool_consts_offset + bool_consts_size; - GLsizei consts_size = 0; - - batch_state_.state_stride = header_size + consts_size; - } - - // Allocate a command data block. - // We should treat it as write-only. - if (!command_buffer_.CanAcquire(batch_state_.command_stride)) { - Flush(FlushMode::kMakeCoherent); - } - active_draw_.command_allocation = - command_buffer_.Acquire(batch_state_.command_stride); - assert_not_null(active_draw_.command_allocation.host_ptr); - - // Allocate a state data block. - // We should treat it as write-only. - if (!state_buffer_.CanAcquire(batch_state_.state_stride)) { - Flush(FlushMode::kMakeCoherent); - } - active_draw_.state_allocation = - state_buffer_.Acquire(batch_state_.state_stride); - assert_not_null(active_draw_.state_allocation.host_ptr); - - active_draw_.command_address = - reinterpret_cast<uintptr_t>(active_draw_.command_allocation.host_ptr); - auto state_host_ptr = - reinterpret_cast<uintptr_t>(active_draw_.state_allocation.host_ptr); - active_draw_.header = reinterpret_cast<CommonHeader*>(state_host_ptr); - active_draw_.header->ps_param_gen = -1; - // active_draw_.float_consts = - // reinterpret_cast<float4*>(state_host_ptr + - // batch_state_.float_consts_offset); - // active_draw_.bool_consts = - // reinterpret_cast<uint32_t*>(state_host_ptr + - // batch_state_.bool_consts_offset); - // active_draw_.loop_consts = - // reinterpret_cast<uint32_t*>(state_host_ptr + - // batch_state_.loop_consts_offset); - return true; -} - -void DrawBatcher::DiscardDraw() { - if (!draw_open_) { - // No-op. - return; - } - draw_open_ = false; - - command_buffer_.Discard(std::move(active_draw_.command_allocation)); - state_buffer_.Discard(std::move(active_draw_.state_allocation)); -} - -bool DrawBatcher::CommitDraw() { - assert_true(draw_open_); - draw_open_ = false; - - // Copy over required constants. - CopyConstants(); - - if (batch_state_.state_range_start == UINTPTR_MAX) { - batch_state_.command_range_start = active_draw_.command_allocation.offset; - batch_state_.state_range_start = active_draw_.state_allocation.offset; - } - batch_state_.command_range_length += - active_draw_.command_allocation.aligned_length; - batch_state_.state_range_length += - active_draw_.state_allocation.aligned_length; - - command_buffer_.Commit(std::move(active_draw_.command_allocation)); - state_buffer_.Commit(std::move(active_draw_.state_allocation)); - - ++batch_state_.draw_count; - return true; -} - -void DrawBatcher::TFBBegin(PrimitiveType prim_type) { - if (!tfb_enabled_) { - return; - } - - // Translate the primitive typename to something compatible with TFB. - GLenum gl_prim_type = 0; - switch (prim_type) { - case PrimitiveType::kLineList: - gl_prim_type = GL_LINES; - break; - case PrimitiveType::kLineStrip: - gl_prim_type = GL_LINES; - break; - case PrimitiveType::kLineLoop: - gl_prim_type = GL_LINES; - break; - case PrimitiveType::kPointList: - // The geometry shader associated with this writes out triangles. - gl_prim_type = GL_TRIANGLES; - break; - case PrimitiveType::kTriangleList: - gl_prim_type = GL_TRIANGLES; - break; - case PrimitiveType::kTriangleStrip: - gl_prim_type = GL_TRIANGLES; - break; - case PrimitiveType::kRectangleList: - gl_prim_type = GL_TRIANGLES; - break; - case PrimitiveType::kTriangleFan: - gl_prim_type = GL_TRIANGLES; - break; - case PrimitiveType::kQuadList: - // FIXME: In some cases the geometry shader will output lines. - // See: GL4CommandProcessor::UpdateShaders - gl_prim_type = GL_TRIANGLES; - break; - default: - assert_unhandled_case(prim_type); - break; - } - - // TODO(DrChat): Resize the TFVBO here. - // Could draw a 2nd time with the rasterizer disabled once we have a primitive - // count. - - tfb_prim_type_ = prim_type; - tfb_prim_type_gl_ = gl_prim_type; - - glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfbo_); - - // Bind the buffer to the TFB object. - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfvbo_); - - // Begin a query for # prims written - glBeginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0, tfqo_); - - // Begin capturing. - glBeginTransformFeedback(gl_prim_type); -} - -void DrawBatcher::TFBEnd() { - if (!tfb_enabled_) { - return; - } - - glEndTransformFeedback(); - glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); - glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); - - // Cache the query size as query objects aren't shared. - GLint prim_count = 0; - glGetQueryObjectiv(tfqo_, GL_QUERY_RESULT, &prim_count); - tfb_prim_count_ = prim_count; -} - -bool DrawBatcher::Flush(FlushMode mode) { - GLboolean cull_enabled = 0; - if (batch_state_.draw_count) { -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - assert_not_zero(batch_state_.command_stride); - assert_not_zero(batch_state_.state_stride); - - // Flush pending buffer changes. - command_buffer_.Flush(); - state_buffer_.Flush(); - array_data_buffer_->Flush(); - - // State data is indexed by draw ID. - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, state_buffer_.handle(), - batch_state_.state_range_start, - batch_state_.state_range_length); - - GLenum prim_type = 0; - bool valid_prim = true; - switch (batch_state_.prim_type) { - case PrimitiveType::kPointList: - prim_type = GL_POINTS; - break; - case PrimitiveType::kLineList: - prim_type = GL_LINES; - break; - case PrimitiveType::kLineStrip: - prim_type = GL_LINE_STRIP; - break; - case PrimitiveType::kLineLoop: - prim_type = GL_LINE_LOOP; - break; - case PrimitiveType::kTriangleList: - prim_type = GL_TRIANGLES; - break; - case PrimitiveType::kTriangleStrip: - prim_type = GL_TRIANGLE_STRIP; - break; - case PrimitiveType::kTriangleFan: - prim_type = GL_TRIANGLE_FAN; - break; - case PrimitiveType::kRectangleList: - prim_type = GL_TRIANGLES; - // Rect lists aren't culled. There may be other things they skip too. - // assert_true( - // (register_file_->values[XE_GPU_REG_PA_SU_SC_MODE_CNTL].u32 - // & 0x3) == 0); - break; - case PrimitiveType::kQuadList: - prim_type = GL_LINES_ADJACENCY; - break; - default: - case PrimitiveType::kTriangleWithWFlags: - prim_type = GL_TRIANGLES; - valid_prim = false; - XELOGE("unsupported primitive type %d", batch_state_.prim_type); - assert_unhandled_case(batch_state_.prim_type); - break; - } - - // Fast path for single draws. - void* indirect_offset = - reinterpret_cast<void*>(batch_state_.command_range_start); - - if (tfb_enabled_) { - TFBBegin(batch_state_.prim_type); - } - - if (valid_prim && batch_state_.draw_count == 1) { - // Fast path for one draw. Removes MDI overhead when not required. - if (batch_state_.indexed) { - auto& cmd = active_draw_.draw_elements_cmd; - glDrawElementsInstancedBaseVertexBaseInstance( - prim_type, cmd->count, batch_state_.index_type, - reinterpret_cast<void*>( - uintptr_t(cmd->first_index) * - (batch_state_.index_type == GL_UNSIGNED_SHORT ? 2 : 4)), - cmd->instance_count, cmd->base_vertex, cmd->base_instance); - } else { - auto& cmd = active_draw_.draw_arrays_cmd; - glDrawArraysInstancedBaseInstance(prim_type, cmd->first_index, - cmd->count, cmd->instance_count, - cmd->base_instance); - } - } else if (valid_prim) { - // Full multi-draw. - if (batch_state_.indexed) { - glMultiDrawElementsIndirect(prim_type, batch_state_.index_type, - indirect_offset, batch_state_.draw_count, - batch_state_.command_stride); - } else { - glMultiDrawArraysIndirect(prim_type, indirect_offset, - batch_state_.draw_count, - batch_state_.command_stride); - } - } - - if (tfb_enabled_) { - TFBEnd(); - } - - batch_state_.command_range_start = UINTPTR_MAX; - batch_state_.command_range_length = 0; - batch_state_.state_range_start = UINTPTR_MAX; - batch_state_.state_range_length = 0; - batch_state_.draw_count = 0; - } - - if (mode == FlushMode::kReconfigure) { - // Reset - we'll update it as soon as we have all the information. - batch_state_.needs_reconfigure = true; - } - - return true; -} - -void DrawBatcher::CopyConstants() { - // TODO(benvanik): partial updates, etc. We could use shader constant access - // knowledge that we get at compile time to only upload those constants - // required. If we did this as a variable length then we could really cut - // down on state block sizes. - - std::memcpy(active_draw_.header->float_consts, - ®ister_file_->values[XE_GPU_REG_SHADER_CONSTANT_000_X].f32, - sizeof(active_draw_.header->float_consts)); - std::memcpy( - active_draw_.header->bool_consts, - ®ister_file_->values[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].f32, - sizeof(active_draw_.header->bool_consts)); - std::memcpy(active_draw_.header->loop_consts, - ®ister_file_->values[XE_GPU_REG_SHADER_CONSTANT_LOOP_00].f32, - sizeof(active_draw_.header->loop_consts)); -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe diff --git a/src/xenia/gpu/gl4/draw_batcher.h b/src/xenia/gpu/gl4/draw_batcher.h deleted file mode 100644 index fdecfb9da..000000000 --- a/src/xenia/gpu/gl4/draw_batcher.h +++ /dev/null @@ -1,191 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_GPU_GL4_DRAW_BATCHER_H_ -#define XENIA_GPU_GL4_DRAW_BATCHER_H_ - -#include "xenia/gpu/gl4/gl4_shader.h" -#include "xenia/gpu/register_file.h" -#include "xenia/gpu/xenos.h" -#include "xenia/ui/gl/circular_buffer.h" -#include "xenia/ui/gl/gl_context.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -using xe::ui::gl::CircularBuffer; - -union float4 { - float v[4]; - struct { - float x, y, z, w; - }; -}; - -#pragma pack(push, 4) -struct DrawArraysIndirectCommand { - GLuint count; - GLuint instance_count; - GLuint first_index; - GLuint base_instance; -}; -struct DrawElementsIndirectCommand { - GLuint count; - GLuint instance_count; - GLuint first_index; - GLint base_vertex; - GLuint base_instance; -}; -#pragma pack(pop) - -class DrawBatcher { - public: - enum class FlushMode { - kMakeCoherent, - kStateChange, - kReconfigure, - }; - - explicit DrawBatcher(RegisterFile* register_file); - - bool Initialize(CircularBuffer* array_data_buffer); - void Shutdown(); - - PrimitiveType prim_type() const { return batch_state_.prim_type; } - - void set_window_scalar(float width_scalar, float height_scalar) { - active_draw_.header->window_scale.x = width_scalar; - active_draw_.header->window_scale.y = height_scalar; - } - void set_vtx_fmt(float xy, float z, float w) { - active_draw_.header->vtx_fmt.x = xy; - active_draw_.header->vtx_fmt.y = xy; - active_draw_.header->vtx_fmt.z = z; - active_draw_.header->vtx_fmt.w = w; - } - void set_alpha_test(bool enabled, uint32_t func, float ref) { - active_draw_.header->alpha_test.x = enabled ? 1.0f : 0.0f; - active_draw_.header->alpha_test.y = static_cast<float>(func); - active_draw_.header->alpha_test.z = ref; - } - void set_ps_param_gen(int register_index) { - active_draw_.header->ps_param_gen = register_index; - } - void set_texture_sampler(int index, GLuint64 handle, uint32_t swizzle) { - active_draw_.header->texture_samplers[index] = handle; - active_draw_.header->texture_swizzles[index] = swizzle; - } - void set_index_buffer(const CircularBuffer::Allocation& allocation) { - // Offset is used in glDrawElements. - auto& cmd = active_draw_.draw_elements_cmd; - size_t index_size = batch_state_.index_type == GL_UNSIGNED_SHORT ? 2 : 4; - cmd->first_index = GLuint(allocation.offset / index_size); - } - - bool ReconfigurePipeline(GL4Shader* vertex_shader, GL4Shader* pixel_shader, - GLuint pipeline); - - bool BeginDrawArrays(PrimitiveType prim_type, uint32_t index_count); - bool BeginDrawElements(PrimitiveType prim_type, uint32_t index_count, - IndexFormat index_format); - void DiscardDraw(); - bool CommitDraw(); - bool Flush(FlushMode mode); - - // TFB - Filled with vertex shader output from the last flush. - size_t QueryTFBSize(); - bool ReadbackTFB(void* buffer, size_t size); - - GLuint tfvbo() { return tfvbo_; } - bool is_tfb_enabled() const { return tfb_enabled_; } - void set_tfb_enabled(bool enabled) { tfb_enabled_ = enabled; } - - private: - bool InitializeTFB(); - void ShutdownTFB(); - - void TFBBegin(PrimitiveType prim_type); - void TFBEnd(); - - bool BeginDraw(); - void CopyConstants(); - - RegisterFile* register_file_; - CircularBuffer command_buffer_; - CircularBuffer state_buffer_; - CircularBuffer* array_data_buffer_; - - GLuint tfbo_ = 0; - GLuint tfvbo_ = 0; - GLuint tfqo_ = 0; - PrimitiveType tfb_prim_type_ = PrimitiveType::kNone; - GLenum tfb_prim_type_gl_ = 0; - GLint tfb_prim_count_ = 0; - bool tfb_enabled_ = false; - - struct BatchState { - bool needs_reconfigure; - PrimitiveType prim_type; - bool indexed; - GLenum index_type; - - GL4Shader* vertex_shader; - GL4Shader* pixel_shader; - GLuint pipeline; - - GLsizei command_stride; - GLsizei state_stride; - GLsizei float_consts_offset; - GLsizei bool_consts_offset; - GLsizei loop_consts_offset; - - uintptr_t command_range_start; - uintptr_t command_range_length; - uintptr_t state_range_start; - uintptr_t state_range_length; - GLsizei draw_count; - } batch_state_; - - // This must match GL4Shader's header. - struct CommonHeader { - float4 window_scale; // sx,sy, ?, ? - float4 vtx_fmt; // - float4 alpha_test; // alpha test enable, func, ref, ? - int ps_param_gen; - int padding[3]; - - // TODO(benvanik): pack tightly - GLuint64 texture_samplers[32]; - GLuint texture_swizzles[32]; - - float4 float_consts[512]; - uint32_t bool_consts[8]; - uint32_t loop_consts[32]; - }; - struct { - CircularBuffer::Allocation command_allocation; - CircularBuffer::Allocation state_allocation; - - union { - DrawArraysIndirectCommand* draw_arrays_cmd; - DrawElementsIndirectCommand* draw_elements_cmd; - uintptr_t command_address; - }; - - CommonHeader* header; - } active_draw_; - bool draw_open_; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_DRAW_BATCHER_H_ diff --git a/src/xenia/gpu/gl4/gl4_command_processor.cc b/src/xenia/gpu/gl4/gl4_command_processor.cc deleted file mode 100644 index 5392c22d2..000000000 --- a/src/xenia/gpu/gl4/gl4_command_processor.cc +++ /dev/null @@ -1,2131 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/gpu/gl4/gl4_command_processor.h" - -#include <algorithm> - -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/profiling.h" -#include "xenia/gpu/gl4/gl4_gpu_flags.h" -#include "xenia/gpu/gl4/gl4_graphics_system.h" -#include "xenia/gpu/gpu_flags.h" -#include "xenia/gpu/sampler_info.h" -#include "xenia/gpu/texture_info.h" -#include "xenia/gpu/xenos.h" - -#include "third_party/xxhash/xxhash.h" - -DEFINE_bool(draw_all_framebuffers, false, - "Copy all render targets to screen on swap"); - -namespace xe { -namespace gpu { -namespace gl4 { - -using namespace xe::gpu::xenos; - -const GLuint kAnyTarget = UINT_MAX; - -// All uncached vertex/index data goes here. If it fills up we need to sync -// with the GPU, so this should be large enough to prevent that in a normal -// frame. -const size_t kScratchBufferCapacity = 256 * 1024 * 1024; -const size_t kScratchBufferAlignment = 256; - -GL4CommandProcessor::CachedPipeline::CachedPipeline() - : vertex_program(0), fragment_program(0), handles({0}) {} - -GL4CommandProcessor::CachedPipeline::~CachedPipeline() { - glDeleteProgramPipelines(1, &handles.default_pipeline); - glDeleteProgramPipelines(1, &handles.point_list_pipeline); - glDeleteProgramPipelines(1, &handles.rect_list_pipeline); - glDeleteProgramPipelines(1, &handles.quad_list_pipeline); - glDeleteProgramPipelines(1, &handles.line_quad_list_pipeline); -} - -GL4CommandProcessor::GL4CommandProcessor(GL4GraphicsSystem* graphics_system, - kernel::KernelState* kernel_state) - : CommandProcessor(graphics_system, kernel_state), - shader_translator_(GlslShaderTranslator::Dialect::kGL45), - draw_batcher_(graphics_system_->register_file()), - scratch_buffer_(kScratchBufferCapacity, kScratchBufferAlignment), - shader_cache_(&shader_translator_) {} - -GL4CommandProcessor::~GL4CommandProcessor() = default; - -void GL4CommandProcessor::ClearCaches() { - texture_cache()->Clear(); - - for (auto& cached_framebuffer : cached_framebuffers_) { - glDeleteFramebuffers(1, &cached_framebuffer.framebuffer); - } - cached_framebuffers_.clear(); - - for (auto& cached_color_render_target : cached_color_render_targets_) { - glDeleteTextures(1, &cached_color_render_target.texture); - } - cached_color_render_targets_.clear(); - - for (auto& cached_depth_render_target : cached_depth_render_targets_) { - glDeleteTextures(1, &cached_depth_render_target.texture); - } - cached_depth_render_targets_.clear(); - - CommandProcessor::ClearCaches(); -} - -bool GL4CommandProcessor::SetupContext() { - if (!CommandProcessor::SetupContext()) { - XELOGE("Unable to initialize base command processor context"); - return false; - } - - // Circular buffer holding scratch vertex/index data. - if (!scratch_buffer_.Initialize()) { - XELOGE("Unable to initialize scratch buffer"); - return false; - } - - // Command buffer. - if (!draw_batcher_.Initialize(&scratch_buffer_)) { - XELOGE("Unable to initialize command buffer"); - return false; - } - - // Texture cache that keeps track of any textures/samplers used. - if (!texture_cache_.Initialize(memory_, &scratch_buffer_)) { - XELOGE("Unable to initialize texture cache"); - return false; - } - - const std::string geometry_header = - "#version 450\n" - "#extension all : warn\n" - "#extension GL_ARB_explicit_uniform_location : require\n" - "#extension GL_ARB_shading_language_420pack : require\n" - "in gl_PerVertex {\n" - " vec4 gl_Position;\n" - " float gl_PointSize;\n" - " float gl_ClipDistance[];\n" - "} gl_in[];\n" - "out gl_PerVertex {\n" - " vec4 gl_Position;\n" - " float gl_PointSize;\n" - " float gl_ClipDistance[];\n" - "};\n" - "struct VertexData {\n" - " vec4 o[16];\n" - "};\n" - "\n" - "layout(location = 1) in VertexData in_vtx[];\n" - "layout(location = 1) out VertexData out_vtx;\n"; - // 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. - std::string point_list_shader = - geometry_header + - "layout(points) in;\n" - "layout(triangle_strip, max_vertices = 4) out;\n" - "void main() {\n" - " const vec2 offsets[4] = {\n" - " vec2(-1.0, 1.0),\n" - " vec2( 1.0, 1.0),\n" - " vec2(-1.0, -1.0),\n" - " vec2( 1.0, -1.0),\n" - " };\n" - " vec4 pos = gl_in[0].gl_Position;\n" - " float psize = gl_in[0].gl_PointSize;\n" - " for (int i = 0; i < 4; ++i) {\n" - " gl_Position = vec4(pos.xy + offsets[i] * psize, pos.zw);\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " }\n" - " EndPrimitive();\n" - "}\n"; - std::string rect_list_shader = - geometry_header + - "layout(triangles) in;\n" - "layout(triangle_strip, max_vertices = 6) out;\n" - "void main() {\n" - // Most games use the left-aligned form. - " bool left_aligned = gl_in[0].gl_Position.x == \n" - " gl_in[2].gl_Position.x;\n" - " if (left_aligned) {\n" - // 0 ------ 1 - // | - | - // | // | - // | - | - // 2 ----- [3] - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " gl_Position = gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[1].gl_PointSize;\n" - " out_vtx = in_vtx[1];\n" - " EmitVertex();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " EndPrimitive();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " gl_Position = gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[1].gl_PointSize;\n" - " out_vtx = in_vtx[1];\n" - " EmitVertex();\n" - " gl_Position = \n" - " (gl_in[1].gl_Position + gl_in[2].gl_Position) - \n" - " gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " for (int i = 0; i < 16; ++i) {\n" - " out_vtx.o[i] = -in_vtx[0].o[i] + in_vtx[1].o[i] + \n" - " in_vtx[2].o[i];\n" - " }\n" - " EmitVertex();\n" - " EndPrimitive();\n" - " } else {\n" - // 0 ------ 1 - // | - | - // | \\ | - // | - | - // [3] ----- 2 - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " gl_Position = gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[1].gl_PointSize;\n" - " out_vtx = in_vtx[1];\n" - " EmitVertex();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " EndPrimitive();\n" - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " gl_Position = (gl_in[0].gl_Position + gl_in[2].gl_Position) - \n" - " gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " for (int i = 0; i < 16; ++i) {\n" - " out_vtx.o[i] = in_vtx[0].o[i] + -in_vtx[1].o[i] + \n" - " in_vtx[2].o[i];\n" - " }\n" - " EmitVertex();\n" - " EndPrimitive();\n" - " }\n" - "}\n"; - std::string quad_list_shader = - geometry_header + - "layout(lines_adjacency) in;\n" - "layout(triangle_strip, max_vertices = 4) out;\n" - "void main() {\n" - " const int order[4] = { 0, 1, 3, 2 };\n" - " for (int i = 0; i < 4; ++i) {\n" - " int input_index = order[i];\n" - " gl_Position = gl_in[input_index].gl_Position;\n" - " gl_PointSize = gl_in[input_index].gl_PointSize;\n" - " out_vtx = in_vtx[input_index];\n" - " EmitVertex();\n" - " }\n" - " EndPrimitive();\n" - "}\n"; - std::string line_quad_list_shader = - geometry_header + - "layout(lines_adjacency) in;\n" - "layout(line_strip, max_vertices = 5) out;\n" - "void main() {\n" - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " gl_Position = gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[1].gl_PointSize;\n" - " out_vtx = in_vtx[1];\n" - " EmitVertex();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " gl_Position = gl_in[3].gl_Position;\n" - " gl_PointSize = gl_in[3].gl_PointSize;\n" - " out_vtx = in_vtx[3];\n" - " EmitVertex();\n" - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " EndPrimitive();\n" - "}\n"; - point_list_geometry_program_ = CreateGeometryProgram(point_list_shader); - rect_list_geometry_program_ = CreateGeometryProgram(rect_list_shader); - quad_list_geometry_program_ = CreateGeometryProgram(quad_list_shader); - line_quad_list_geometry_program_ = - CreateGeometryProgram(line_quad_list_shader); - if (!point_list_geometry_program_ || !rect_list_geometry_program_ || - !quad_list_geometry_program_ || !line_quad_list_geometry_program_) { - return false; - } - - glEnable(GL_SCISSOR_TEST); - glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); - glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_UPPER_LEFT); - - return true; -} - -GLuint GL4CommandProcessor::CreateGeometryProgram(const std::string& source) { - auto source_str = source.c_str(); - GLuint program = glCreateShaderProgramv(GL_GEOMETRY_SHADER, 1, &source_str); - - // Get error log, if we failed to link. - GLint link_status = 0; - glGetProgramiv(program, GL_LINK_STATUS, &link_status); - if (!link_status) { - GLint log_length = 0; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); - std::string info_log; - info_log.resize(log_length - 1); - glGetProgramInfoLog(program, log_length, &log_length, - const_cast<char*>(info_log.data())); - XELOGE("Unable to link program: %s", info_log.c_str()); - glDeleteProgram(program); - return 0; - } - - return program; -} - -void GL4CommandProcessor::ShutdownContext() { - glDeleteProgram(point_list_geometry_program_); - glDeleteProgram(rect_list_geometry_program_); - glDeleteProgram(quad_list_geometry_program_); - glDeleteProgram(line_quad_list_geometry_program_); - texture_cache_.Shutdown(); - draw_batcher_.Shutdown(); - scratch_buffer_.Shutdown(); - - all_pipelines_.clear(); - shader_cache_.Reset(); - - CommandProcessor::ShutdownContext(); -} - -void GL4CommandProcessor::MakeCoherent() { - RegisterFile* regs = register_file_; - auto status_host = regs->values[XE_GPU_REG_COHER_STATUS_HOST].u32; - - CommandProcessor::MakeCoherent(); - - if (status_host & 0x80000000ul) { - scratch_buffer_.ClearCache(); - } -} - -void GL4CommandProcessor::PrepareForWait() { - SCOPE_profile_cpu_f("gpu"); - - CommandProcessor::PrepareForWait(); - - // TODO(benvanik): fences and fancy stuff. We should figure out a way to - // make interrupt callbacks from the GPU so that we don't have to do a full - // synchronize here. - glFlush(); - // glFinish(); - - if (FLAGS_thread_safe_gl) { - context_->ClearCurrent(); - } -} - -void GL4CommandProcessor::ReturnFromWait() { - if (FLAGS_thread_safe_gl) { - context_->MakeCurrent(); - } - - CommandProcessor::ReturnFromWait(); -} - -void GL4CommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, - uint32_t frontbuffer_width, - uint32_t frontbuffer_height) { - // Ensure we issue any pending draws. - draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent); - - // One-time initialization. - // TODO(benvanik): move someplace more sane? - if (!swap_state_.front_buffer_texture) { - std::lock_guard<std::mutex> lock(swap_state_.mutex); - swap_state_.width = frontbuffer_width; - swap_state_.height = frontbuffer_height; - GLuint front_buffer_texture; - GLuint back_buffer_texture; - glCreateTextures(GL_TEXTURE_2D, 1, &front_buffer_texture); - glCreateTextures(GL_TEXTURE_2D, 1, &back_buffer_texture); - swap_state_.front_buffer_texture = front_buffer_texture; - swap_state_.back_buffer_texture = back_buffer_texture; - glTextureStorage2D(front_buffer_texture, 1, GL_RGBA8, swap_state_.width, - swap_state_.height); - glTextureStorage2D(back_buffer_texture, 1, GL_RGBA8, swap_state_.width, - swap_state_.height); - } - - // Lookup the framebuffer in the recently-resolved list. - // TODO(benvanik): make this much more sophisticated. - // TODO(benvanik): handle not found cases. - // TODO(benvanik): handle dirty cases (resolved to sysmem, touched). - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // HACK: just use whatever our current framebuffer is. - GLuint framebuffer_texture = last_framebuffer_texture_; - - if (last_framebuffer_texture_ == 0) { - framebuffer_texture = - active_framebuffer_ ? active_framebuffer_->color_targets[0] : 0; - } - - // Copy the the given framebuffer to the current backbuffer. - Rect2D src_rect(0, 0, frontbuffer_width ? frontbuffer_width : 1280, - frontbuffer_height ? frontbuffer_height : 720); - Rect2D dest_rect(0, 0, swap_state_.width, swap_state_.height); - if (framebuffer_texture != 0) { - reinterpret_cast<xe::ui::gl::GLContext*>(context_.get()) - ->blitter() - ->CopyColorTexture2D( - framebuffer_texture, src_rect, - static_cast<GLuint>(swap_state_.back_buffer_texture), dest_rect, - GL_LINEAR, true); - } - - if (FLAGS_draw_all_framebuffers) { - int32_t offsetx = (1280 - (1280 / 5)); - int32_t offsety = 0; - int32_t doffsetx = 0; - for (int i = 0; i < cached_framebuffers_.size(); i++) { - bool has_colortargets = false; - - // Copy color targets to top right corner - for (int j = 0; j < 4; j++) { - GLuint tex = cached_framebuffers_[i].color_targets[j]; - if (!tex) { - continue; - } - has_colortargets = true; - - dest_rect = {offsetx, offsety, 1280 / 5, 720 / 5}; - reinterpret_cast<ui::gl::GLContext*>(context_.get()) - ->blitter() - ->CopyColorTexture2D( - tex, src_rect, - static_cast<GLuint>(swap_state_.back_buffer_texture), dest_rect, - GL_LINEAR, true); - - offsety += 720 / 5; - } - - if (has_colortargets) { - offsetx -= 1280 / 5; - } - - offsety = 0; - - GLuint tex = cached_framebuffers_[i].depth_target; - if (!tex) { - continue; - } - - // Copy depth targets to bottom left corner of screen - dest_rect = {doffsetx, (int32_t)swap_state_.height - (720 / 5), 1280 / 5, - 720 / 5}; - reinterpret_cast<ui::gl::GLContext*>(context_.get()) - ->blitter() - ->CopyColorTexture2D( - tex, src_rect, - static_cast<GLuint>(swap_state_.back_buffer_texture), dest_rect, - GL_LINEAR, false); - - doffsetx += 1280 / 5; - } - } - - // Need to finish to be sure the other context sees the right data. - // TODO(benvanik): prevent this? fences? - glFinish(); - - if (context_->WasLost()) { - // We've lost the context due to a TDR. - // TODO: Dump the current commands to a tracefile. - assert_always(); - } - - // Remove any dead textures, etc. - texture_cache_.Scavenge(); -} - -Shader* GL4CommandProcessor::LoadShader(ShaderType shader_type, - uint32_t guest_address, - const uint32_t* host_address, - uint32_t dword_count) { - return shader_cache_.LookupOrInsertShader(shader_type, host_address, - dword_count); -} - -bool GL4CommandProcessor::IssueDraw(PrimitiveType prim_type, - uint32_t index_count, - IndexBufferInfo* index_buffer_info) { -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - bool draw_valid; - if (index_buffer_info) { - draw_valid = draw_batcher_.BeginDrawElements(prim_type, index_count, - index_buffer_info->format); - } else { - draw_valid = draw_batcher_.BeginDrawArrays(prim_type, index_count); - } - if (!draw_valid) { - return false; - } - - auto& regs = *register_file_; - - auto enable_mode = - static_cast<ModeControl>(regs[XE_GPU_REG_RB_MODECONTROL].u32 & 0x7); - if (enable_mode == ModeControl::kIgnore) { - // Ignored. - draw_batcher_.DiscardDraw(); - return true; - } else if (enable_mode == ModeControl::kCopy) { - // Special copy handling. - draw_batcher_.DiscardDraw(); - return IssueCopy(); - } - -#define CHECK_ISSUE_UPDATE_STATUS(status, mismatch, error_message) \ - { \ - if (status == UpdateStatus::kError) { \ - XELOGE(error_message); \ - draw_batcher_.DiscardDraw(); \ - return false; \ - } else if (status == UpdateStatus::kMismatch) { \ - mismatch = true; \ - } \ - } - - UpdateStatus status; - bool mismatch = false; - status = UpdateShaders(draw_batcher_.prim_type()); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to prepare draw shaders"); - status = UpdateRenderTargets(); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup render targets"); - if (!active_framebuffer_) { - // No framebuffer, so nothing we do will actually have an effect. - // Treat it as a no-op. - // TODO(benvanik): if we have a vs export, still allow it to go. - draw_batcher_.DiscardDraw(); - return true; - } - - status = UpdateState(draw_batcher_.prim_type()); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup render state"); - status = PopulateSamplers(); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, - "Unable to prepare draw samplers"); - - status = PopulateIndexBuffer(index_buffer_info); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup index buffer"); - status = PopulateVertexBuffers(); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup vertex buffers"); - - if (!draw_batcher_.CommitDraw()) { - return false; - } - - // TODO(benvanik): find a way to get around glVertexArrayVertexBuffer below. - draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent); - if (context_->WasLost()) { - // This draw lost us the context. This typically isn't hit. - assert_always(); - return false; - } - - return true; -} - -bool GL4CommandProcessor::SetShadowRegister(uint32_t* dest, - uint32_t register_name) { - uint32_t value = register_file_->values[register_name].u32; - if (*dest == value) { - return false; - } - *dest = value; - return true; -} - -bool GL4CommandProcessor::SetShadowRegister(float* dest, - uint32_t register_name) { - float value = register_file_->values[register_name].f32; - if (*dest == value) { - return false; - } - *dest = value; - return true; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateShaders( - PrimitiveType prim_type) { - auto& regs = update_shaders_regs_; - - // These are the constant base addresses/ranges for shaders. - // We have these hardcoded right now cause nothing seems to differ. - assert_true(register_file_->values[XE_GPU_REG_SQ_VS_CONST].u32 == - 0x000FF000 || - register_file_->values[XE_GPU_REG_SQ_VS_CONST].u32 == 0x00000000); - assert_true(register_file_->values[XE_GPU_REG_SQ_PS_CONST].u32 == - 0x000FF100 || - register_file_->values[XE_GPU_REG_SQ_PS_CONST].u32 == 0x00000000); - - bool dirty = false; - dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, - XE_GPU_REG_PA_SU_SC_MODE_CNTL); - dirty |= SetShadowRegister(®s.sq_program_cntl, XE_GPU_REG_SQ_PROGRAM_CNTL); - dirty |= SetShadowRegister(®s.sq_context_misc, XE_GPU_REG_SQ_CONTEXT_MISC); - dirty |= regs.vertex_shader != active_vertex_shader_; - dirty |= regs.pixel_shader != active_pixel_shader_; - dirty |= regs.prim_type != prim_type; - if (!dirty) { - return UpdateStatus::kCompatible; - } - regs.vertex_shader = static_cast<GL4Shader*>(active_vertex_shader_); - regs.pixel_shader = static_cast<GL4Shader*>(active_pixel_shader_); - regs.prim_type = prim_type; - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - xe_gpu_program_cntl_t program_cntl; - program_cntl.dword_0 = regs.sq_program_cntl; - - // Populate a register in the pixel shader with frag coord. - int ps_param_gen = (regs.sq_context_misc >> 8) & 0xFF; - draw_batcher_.set_ps_param_gen(program_cntl.param_gen ? ps_param_gen : -1); - - // Normal vertex shaders only, for now. - // TODO(benvanik): transform feedback/memexport. - // https://github.com/freedreno/freedreno/blob/master/includes/a2xx.xml.h - // 0 = normal - // 2 = point size - assert_true(program_cntl.vs_export_mode == 0 || - program_cntl.vs_export_mode == 2); - - if (!regs.vertex_shader->is_valid()) { - XELOGE("Vertex shader invalid"); - return UpdateStatus::kError; - } - if (!regs.pixel_shader->is_valid()) { - XELOGE("Pixel shader invalid"); - return UpdateStatus::kError; - } - - GLuint vertex_program = regs.vertex_shader->program(); - GLuint fragment_program = regs.pixel_shader->program(); - - uint64_t key = (uint64_t(vertex_program) << 32) | fragment_program; - CachedPipeline* cached_pipeline = nullptr; - auto it = cached_pipelines_.find(key); - if (it == cached_pipelines_.end()) { - // Existing pipeline for these programs not found - create it. - auto new_pipeline = std::make_unique<CachedPipeline>(); - new_pipeline->vertex_program = vertex_program; - new_pipeline->fragment_program = fragment_program; - new_pipeline->handles.default_pipeline = 0; - cached_pipeline = new_pipeline.get(); - all_pipelines_.emplace_back(std::move(new_pipeline)); - cached_pipelines_.insert({key, cached_pipeline}); - } else { - // Found a pipeline container - it may or may not have what we want. - cached_pipeline = it->second; - } - if (!cached_pipeline->handles.default_pipeline) { - // Perhaps it's a bit wasteful to do all of these, but oh well. - GLuint pipelines[5]; - glCreateProgramPipelines(GLsizei(xe::countof(pipelines)), pipelines); - - glUseProgramStages(pipelines[0], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[0], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.default_pipeline = pipelines[0]; - - glUseProgramStages(pipelines[1], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[1], GL_GEOMETRY_SHADER_BIT, - point_list_geometry_program_); - glUseProgramStages(pipelines[1], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.point_list_pipeline = pipelines[1]; - - glUseProgramStages(pipelines[2], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[2], GL_GEOMETRY_SHADER_BIT, - rect_list_geometry_program_); - glUseProgramStages(pipelines[2], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.rect_list_pipeline = pipelines[2]; - - glUseProgramStages(pipelines[3], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[3], GL_GEOMETRY_SHADER_BIT, - quad_list_geometry_program_); - glUseProgramStages(pipelines[3], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.quad_list_pipeline = pipelines[3]; - - glUseProgramStages(pipelines[4], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[4], GL_GEOMETRY_SHADER_BIT, - line_quad_list_geometry_program_); - glUseProgramStages(pipelines[4], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.line_quad_list_pipeline = pipelines[4]; - - // This can be set once, as the buffer never changes. - glVertexArrayElementBuffer(regs.vertex_shader->vao(), - scratch_buffer_.handle()); - } - - bool line_mode = false; - if (((regs.pa_su_sc_mode_cntl >> 3) & 0x3) != 0) { - uint32_t front_poly_mode = (regs.pa_su_sc_mode_cntl >> 5) & 0x7; - if (front_poly_mode == 1) { - line_mode = true; - } - } - - GLuint pipeline; - switch (regs.prim_type) { - default: - // Default pipeline used. - pipeline = cached_pipeline->handles.default_pipeline; - break; - case PrimitiveType::kPointList: - pipeline = cached_pipeline->handles.point_list_pipeline; - break; - case PrimitiveType::kRectangleList: - pipeline = cached_pipeline->handles.rect_list_pipeline; - break; - case PrimitiveType::kQuadList: { - if (line_mode) { - pipeline = cached_pipeline->handles.line_quad_list_pipeline; - } else { - pipeline = cached_pipeline->handles.quad_list_pipeline; - } - break; - } - } - - draw_batcher_.ReconfigurePipeline(regs.vertex_shader, regs.pixel_shader, - pipeline); - - glBindProgramPipeline(pipeline); - glBindVertexArray(regs.vertex_shader->vao()); - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRenderTargets() { - auto& regs = update_render_targets_regs_; - - bool dirty = false; - dirty |= SetShadowRegister(®s.rb_modecontrol, XE_GPU_REG_RB_MODECONTROL); - dirty |= SetShadowRegister(®s.rb_surface_info, XE_GPU_REG_RB_SURFACE_INFO); - dirty |= SetShadowRegister(®s.rb_color_info, XE_GPU_REG_RB_COLOR_INFO); - dirty |= SetShadowRegister(®s.rb_color1_info, XE_GPU_REG_RB_COLOR1_INFO); - dirty |= SetShadowRegister(®s.rb_color2_info, XE_GPU_REG_RB_COLOR2_INFO); - dirty |= SetShadowRegister(®s.rb_color3_info, XE_GPU_REG_RB_COLOR3_INFO); - dirty |= SetShadowRegister(®s.rb_color_mask, XE_GPU_REG_RB_COLOR_MASK); - dirty |= SetShadowRegister(®s.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL); - dirty |= - SetShadowRegister(®s.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK); - dirty |= SetShadowRegister(®s.rb_depth_info, XE_GPU_REG_RB_DEPTH_INFO); - if (!dirty) { - return UpdateStatus::kCompatible; - } - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - auto enable_mode = static_cast<ModeControl>(regs.rb_modecontrol & 0x7); - - // RB_SURFACE_INFO - // http://fossies.org/dox/MesaLib-10.3.5/fd2__gmem_8c_source.html - uint32_t surface_pitch = regs.rb_surface_info & 0x3FFF; - auto surface_msaa = - static_cast<MsaaSamples>((regs.rb_surface_info >> 16) & 0x3); - - // Get/create all color render targets, if we are using them. - // In depth-only mode we don't need them. - // Note that write mask may be more permissive than we want, so we mix that - // with the actual targets the pixel shader writes to. - GLenum draw_buffers[4] = {GL_NONE, GL_NONE, GL_NONE, GL_NONE}; - GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget}; - if (enable_mode == ModeControl::kColorDepth) { - uint32_t color_info[4] = { - regs.rb_color_info, - regs.rb_color1_info, - regs.rb_color2_info, - regs.rb_color3_info, - }; - // A2XX_RB_COLOR_MASK_WRITE_* == D3DRS_COLORWRITEENABLE - for (int n = 0; n < xe::countof(color_info); n++) { - uint32_t write_mask = (regs.rb_color_mask >> (n * 4)) & 0xF; - if (!write_mask || !active_pixel_shader_->writes_color_target(n)) { - // Unused, so keep disabled and set to wildcard so we'll take any - // framebuffer that has it. - continue; - } - uint32_t color_base = color_info[n] & 0xFFF; - auto color_format = - static_cast<ColorRenderTargetFormat>((color_info[n] >> 16) & 0xF); - color_targets[n] = GetColorRenderTarget(surface_pitch, surface_msaa, - color_base, color_format); - draw_buffers[n] = GL_COLOR_ATTACHMENT0 + n; - glColorMaski(n, !!(write_mask & 0x1), !!(write_mask & 0x2), - !!(write_mask & 0x4), !!(write_mask & 0x8)); - } - } - - // Get/create depth buffer, but only if we are going to use it. - bool uses_depth = (regs.rb_depthcontrol & 0x00000002) || - (regs.rb_depthcontrol & 0x00000004); - uint32_t stencil_write_mask = (regs.rb_stencilrefmask & 0x00FF0000) >> 16; - bool uses_stencil = - (regs.rb_depthcontrol & 0x00000001) || (stencil_write_mask != 0); - GLuint depth_target = kAnyTarget; - if (uses_depth || uses_stencil) { - uint32_t depth_base = regs.rb_depth_info & 0xFFF; - auto depth_format = - static_cast<DepthRenderTargetFormat>((regs.rb_depth_info >> 16) & 0x1); - depth_target = GetDepthRenderTarget(surface_pitch, surface_msaa, depth_base, - depth_format); - // TODO(benvanik): when a game switches does it expect to keep the same - // depth buffer contents? - } - - // Get/create a framebuffer with the required targets. - // Note that none may be returned if we really don't need one. - auto cached_framebuffer = GetFramebuffer(color_targets, depth_target); - active_framebuffer_ = cached_framebuffer; - if (active_framebuffer_) { - // Setup just the targets we want. - glNamedFramebufferDrawBuffers(cached_framebuffer->framebuffer, 4, - draw_buffers); - - // Make active. - // TODO(benvanik): can we do this all named? - // TODO(benvanik): do we want this on READ too? - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cached_framebuffer->framebuffer); - } - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateState( - PrimitiveType prim_type) { - bool mismatch = false; - -#define CHECK_UPDATE_STATUS(status, mismatch, error_message) \ - { \ - if (status == UpdateStatus::kError) { \ - XELOGE(error_message); \ - return status; \ - } else if (status == UpdateStatus::kMismatch) { \ - mismatch = true; \ - } \ - } - - UpdateStatus status; - status = UpdateViewportState(); - CHECK_UPDATE_STATUS(status, mismatch, "Unable to update viewport state"); - status = UpdateRasterizerState(prim_type); - CHECK_UPDATE_STATUS(status, mismatch, "Unable to update rasterizer state"); - status = UpdateBlendState(); - CHECK_UPDATE_STATUS(status, mismatch, "Unable to update blend state"); - status = UpdateDepthStencilState(); - CHECK_UPDATE_STATUS(status, mismatch, "Unable to update depth/stencil state"); - - return mismatch ? UpdateStatus::kMismatch : UpdateStatus::kCompatible; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateViewportState() { - auto& regs = update_viewport_state_regs_; - - bool dirty = false; - // dirty |= SetShadowRegister(&state_regs.pa_cl_clip_cntl, - // XE_GPU_REG_PA_CL_CLIP_CNTL); - dirty |= SetShadowRegister(®s.rb_surface_info, XE_GPU_REG_RB_SURFACE_INFO); - dirty |= SetShadowRegister(®s.pa_cl_vte_cntl, XE_GPU_REG_PA_CL_VTE_CNTL); - dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, - XE_GPU_REG_PA_SU_SC_MODE_CNTL); - dirty |= SetShadowRegister(®s.pa_sc_window_offset, - XE_GPU_REG_PA_SC_WINDOW_OFFSET); - dirty |= SetShadowRegister(®s.pa_sc_window_scissor_tl, - XE_GPU_REG_PA_SC_WINDOW_SCISSOR_TL); - dirty |= SetShadowRegister(®s.pa_sc_window_scissor_br, - XE_GPU_REG_PA_SC_WINDOW_SCISSOR_BR); - dirty |= SetShadowRegister(®s.pa_cl_vport_xoffset, - XE_GPU_REG_PA_CL_VPORT_XOFFSET); - dirty |= SetShadowRegister(®s.pa_cl_vport_yoffset, - XE_GPU_REG_PA_CL_VPORT_YOFFSET); - dirty |= SetShadowRegister(®s.pa_cl_vport_zoffset, - XE_GPU_REG_PA_CL_VPORT_ZOFFSET); - dirty |= SetShadowRegister(®s.pa_cl_vport_xscale, - XE_GPU_REG_PA_CL_VPORT_XSCALE); - dirty |= SetShadowRegister(®s.pa_cl_vport_yscale, - XE_GPU_REG_PA_CL_VPORT_YSCALE); - dirty |= SetShadowRegister(®s.pa_cl_vport_zscale, - XE_GPU_REG_PA_CL_VPORT_ZSCALE); - - // Much of this state machine is extracted from: - // https://github.com/freedreno/mesa/blob/master/src/mesa/drivers/dri/r200/r200_state.c - // http://fossies.org/dox/MesaLib-10.3.5/fd2__gmem_8c_source.html - // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf - - // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf - // VTX_XY_FMT = true: the incoming X, Y have already been multiplied by 1/W0. - // = false: multiply the X, Y coordinates by 1/W0. - // VTX_Z_FMT = true: the incoming Z has already been multiplied by 1/W0. - // = false: multiply the Z coordinate by 1/W0. - // VTX_W0_FMT = true: the incoming W0 is not 1/W0. Perform the reciprocal to - // get 1/W0. - draw_batcher_.set_vtx_fmt((regs.pa_cl_vte_cntl >> 8) & 0x1 ? 1.0f : 0.0f, - (regs.pa_cl_vte_cntl >> 9) & 0x1 ? 1.0f : 0.0f, - (regs.pa_cl_vte_cntl >> 10) & 0x1 ? 1.0f : 0.0f); - - // Done in VS, no need to flush state. - if ((regs.pa_cl_vte_cntl & (1 << 0)) > 0) { - draw_batcher_.set_window_scalar(1.0f, 1.0f); - } else { - draw_batcher_.set_window_scalar(1.0f / 2560.0f, -1.0f / 2560.0f); - } - - if (!dirty) { - return UpdateStatus::kCompatible; - } - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - // Clipping. - // https://github.com/freedreno/amd-gpu/blob/master/include/reg/yamato/14/yamato_genenum.h#L1587 - // bool clip_enabled = ((regs.pa_cl_clip_cntl >> 17) & 0x1) == 0; - // bool dx_clip = ((regs.pa_cl_clip_cntl >> 19) & 0x1) == 0x1; - //// TODO(benvanik): depth range? - // if (dx_clip) { - // glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); - //} else { - // glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE); - //} - - // Window parameters. - // http://ftp.tku.edu.tw/NetBSD/NetBSD-current/xsrc/external/mit/xf86-video-ati/dist/src/r600_reg_auto_r6xx.h - // See r200UpdateWindow: - // https://github.com/freedreno/mesa/blob/master/src/mesa/drivers/dri/r200/r200_state.c - int16_t window_offset_x = 0; - int16_t window_offset_y = 0; - if ((regs.pa_su_sc_mode_cntl >> 16) & 1) { - window_offset_x = regs.pa_sc_window_offset & 0x7FFF; - window_offset_y = (regs.pa_sc_window_offset >> 16) & 0x7FFF; - if (window_offset_x & 0x4000) { - window_offset_x |= 0x8000; - } - if (window_offset_y & 0x4000) { - window_offset_y |= 0x8000; - } - } - - GLint ws_x = regs.pa_sc_window_scissor_tl & 0x7FFF; - GLint ws_y = (regs.pa_sc_window_scissor_tl >> 16) & 0x7FFF; - GLsizei ws_w = (regs.pa_sc_window_scissor_br & 0x7FFF) - ws_x; - GLsizei ws_h = ((regs.pa_sc_window_scissor_br >> 16) & 0x7FFF) - ws_y; - ws_x += window_offset_x; - ws_y += window_offset_y; - glScissorIndexed(0, ws_x, ws_y, ws_w, ws_h); - - // HACK: no clue where to get these values. - // RB_SURFACE_INFO - auto surface_msaa = - static_cast<MsaaSamples>((regs.rb_surface_info >> 16) & 0x3); - // TODO(benvanik): ?? - float window_width_scalar = 1; - float window_height_scalar = 1; - switch (surface_msaa) { - case MsaaSamples::k1X: - break; - case MsaaSamples::k2X: - window_width_scalar = 2; - break; - case MsaaSamples::k4X: - window_width_scalar = 2; - window_height_scalar = 2; - break; - } - - // Whether each of the viewport settings are enabled. - // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf - bool vport_xscale_enable = (regs.pa_cl_vte_cntl & (1 << 0)) > 0; - bool vport_xoffset_enable = (regs.pa_cl_vte_cntl & (1 << 1)) > 0; - bool vport_yscale_enable = (regs.pa_cl_vte_cntl & (1 << 2)) > 0; - bool vport_yoffset_enable = (regs.pa_cl_vte_cntl & (1 << 3)) > 0; - bool vport_zscale_enable = (regs.pa_cl_vte_cntl & (1 << 4)) > 0; - bool vport_zoffset_enable = (regs.pa_cl_vte_cntl & (1 << 5)) > 0; - assert_true(vport_xscale_enable == vport_yscale_enable == - vport_zscale_enable == vport_xoffset_enable == - vport_yoffset_enable == vport_zoffset_enable); - - if (vport_xscale_enable) { - float texel_offset_x = 0.0f; - float texel_offset_y = 0.0f; - float vox = vport_xoffset_enable ? regs.pa_cl_vport_xoffset : 0; - float voy = vport_yoffset_enable ? regs.pa_cl_vport_yoffset : 0; - float vsx = vport_xscale_enable ? regs.pa_cl_vport_xscale : 1; - float vsy = vport_yscale_enable ? regs.pa_cl_vport_yscale : 1; - window_width_scalar = window_height_scalar = 1; - float vpw = 2 * window_width_scalar * vsx; - float vph = -2 * window_height_scalar * vsy; - float vpx = window_width_scalar * vox - vpw / 2 + window_offset_x; - float vpy = window_height_scalar * voy - vph / 2 + window_offset_y; - glViewportIndexedf(0, vpx + texel_offset_x, vpy + texel_offset_y, vpw, vph); - - // TODO(benvanik): depth range adjustment? - // float voz = vport_zoffset_enable ? regs.pa_cl_vport_zoffset : 0; - // float vsz = vport_zscale_enable ? regs.pa_cl_vport_zscale : 1; - } else { - float texel_offset_x = 0.0f; - float texel_offset_y = 0.0f; - float vpw = 2 * 2560.0f * window_width_scalar; - float vph = 2 * 2560.0f * window_height_scalar; - float vpx = -2560.0f * window_width_scalar + window_offset_x; - float vpy = -2560.0f * window_height_scalar + window_offset_y; - glViewportIndexedf(0, vpx + texel_offset_x, vpy + texel_offset_y, vpw, vph); - } - float voz = vport_zoffset_enable ? regs.pa_cl_vport_zoffset : 0; - float vsz = vport_zscale_enable ? regs.pa_cl_vport_zscale : 1; - glDepthRangef(voz, voz + vsz); - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRasterizerState( - PrimitiveType prim_type) { - auto& regs = update_rasterizer_state_regs_; - - bool dirty = false; - dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, - XE_GPU_REG_PA_SU_SC_MODE_CNTL); - dirty |= SetShadowRegister(®s.pa_sc_screen_scissor_tl, - XE_GPU_REG_PA_SC_SCREEN_SCISSOR_TL); - dirty |= SetShadowRegister(®s.pa_sc_screen_scissor_br, - XE_GPU_REG_PA_SC_SCREEN_SCISSOR_BR); - dirty |= SetShadowRegister(®s.multi_prim_ib_reset_index, - XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX); - dirty |= SetShadowRegister(®s.pa_sc_viz_query, XE_GPU_REG_PA_SC_VIZ_QUERY); - dirty |= regs.prim_type != prim_type; - if (!dirty) { - return UpdateStatus::kCompatible; - } - - regs.prim_type = prim_type; - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - // viz query enabled - // assert_zero(regs.pa_sc_viz_query & 0x01); - - // Kill pix post early-z test - // assert_zero(regs.pa_sc_viz_query & 0x80); - - // Scissoring. - // TODO(benvanik): is this used? we are using scissoring for window scissor. - if (regs.pa_sc_screen_scissor_tl != 0 && - regs.pa_sc_screen_scissor_br != 0x20002000) { - assert_always(); - // glEnable(GL_SCISSOR_TEST); - // TODO(benvanik): signed? - int32_t screen_scissor_x = regs.pa_sc_screen_scissor_tl & 0x7FFF; - int32_t screen_scissor_y = (regs.pa_sc_screen_scissor_tl >> 16) & 0x7FFF; - int32_t screen_scissor_w = - regs.pa_sc_screen_scissor_br & 0x7FFF - screen_scissor_x; - int32_t screen_scissor_h = - (regs.pa_sc_screen_scissor_br >> 16) & 0x7FFF - screen_scissor_y; - glScissor(screen_scissor_x, screen_scissor_y, screen_scissor_w, - screen_scissor_h); - } else { - // glDisable(GL_SCISSOR_TEST); - } - - switch (regs.pa_su_sc_mode_cntl & 0x3) { - case 0: - glDisable(GL_CULL_FACE); - break; - case 1: - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - break; - case 2: - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - break; - } - if (regs.pa_su_sc_mode_cntl & 0x4) { - glFrontFace(GL_CW); - } else { - glFrontFace(GL_CCW); - } - - if (prim_type == PrimitiveType::kRectangleList) { - // Rectangle lists aren't culled. There may be other things they skip too. - glDisable(GL_CULL_FACE); - } - - static const GLenum kFillModes[3] = { - GL_POINT, - GL_LINE, - GL_FILL, - }; - bool poly_mode = ((regs.pa_su_sc_mode_cntl >> 3) & 0x3) != 0; - if (poly_mode) { - uint32_t front_poly_mode = (regs.pa_su_sc_mode_cntl >> 5) & 0x7; - uint32_t back_poly_mode = (regs.pa_su_sc_mode_cntl >> 8) & 0x7; - // GL only supports both matching. - assert_true(front_poly_mode == back_poly_mode); - glPolygonMode(GL_FRONT_AND_BACK, kFillModes[front_poly_mode]); - } else { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } - - if (regs.pa_su_sc_mode_cntl & (1 << 19)) { - glProvokingVertex(GL_LAST_VERTEX_CONVENTION); - } else { - glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); - } - - if (regs.pa_su_sc_mode_cntl & (1 << 21)) { - glEnable(GL_PRIMITIVE_RESTART); - } else { - glDisable(GL_PRIMITIVE_RESTART); - } - glPrimitiveRestartIndex(regs.multi_prim_ib_reset_index); - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateBlendState() { - auto& reg_file = *register_file_; - auto& regs = update_blend_state_regs_; - - // Alpha testing -- ALPHAREF, ALPHAFUNC, ALPHATESTENABLE - // Deprecated in GL, implemented in shader. - // if(ALPHATESTENABLE && frag_out.a [<=/ALPHAFUNC] ALPHAREF) discard; - uint32_t color_control = reg_file[XE_GPU_REG_RB_COLORCONTROL].u32; - draw_batcher_.set_alpha_test((color_control & 0x8) != 0, // ALPAHTESTENABLE - color_control & 0x7, // ALPHAFUNC - reg_file[XE_GPU_REG_RB_ALPHA_REF].f32); - - bool dirty = false; - dirty |= - SetShadowRegister(®s.rb_blendcontrol[0], XE_GPU_REG_RB_BLENDCONTROL_0); - dirty |= - SetShadowRegister(®s.rb_blendcontrol[1], XE_GPU_REG_RB_BLENDCONTROL_1); - dirty |= - SetShadowRegister(®s.rb_blendcontrol[2], XE_GPU_REG_RB_BLENDCONTROL_2); - dirty |= - SetShadowRegister(®s.rb_blendcontrol[3], XE_GPU_REG_RB_BLENDCONTROL_3); - dirty |= SetShadowRegister(®s.rb_blend_rgba[0], XE_GPU_REG_RB_BLEND_RED); - dirty |= SetShadowRegister(®s.rb_blend_rgba[1], XE_GPU_REG_RB_BLEND_GREEN); - dirty |= SetShadowRegister(®s.rb_blend_rgba[2], XE_GPU_REG_RB_BLEND_BLUE); - dirty |= SetShadowRegister(®s.rb_blend_rgba[3], XE_GPU_REG_RB_BLEND_ALPHA); - if (!dirty) { - return UpdateStatus::kCompatible; - } - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - static const GLenum blend_map[] = { - /* 0 */ GL_ZERO, - /* 1 */ GL_ONE, - /* 2 */ GL_ZERO, // ? - /* 3 */ GL_ZERO, // ? - /* 4 */ GL_SRC_COLOR, - /* 5 */ GL_ONE_MINUS_SRC_COLOR, - /* 6 */ GL_SRC_ALPHA, - /* 7 */ GL_ONE_MINUS_SRC_ALPHA, - /* 8 */ GL_DST_COLOR, - /* 9 */ GL_ONE_MINUS_DST_COLOR, - /* 10 */ GL_DST_ALPHA, - /* 11 */ GL_ONE_MINUS_DST_ALPHA, - /* 12 */ GL_CONSTANT_COLOR, - /* 13 */ GL_ONE_MINUS_CONSTANT_COLOR, - /* 14 */ GL_CONSTANT_ALPHA, - /* 15 */ GL_ONE_MINUS_CONSTANT_ALPHA, - /* 16 */ GL_SRC_ALPHA_SATURATE, - }; - static const GLenum blend_op_map[] = { - /* 0 */ GL_FUNC_ADD, - /* 1 */ GL_FUNC_SUBTRACT, - /* 2 */ GL_MIN, - /* 3 */ GL_MAX, - /* 4 */ GL_FUNC_REVERSE_SUBTRACT, - }; - for (int i = 0; i < xe::countof(regs.rb_blendcontrol); ++i) { - uint32_t blend_control = regs.rb_blendcontrol[i]; - // A2XX_RB_BLEND_CONTROL_COLOR_SRCBLEND - auto src_blend = blend_map[(blend_control & 0x0000001F) >> 0]; - // A2XX_RB_BLEND_CONTROL_COLOR_DESTBLEND - auto dest_blend = blend_map[(blend_control & 0x00001F00) >> 8]; - // A2XX_RB_BLEND_CONTROL_COLOR_COMB_FCN - auto blend_op = blend_op_map[(blend_control & 0x000000E0) >> 5]; - // A2XX_RB_BLEND_CONTROL_ALPHA_SRCBLEND - auto src_blend_alpha = blend_map[(blend_control & 0x001F0000) >> 16]; - // A2XX_RB_BLEND_CONTROL_ALPHA_DESTBLEND - auto dest_blend_alpha = blend_map[(blend_control & 0x1F000000) >> 24]; - // A2XX_RB_BLEND_CONTROL_ALPHA_COMB_FCN - auto blend_op_alpha = blend_op_map[(blend_control & 0x00E00000) >> 21]; - // A2XX_RB_COLORCONTROL_BLEND_DISABLE ?? Can't find this! - // Just guess based on actions. - // bool blend_enable = - // !((src_blend == GL_ONE) && (dest_blend == GL_ZERO) && - // (blend_op == GL_FUNC_ADD) && (src_blend_alpha == GL_ONE) && - // (dest_blend_alpha == GL_ZERO) && (blend_op_alpha == GL_FUNC_ADD)); - bool blend_enable = !(color_control & 0x20); - if (blend_enable) { - glEnablei(GL_BLEND, i); - glBlendEquationSeparatei(i, blend_op, blend_op_alpha); - glBlendFuncSeparatei(i, src_blend, dest_blend, src_blend_alpha, - dest_blend_alpha); - } else { - glDisablei(GL_BLEND, i); - } - } - - glBlendColor(regs.rb_blend_rgba[0], regs.rb_blend_rgba[1], - regs.rb_blend_rgba[2], regs.rb_blend_rgba[3]); - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus -GL4CommandProcessor::UpdateDepthStencilState() { - auto& regs = update_depth_stencil_state_regs_; - - bool dirty = false; - dirty |= SetShadowRegister(®s.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL); - dirty |= - SetShadowRegister(®s.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK); - if (!dirty) { - return UpdateStatus::kCompatible; - } - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - static const GLenum compare_func_map[] = { - /* 0 */ GL_NEVER, - /* 1 */ GL_LESS, - /* 2 */ GL_EQUAL, - /* 3 */ GL_LEQUAL, - /* 4 */ GL_GREATER, - /* 5 */ GL_NOTEQUAL, - /* 6 */ GL_GEQUAL, - /* 7 */ GL_ALWAYS, - }; - static const GLenum stencil_op_map[] = { - /* 0 */ GL_KEEP, - /* 1 */ GL_ZERO, - /* 2 */ GL_REPLACE, - /* 3 */ GL_INCR_WRAP, - /* 4 */ GL_DECR_WRAP, - /* 5 */ GL_INVERT, - /* 6 */ GL_INCR, - /* 7 */ GL_DECR, - }; - // A2XX_RB_DEPTHCONTROL_Z_ENABLE - if (regs.rb_depthcontrol & 0x00000002) { - glEnable(GL_DEPTH_TEST); - } else { - glDisable(GL_DEPTH_TEST); - } - // glDisable(GL_DEPTH_TEST); - // A2XX_RB_DEPTHCONTROL_Z_WRITE_ENABLE - glDepthMask((regs.rb_depthcontrol & 0x00000004) ? GL_TRUE : GL_FALSE); - // A2XX_RB_DEPTHCONTROL_EARLY_Z_ENABLE - // ? - // A2XX_RB_DEPTHCONTROL_ZFUNC - glDepthFunc(compare_func_map[(regs.rb_depthcontrol & 0x00000070) >> 4]); - // A2XX_RB_DEPTHCONTROL_STENCIL_ENABLE - if (regs.rb_depthcontrol & 0x00000001) { - glEnable(GL_STENCIL_TEST); - } else { - glDisable(GL_STENCIL_TEST); - } - // RB_STENCILREFMASK_STENCILREF - uint32_t stencil_ref = (regs.rb_stencilrefmask & 0x000000FF); - // RB_STENCILREFMASK_STENCILMASK - uint32_t stencil_read_mask = (regs.rb_stencilrefmask & 0x0000FF00) >> 8; - // RB_STENCILREFMASK_STENCILWRITEMASK - glStencilMask((regs.rb_stencilrefmask & 0x00FF0000) >> 16); - // A2XX_RB_DEPTHCONTROL_BACKFACE_ENABLE - bool backface_enabled = (regs.rb_depthcontrol & 0x00000080) != 0; - if (backface_enabled) { - // A2XX_RB_DEPTHCONTROL_STENCILFUNC - glStencilFuncSeparate( - GL_FRONT, compare_func_map[(regs.rb_depthcontrol & 0x00000700) >> 8], - stencil_ref, stencil_read_mask); - // A2XX_RB_DEPTHCONTROL_STENCILFAIL - // A2XX_RB_DEPTHCONTROL_STENCILZFAIL - // A2XX_RB_DEPTHCONTROL_STENCILZPASS - glStencilOpSeparate( - GL_FRONT, stencil_op_map[(regs.rb_depthcontrol & 0x00003800) >> 11], - stencil_op_map[(regs.rb_depthcontrol & 0x000E0000) >> 17], - stencil_op_map[(regs.rb_depthcontrol & 0x0001C000) >> 14]); - // A2XX_RB_DEPTHCONTROL_STENCILFUNC_BF - glStencilFuncSeparate( - GL_BACK, compare_func_map[(regs.rb_depthcontrol & 0x00700000) >> 20], - stencil_ref, stencil_read_mask); - // A2XX_RB_DEPTHCONTROL_STENCILFAIL_BF - // A2XX_RB_DEPTHCONTROL_STENCILZFAIL_BF - // A2XX_RB_DEPTHCONTROL_STENCILZPASS_BF - glStencilOpSeparate( - GL_BACK, stencil_op_map[(regs.rb_depthcontrol & 0x03800000) >> 23], - stencil_op_map[(regs.rb_depthcontrol & 0xE0000000) >> 29], - stencil_op_map[(regs.rb_depthcontrol & 0x1C000000) >> 26]); - } else { - // Backfaces disabled - treat backfaces as frontfaces. - glStencilFunc(compare_func_map[(regs.rb_depthcontrol & 0x00000700) >> 8], - stencil_ref, stencil_read_mask); - glStencilOp(stencil_op_map[(regs.rb_depthcontrol & 0x00003800) >> 11], - stencil_op_map[(regs.rb_depthcontrol & 0x000E0000) >> 17], - stencil_op_map[(regs.rb_depthcontrol & 0x0001C000) >> 14]); - } - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateIndexBuffer( - IndexBufferInfo* index_buffer_info) { - auto& regs = *register_file_; - if (!index_buffer_info || !index_buffer_info->guest_base) { - // No index buffer or auto draw. - return UpdateStatus::kCompatible; - } - auto& info = *index_buffer_info; - -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - // Min/max index ranges for clamping. This is often [0g,FFFF|FFFFFF]. - // All indices should be clamped to [min,max]. May be a way to do this in GL. - uint32_t min_index = regs[XE_GPU_REG_VGT_MIN_VTX_INDX].u32; - uint32_t max_index = regs[XE_GPU_REG_VGT_MAX_VTX_INDX].u32; - assert_true(min_index == 0); - assert_true(max_index == 0xFFFF || max_index == 0xFFFFFF); - - assert_true(info.endianness == Endian::k8in16 || - info.endianness == Endian::k8in32); - - trace_writer_.WriteMemoryRead(info.guest_base, info.length); - - size_t total_size = - info.count * (info.format == IndexFormat::kInt32 ? sizeof(uint32_t) - : sizeof(uint16_t)); - CircularBuffer::Allocation allocation; - if (!scratch_buffer_.AcquireCached(info.guest_base, total_size, - &allocation)) { - if (info.format == IndexFormat::kInt32) { - auto dest = reinterpret_cast<uint32_t*>(allocation.host_ptr); - auto src = memory_->TranslatePhysical<const uint32_t*>(info.guest_base); - xe::copy_and_swap_32_aligned(dest, src, info.count); - } else { - auto dest = reinterpret_cast<uint16_t*>(allocation.host_ptr); - auto src = memory_->TranslatePhysical<const uint16_t*>(info.guest_base); - xe::copy_and_swap_16_aligned(dest, src, info.count); - } - draw_batcher_.set_index_buffer(allocation); - scratch_buffer_.Commit(std::move(allocation)); - } else { - draw_batcher_.set_index_buffer(allocation); - } - - return UpdateStatus::kCompatible; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateVertexBuffers() { -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - auto& regs = *register_file_; - assert_not_null(active_vertex_shader_); - - for (const auto& vertex_binding : active_vertex_shader_->vertex_bindings()) { - int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + - (vertex_binding.fetch_constant / 3) * 6; - const auto group = reinterpret_cast<xe_gpu_fetch_group_t*>(®s.values[r]); - const xe_gpu_vertex_fetch_t* fetch = nullptr; - switch (vertex_binding.fetch_constant % 3) { - case 0: - fetch = &group->vertex_fetch_0; - break; - case 1: - fetch = &group->vertex_fetch_1; - break; - case 2: - fetch = &group->vertex_fetch_2; - break; - } - assert_true(fetch->endian == 2); - - size_t valid_range = size_t(fetch->size * 4); - - trace_writer_.WriteMemoryRead(fetch->address << 2, valid_range); - - auto vertex_shader = static_cast<GL4Shader*>(active_vertex_shader_); - CircularBuffer::Allocation allocation; - if (!scratch_buffer_.AcquireCached(fetch->address << 2, valid_range, - &allocation)) { - // Copy and byte swap the entire buffer. - // We could be smart about this to save GPU bandwidth by building a CRC - // as we copy and only if it differs from the previous value committing - // it (and if it matches just discard and reuse). - xe::copy_and_swap_32_aligned( - allocation.host_ptr, - memory_->TranslatePhysical<const uint32_t*>(fetch->address << 2), - valid_range / 4); - - // TODO(benvanik): if we could find a way to avoid this, we could use - // multidraw without flushing. - glVertexArrayVertexBuffer( - vertex_shader->vao(), - static_cast<GLuint>(vertex_binding.binding_index), - scratch_buffer_.handle(), allocation.offset, - vertex_binding.stride_words * 4); - - scratch_buffer_.Commit(std::move(allocation)); - } else { - // TODO(benvanik): if we could find a way to avoid this, we could use - // multidraw without flushing. - glVertexArrayVertexBuffer( - vertex_shader->vao(), - static_cast<GLuint>(vertex_binding.binding_index), - scratch_buffer_.handle(), allocation.offset, - vertex_binding.stride_words * 4); - } - } - - return UpdateStatus::kCompatible; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSamplers() { -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - bool mismatch = false; - - // VS and PS samplers are shared, but may be used exclusively. - // We walk each and setup lazily. - bool has_setup_sampler[32] = {false}; - - // Vertex texture samplers. - for (auto& texture_binding : active_vertex_shader_->texture_bindings()) { - if (has_setup_sampler[texture_binding.fetch_constant]) { - continue; - } - has_setup_sampler[texture_binding.fetch_constant] = true; - auto status = PopulateSampler(texture_binding); - if (status == UpdateStatus::kError) { - return status; - } else if (status == UpdateStatus::kMismatch) { - mismatch = true; - } - } - - // Pixel shader texture sampler. - for (auto& texture_binding : active_pixel_shader_->texture_bindings()) { - if (has_setup_sampler[texture_binding.fetch_constant]) { - continue; - } - has_setup_sampler[texture_binding.fetch_constant] = true; - auto status = PopulateSampler(texture_binding); - if (status == UpdateStatus::kError) { - return UpdateStatus::kError; - } else if (status == UpdateStatus::kMismatch) { - mismatch = true; - } - } - - return mismatch ? UpdateStatus::kMismatch : UpdateStatus::kCompatible; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSampler( - const Shader::TextureBinding& texture_binding) { - auto& regs = *register_file_; - int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + - texture_binding.fetch_constant * 6; - auto group = reinterpret_cast<const xe_gpu_fetch_group_t*>(®s.values[r]); - auto& fetch = group->texture_fetch; - - // Reset slot. - // If we fail, we still draw but with an invalid texture. - draw_batcher_.set_texture_sampler(texture_binding.fetch_constant, 0, 0); - - if (FLAGS_disable_textures) { - return UpdateStatus::kCompatible; - } - - // ? - if (!fetch.type) { - return UpdateStatus::kCompatible; - } - assert_true(fetch.type == 0x2); - - TextureInfo texture_info; - if (!TextureInfo::Prepare(fetch, &texture_info)) { - XELOGE("Unable to parse texture fetcher info"); - return UpdateStatus::kCompatible; // invalid texture used - } - SamplerInfo sampler_info; - if (!SamplerInfo::Prepare(fetch, texture_binding.fetch_instr, - &sampler_info)) { - XELOGE("Unable to parse sampler info"); - return UpdateStatus::kCompatible; // invalid texture used - } - - trace_writer_.WriteMemoryRead(texture_info.guest_address, - texture_info.input_length); - - auto entry_view = texture_cache_.Demand(texture_info, sampler_info); - if (!entry_view) { - // Unable to create/fetch/etc. - XELOGE("Failed to demand texture"); - return UpdateStatus::kCompatible; - } - - // Shaders will use bindless to fetch right from it. - draw_batcher_.set_texture_sampler(texture_binding.fetch_constant, - entry_view->texture_sampler_handle, - fetch.swizzle); - - return UpdateStatus::kCompatible; -} - -bool GL4CommandProcessor::IssueCopy() { - SCOPE_profile_cpu_f("gpu"); - auto& regs = *register_file_; - - // This is used to resolve surfaces, taking them from EDRAM render targets - // to system memory. It can optionally clear color/depth surfaces, too. - // The command buffer has stuff for actually doing this by drawing, however - // we should be able to do it without that much easier. - - uint32_t copy_control = regs[XE_GPU_REG_RB_COPY_CONTROL].u32; - // Render targets 0-3, 4 = depth - uint32_t copy_src_select = copy_control & 0x7; - bool color_clear_enabled = (copy_control >> 8) & 0x1; - bool depth_clear_enabled = (copy_control >> 9) & 0x1; - auto copy_command = static_cast<CopyCommand>((copy_control >> 20) & 0x3); - - uint32_t copy_dest_info = regs[XE_GPU_REG_RB_COPY_DEST_INFO].u32; - auto copy_dest_endian = static_cast<Endian128>(copy_dest_info & 0x7); - uint32_t copy_dest_array = (copy_dest_info >> 3) & 0x1; - assert_true(copy_dest_array == 0); - uint32_t copy_dest_slice = (copy_dest_info >> 4) & 0x7; - assert_true(copy_dest_slice == 0); - auto copy_dest_format = - static_cast<ColorFormat>((copy_dest_info >> 7) & 0x3F); - uint32_t copy_dest_number = (copy_dest_info >> 13) & 0x7; - // assert_true(copy_dest_number == 0); // ? - uint32_t copy_dest_bias = (copy_dest_info >> 16) & 0x3F; - // assert_true(copy_dest_bias == 0); - uint32_t copy_dest_swap = (copy_dest_info >> 25) & 0x1; - - uint32_t copy_dest_base = regs[XE_GPU_REG_RB_COPY_DEST_BASE].u32; - uint32_t copy_dest_pitch = regs[XE_GPU_REG_RB_COPY_DEST_PITCH].u32; - uint32_t copy_dest_height = (copy_dest_pitch >> 16) & 0x3FFF; - copy_dest_pitch &= 0x3FFF; - - // None of this is supported yet: - uint32_t copy_surface_slice = regs[XE_GPU_REG_RB_COPY_SURFACE_SLICE].u32; - assert_true(copy_surface_slice == 0); - uint32_t copy_func = regs[XE_GPU_REG_RB_COPY_FUNC].u32; - assert_true(copy_func == 0); - uint32_t copy_ref = regs[XE_GPU_REG_RB_COPY_REF].u32; - assert_true(copy_ref == 0); - uint32_t copy_mask = regs[XE_GPU_REG_RB_COPY_MASK].u32; - assert_true(copy_mask == 0); - - // RB_SURFACE_INFO - // http://fossies.org/dox/MesaLib-10.3.5/fd2__gmem_8c_source.html - uint32_t surface_info = regs[XE_GPU_REG_RB_SURFACE_INFO].u32; - uint32_t surface_pitch = surface_info & 0x3FFF; - auto surface_msaa = static_cast<MsaaSamples>((surface_info >> 16) & 0x3); - - // Depending on the source, pick the buffer we'll be sourcing. - // We then query for a cached framebuffer setup with that buffer active. - TextureFormat src_format = TextureFormat::kUnknown; - GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget}; - GLuint depth_target = kAnyTarget; - if (copy_src_select <= 3 || color_clear_enabled) { - // Source from a color target. - uint32_t color_info[4] = { - regs[XE_GPU_REG_RB_COLOR_INFO].u32, - regs[XE_GPU_REG_RB_COLOR1_INFO].u32, - regs[XE_GPU_REG_RB_COLOR2_INFO].u32, - regs[XE_GPU_REG_RB_COLOR3_INFO].u32, - }; - uint32_t color_base = color_info[copy_src_select] & 0xFFF; - auto color_format = static_cast<ColorRenderTargetFormat>( - (color_info[copy_src_select] >> 16) & 0xF); - color_targets[copy_src_select] = GetColorRenderTarget( - surface_pitch, surface_msaa, color_base, color_format); - - if (copy_src_select <= 3) { - src_format = ColorRenderTargetToTextureFormat(color_format); - } - } - - // Grab the depth/stencil if we're sourcing from it or clear is enabled. - if (copy_src_select > 3 || depth_clear_enabled) { - uint32_t depth_info = regs[XE_GPU_REG_RB_DEPTH_INFO].u32; - uint32_t depth_base = depth_info & 0xFFF; - auto depth_format = - static_cast<DepthRenderTargetFormat>((depth_info >> 16) & 0x1); - depth_target = GetDepthRenderTarget(surface_pitch, surface_msaa, depth_base, - depth_format); - - if (copy_src_select > 3) { - src_format = DepthRenderTargetToTextureFormat(depth_format); - } - } - - auto source_framebuffer = GetFramebuffer(color_targets, depth_target); - if (!source_framebuffer) { - // If we get here we are likely missing some state checks. - assert_always("No framebuffer for copy source? no-op copy?"); - XELOGE("No framebuffer for copy source"); - return false; - } - - active_framebuffer_ = source_framebuffer; - - GLenum read_format; - GLenum read_type; - size_t read_size = 0; - switch (copy_dest_format) { - case ColorFormat::k_1_5_5_5: - read_format = GL_RGB5_A1; - read_type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - read_size = 16; - break; - case ColorFormat::k_2_10_10_10: - read_format = GL_RGB10_A2; - read_type = GL_UNSIGNED_INT_10_10_10_2; - read_size = 32; - break; - case ColorFormat::k_4_4_4_4: - read_format = GL_RGBA4; - read_type = GL_UNSIGNED_SHORT_4_4_4_4; - read_size = 16; - break; - case ColorFormat::k_5_6_5: - read_format = GL_RGB565; - read_type = GL_UNSIGNED_SHORT_5_6_5; - read_size = 16; - break; - case ColorFormat::k_8: - read_format = GL_R8; - read_type = GL_UNSIGNED_BYTE; - read_size = 8; - break; - case ColorFormat::k_8_8: - read_format = GL_RG8; - read_type = GL_UNSIGNED_BYTE; - read_size = 16; - break; - case ColorFormat::k_8_8_8_8: - read_format = copy_dest_swap ? GL_BGRA : GL_RGBA; - read_type = GL_UNSIGNED_BYTE; - read_size = 32; - break; - case ColorFormat::k_16: - read_format = GL_R16; - read_type = GL_UNSIGNED_SHORT; - read_size = 16; - break; - case ColorFormat::k_16_FLOAT: - read_format = GL_R16F; - read_type = GL_HALF_FLOAT; - read_size = 16; - break; - case ColorFormat::k_16_16: - read_format = GL_RG16; - read_type = GL_UNSIGNED_SHORT; - read_size = 32; - break; - case ColorFormat::k_16_16_FLOAT: - read_format = GL_RG16F; - read_type = GL_HALF_FLOAT; - read_size = 32; - break; - case ColorFormat::k_16_16_16_16: - read_format = GL_RGBA16; - read_type = GL_UNSIGNED_SHORT; - read_size = 64; - break; - case ColorFormat::k_16_16_16_16_FLOAT: - read_format = GL_RGBA16F; - read_type = GL_HALF_FLOAT; - read_size = 64; - break; - case ColorFormat::k_32_FLOAT: - read_format = GL_R32F; - read_type = GL_FLOAT; - read_size = 32; - break; - case ColorFormat::k_32_32_FLOAT: - read_format = GL_RG32F; - read_type = GL_FLOAT; - read_size = 64; - break; - case ColorFormat::k_32_32_32_32_FLOAT: - read_format = GL_RGBA32F; - read_type = GL_FLOAT; - read_size = 128; - break; - case ColorFormat::k_10_11_11: - case ColorFormat::k_11_11_10: - read_format = GL_R11F_G11F_B10F; - read_type = GL_UNSIGNED_INT_10F_11F_11F_REV; - read_size = 32; - break; - default: - assert_unhandled_case(copy_dest_format); - return false; - } - - // TODO(benvanik): swap channel ordering on copy_dest_swap - // Can we use GL swizzles for this? - - // Swap byte order during read. - // TODO(benvanik): handle other endian modes. - switch (copy_dest_endian) { - case Endian128::kUnspecified: - glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); - break; - case Endian128::k8in32: - glPixelStorei(GL_PACK_SWAP_BYTES, GL_TRUE); - break; - default: - // assert_unhandled_case(copy_dest_endian); - glPixelStorei(GL_PACK_SWAP_BYTES, GL_TRUE); - break; - } - - // TODO(benvanik): tweak alignments/strides. - // glPixelStorei(GL_PACK_ALIGNMENT, 1); - // glPixelStorei(GL_PACK_ROW_LENGTH, 0); - // glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); - - // TODO(benvanik): any way to scissor this? a200 has: - // REG_A2XX_RB_COPY_DEST_OFFSET = A2XX_RB_COPY_DEST_OFFSET_X(tile->xoff) | - // A2XX_RB_COPY_DEST_OFFSET_Y(tile->yoff); - // but I can't seem to find something similar. - uint32_t dest_logical_width = copy_dest_pitch; - uint32_t dest_logical_height = copy_dest_height; - uint32_t dest_block_width = xe::round_up(dest_logical_width, 32); - uint32_t dest_block_height = /*xe::round_up(*/ dest_logical_height /*, 32)*/; - - uint32_t window_offset = regs[XE_GPU_REG_PA_SC_WINDOW_OFFSET].u32; - int16_t window_offset_x = window_offset & 0x7FFF; - int16_t window_offset_y = (window_offset >> 16) & 0x7FFF; - if (window_offset_x & 0x4000) { - window_offset_x |= 0x8000; - } - if (window_offset_y & 0x4000) { - window_offset_y |= 0x8000; - } - - // HACK: vertices to use are always in vf0. - int copy_vertex_fetch_slot = 0; - int r = - XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + (copy_vertex_fetch_slot / 3) * 6; - const auto group = reinterpret_cast<xe_gpu_fetch_group_t*>(®s.values[r]); - const xe_gpu_vertex_fetch_t* fetch = nullptr; - switch (copy_vertex_fetch_slot % 3) { - case 0: - fetch = &group->vertex_fetch_0; - break; - case 1: - fetch = &group->vertex_fetch_1; - break; - case 2: - fetch = &group->vertex_fetch_2; - break; - } - assert_true(fetch->type == 3); - assert_true(fetch->endian == 2); - assert_true(fetch->size == 6); - const uint8_t* vertex_addr = memory_->TranslatePhysical(fetch->address << 2); - trace_writer_.WriteMemoryRead(fetch->address << 2, fetch->size * 4); - int32_t dest_min_x = int32_t((std::min( - std::min( - GpuSwap(xe::load<float>(vertex_addr + 0), Endian(fetch->endian)), - GpuSwap(xe::load<float>(vertex_addr + 8), Endian(fetch->endian))), - GpuSwap(xe::load<float>(vertex_addr + 16), Endian(fetch->endian))))); - int32_t dest_max_x = int32_t((std::max( - std::max( - GpuSwap(xe::load<float>(vertex_addr + 0), Endian(fetch->endian)), - GpuSwap(xe::load<float>(vertex_addr + 8), Endian(fetch->endian))), - GpuSwap(xe::load<float>(vertex_addr + 16), Endian(fetch->endian))))); - int32_t dest_min_y = int32_t((std::min( - std::min( - GpuSwap(xe::load<float>(vertex_addr + 4), Endian(fetch->endian)), - GpuSwap(xe::load<float>(vertex_addr + 12), Endian(fetch->endian))), - GpuSwap(xe::load<float>(vertex_addr + 20), Endian(fetch->endian))))); - int32_t dest_max_y = int32_t((std::max( - std::max( - GpuSwap(xe::load<float>(vertex_addr + 4), Endian(fetch->endian)), - GpuSwap(xe::load<float>(vertex_addr + 12), Endian(fetch->endian))), - GpuSwap(xe::load<float>(vertex_addr + 20), Endian(fetch->endian))))); - Rect2D dest_rect(dest_min_x, dest_min_y, dest_max_x - dest_min_x, - dest_max_y - dest_min_y); - Rect2D src_rect(0, 0, dest_rect.width, dest_rect.height); - - // The dest base address passed in has already been offset by the window - // offset, so to ensure texture lookup works we need to offset it. - // TODO(benvanik): allow texture cache to lookup partial textures. - // TODO(benvanik): change based on format. - int32_t dest_offset = window_offset_y * copy_dest_pitch * int(read_size / 8); - dest_offset += window_offset_x * 32 * int(read_size / 8); - copy_dest_base += dest_offset; - - // Destination pointer in guest memory. - // We have GL throw bytes directly into it. - // TODO(benvanik): copy to staging texture then PBO back? - void* ptr = memory_->TranslatePhysical(copy_dest_base); - size_t size = copy_dest_pitch * copy_dest_height * (read_size / 8); - - auto blitter = static_cast<xe::ui::gl::GLContext*>(context_.get())->blitter(); - - // Make active so glReadPixels reads from us. - switch (copy_command) { - case CopyCommand::kRaw: { - // This performs a byte-for-byte copy of the textures from src to dest - // with no conversion. Byte swapping may still occur. - if (copy_src_select <= 3) { - // Source from a bound render target. - // TODO(benvanik): RAW copy. - last_framebuffer_texture_ = texture_cache_.CopyTexture( - blitter, copy_dest_base, dest_logical_width, dest_logical_height, - dest_block_width, dest_block_height, - ColorFormatToTextureFormat(copy_dest_format), - copy_dest_swap ? true : false, color_targets[copy_src_select], - src_rect, dest_rect); - if (!FLAGS_disable_framebuffer_readback) { - // std::memset(ptr, 0xDE, - // copy_dest_pitch * copy_dest_height * (read_size / 8)); - // glReadPixels(0, 0, copy_dest_pitch, copy_dest_height, read_format, - // read_type, ptr); - } - } else { - // Source from the bound depth/stencil target. - // TODO(benvanik): RAW copy. - texture_cache_.CopyTexture( - blitter, copy_dest_base, dest_logical_width, dest_logical_height, - dest_block_width, dest_block_height, src_format, - copy_dest_swap ? true : false, depth_target, src_rect, dest_rect); - if (!FLAGS_disable_framebuffer_readback) { - // std::memset(ptr, 0xDE, - // copy_dest_pitch * copy_dest_height * (read_size / 8)); - // glReadPixels(0, 0, copy_dest_pitch, copy_dest_height, - // GL_DEPTH_STENCIL, read_type, ptr); - } - } - break; - } - case CopyCommand::kConvert: { - if (copy_src_select <= 3) { - // Source from a bound render target. - // Either copy the readbuffer into an existing texture or create a new - // one in the cache so we can service future upload requests. - last_framebuffer_texture_ = texture_cache_.ConvertTexture( - blitter, copy_dest_base, dest_logical_width, dest_logical_height, - dest_block_width, dest_block_height, - ColorFormatToTextureFormat(copy_dest_format), - copy_dest_swap ? true : false, color_targets[copy_src_select], - src_rect, dest_rect); - if (!FLAGS_disable_framebuffer_readback) { - // std::memset(ptr, 0xDE, - // copy_dest_pitch * copy_dest_height * (read_size / 8)); - // glReadPixels(0, 0, copy_dest_pitch, copy_dest_height, read_format, - // read_type, ptr); - } - } else { - // Source from the bound depth/stencil target. - texture_cache_.ConvertTexture( - blitter, copy_dest_base, dest_logical_width, dest_logical_height, - dest_block_width, dest_block_height, src_format, - copy_dest_swap ? true : false, depth_target, src_rect, dest_rect); - if (!FLAGS_disable_framebuffer_readback) { - // std::memset(ptr, 0xDE, - // copy_dest_pitch * copy_dest_height * (read_size / 8)); - // glReadPixels(0, 0, copy_dest_pitch, copy_dest_height, - // GL_DEPTH_STENCIL, read_type, ptr); - } - } - break; - } - case CopyCommand::kConstantOne: - case CopyCommand::kNull: - default: - // assert_unhandled_case(copy_command); - return false; - } - - // Perform any requested clears. - uint32_t copy_depth_clear = regs[XE_GPU_REG_RB_DEPTH_CLEAR].u32; - uint32_t copy_color_clear = regs[XE_GPU_REG_RB_COLOR_CLEAR].u32; - uint32_t copy_color_clear_low = regs[XE_GPU_REG_RB_COLOR_CLEAR_LOW].u32; - assert_true(copy_color_clear == copy_color_clear_low); - - if (color_clear_enabled) { - // Clear the render target we selected for copy. - assert_true(copy_src_select < 3); - // TODO(benvanik): verify color order. - float color[] = {(copy_color_clear & 0xFF) / 255.0f, - ((copy_color_clear >> 8) & 0xFF) / 255.0f, - ((copy_color_clear >> 16) & 0xFF) / 255.0f, - ((copy_color_clear >> 24) & 0xFF) / 255.0f}; - // TODO(benvanik): remove query. - GLboolean old_color_mask[4]; - glGetBooleani_v(GL_COLOR_WRITEMASK, copy_src_select, old_color_mask); - glColorMaski(copy_src_select, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glClearNamedFramebufferfv(source_framebuffer->framebuffer, GL_COLOR, - copy_src_select, color); - glColorMaski(copy_src_select, old_color_mask[0], old_color_mask[1], - old_color_mask[2], old_color_mask[3]); - } - - if (depth_clear_enabled && depth_target != kAnyTarget) { - // Clear the current depth buffer. - // TODO(benvanik): verify format. - GLfloat depth = {(copy_depth_clear & 0xFFFFFF00) / - static_cast<float>(0xFFFFFF00)}; - GLint stencil = copy_depth_clear & 0xFF; - GLint old_draw_framebuffer; - GLboolean old_depth_mask; - GLint old_stencil_mask; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_framebuffer); - glGetBooleanv(GL_DEPTH_WRITEMASK, &old_depth_mask); - glGetIntegerv(GL_STENCIL_WRITEMASK, &old_stencil_mask); - glDepthMask(GL_TRUE); - glStencilMask(0xFF); - // HACK: this should work, but throws INVALID_ENUM on nvidia drivers. - // GLEW signature differs from OpenGL docs? - // glClearNamedFramebufferfi(source_framebuffer->framebuffer, - // GL_DEPTH_STENCIL, depth, stencil); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, source_framebuffer->framebuffer); - glClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_framebuffer); - glDepthMask(old_depth_mask); - glStencilMask(old_stencil_mask); - } - - return true; -} - -GLuint GL4CommandProcessor::GetColorRenderTarget( - uint32_t pitch, MsaaSamples samples, uint32_t base, - ColorRenderTargetFormat format) { - // Because we don't know the height of anything, we allocate at full res. - // At 2560x2560, it's impossible for EDRAM to fit anymore. - uint32_t width = 2560; - uint32_t height = 2560; - - // NOTE: we strip gamma formats down to normal ones. - if (format == ColorRenderTargetFormat::k_8_8_8_8_GAMMA) { - format = ColorRenderTargetFormat::k_8_8_8_8; - } - - GLenum internal_format; - switch (format) { - case ColorRenderTargetFormat::k_8_8_8_8: - case ColorRenderTargetFormat::k_8_8_8_8_GAMMA: - internal_format = GL_RGBA8; - break; - case ColorRenderTargetFormat::k_2_10_10_10: - case ColorRenderTargetFormat::k_2_10_10_10_unknown: - internal_format = GL_RGB10_A2UI; - break; - case ColorRenderTargetFormat::k_2_10_10_10_FLOAT: - case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown: - internal_format = GL_RGB10_A2; - break; - case ColorRenderTargetFormat::k_16_16: - internal_format = GL_RG16; - break; - case ColorRenderTargetFormat::k_16_16_FLOAT: - internal_format = GL_RG16F; - break; - case ColorRenderTargetFormat::k_16_16_16_16: - internal_format = GL_RGBA16; - break; - case ColorRenderTargetFormat::k_16_16_16_16_FLOAT: - internal_format = GL_RGBA16F; - break; - case ColorRenderTargetFormat::k_32_FLOAT: - internal_format = GL_R32F; - break; - case ColorRenderTargetFormat::k_32_32_FLOAT: - internal_format = GL_RG32F; - break; - default: - assert_unhandled_case(format); - return 0; - } - - for (auto it = cached_color_render_targets_.begin(); - it != cached_color_render_targets_.end(); ++it) { - if (it->base == base && it->width == width && it->height == height && - it->internal_format == internal_format) { - return it->texture; - } - } - cached_color_render_targets_.push_back(CachedColorRenderTarget()); - auto cached = &cached_color_render_targets_.back(); - cached->base = base; - cached->width = width; - cached->height = height; - cached->format = format; - cached->internal_format = internal_format; - - glCreateTextures(GL_TEXTURE_2D, 1, &cached->texture); - glTextureStorage2D(cached->texture, 1, internal_format, width, height); - - return cached->texture; -} - -GLuint GL4CommandProcessor::GetDepthRenderTarget( - uint32_t pitch, MsaaSamples samples, uint32_t base, - DepthRenderTargetFormat format) { - uint32_t width = 2560; - uint32_t height = 2560; - - GLenum internal_format; - switch (format) { - case DepthRenderTargetFormat::kD24S8: - internal_format = GL_DEPTH24_STENCIL8; - break; - case DepthRenderTargetFormat::kD24FS8: - // TODO(benvanik): not supported in GL? - internal_format = GL_DEPTH24_STENCIL8; - break; - default: - assert_unhandled_case(format); - return 0; - } - - for (auto it = cached_depth_render_targets_.begin(); - it != cached_depth_render_targets_.end(); ++it) { - if (it->base == base && it->width == width && it->height == height && - it->format == format) { - return it->texture; - } - } - cached_depth_render_targets_.push_back(CachedDepthRenderTarget()); - auto cached = &cached_depth_render_targets_.back(); - cached->base = base; - cached->width = width; - cached->height = height; - cached->format = format; - cached->internal_format = internal_format; - - glCreateTextures(GL_TEXTURE_2D, 1, &cached->texture); - glTextureStorage2D(cached->texture, 1, internal_format, width, height); - - return cached->texture; -} - -GL4CommandProcessor::CachedFramebuffer* GL4CommandProcessor::GetFramebuffer( - GLuint color_targets[4], GLuint depth_target) { - for (auto it = cached_framebuffers_.begin(); it != cached_framebuffers_.end(); - ++it) { - if ((depth_target == kAnyTarget || it->depth_target == depth_target) && - (color_targets[0] == kAnyTarget || - it->color_targets[0] == color_targets[0]) && - (color_targets[1] == kAnyTarget || - it->color_targets[1] == color_targets[1]) && - (color_targets[2] == kAnyTarget || - it->color_targets[2] == color_targets[2]) && - (color_targets[3] == kAnyTarget || - it->color_targets[3] == color_targets[3])) { - return &*it; - } - } - - GLuint real_color_targets[4]; - bool any_set = false; - for (int i = 0; i < 4; ++i) { - if (color_targets[i] == kAnyTarget) { - real_color_targets[i] = 0; - } else { - any_set = true; - real_color_targets[i] = color_targets[i]; - } - } - GLuint real_depth_target; - if (depth_target == kAnyTarget) { - real_depth_target = 0; - } else { - any_set = true; - real_depth_target = depth_target; - } - if (!any_set) { - // No framebuffer required. - return nullptr; - } - - cached_framebuffers_.push_back(CachedFramebuffer()); - auto cached = &cached_framebuffers_.back(); - glCreateFramebuffers(1, &cached->framebuffer); - for (int i = 0; i < 4; ++i) { - cached->color_targets[i] = real_color_targets[i]; - glNamedFramebufferTexture(cached->framebuffer, GL_COLOR_ATTACHMENT0 + i, - real_color_targets[i], 0); - } - cached->depth_target = real_depth_target; - glNamedFramebufferTexture(cached->framebuffer, GL_DEPTH_STENCIL_ATTACHMENT, - real_depth_target, 0); - - return cached; -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe diff --git a/src/xenia/gpu/gl4/gl4_command_processor.h b/src/xenia/gpu/gl4/gl4_command_processor.h deleted file mode 100644 index e3f45c9eb..000000000 --- a/src/xenia/gpu/gl4/gl4_command_processor.h +++ /dev/null @@ -1,237 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_GPU_GL4_GL4_COMMAND_PROCESSOR_H_ -#define XENIA_GPU_GL4_GL4_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/gl4/draw_batcher.h" -#include "xenia/gpu/gl4/gl4_shader.h" -#include "xenia/gpu/gl4/gl4_shader_cache.h" -#include "xenia/gpu/gl4/texture_cache.h" -#include "xenia/gpu/glsl_shader_translator.h" -#include "xenia/gpu/register_file.h" -#include "xenia/gpu/xenos.h" -#include "xenia/kernel/xthread.h" -#include "xenia/memory.h" -#include "xenia/ui/gl/circular_buffer.h" -#include "xenia/ui/gl/gl_context.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -class GL4GraphicsSystem; - -class GL4CommandProcessor : public CommandProcessor { - public: - GL4CommandProcessor(GL4GraphicsSystem* graphics_system, - kernel::KernelState* kernel_state); - ~GL4CommandProcessor() override; - - void ClearCaches() override; - - // HACK: for debugging; would be good to have this in a base type. - TextureCache* texture_cache() { return &texture_cache_; } - DrawBatcher* draw_batcher() { return &draw_batcher_; } - - GLuint GetColorRenderTarget(uint32_t pitch, MsaaSamples samples, - uint32_t base, ColorRenderTargetFormat format); - GLuint GetDepthRenderTarget(uint32_t pitch, MsaaSamples samples, - uint32_t base, DepthRenderTargetFormat format); - - private: - enum class UpdateStatus { - kCompatible, - kMismatch, - kError, - }; - - struct CachedFramebuffer { - GLuint color_targets[4]; - GLuint depth_target; - GLuint framebuffer; - }; - struct CachedColorRenderTarget { - uint32_t base; - uint32_t width; - uint32_t height; - ColorRenderTargetFormat format; - GLenum internal_format; - GLuint texture; - }; - struct CachedDepthRenderTarget { - uint32_t base; - uint32_t width; - uint32_t height; - DepthRenderTargetFormat format; - GLenum internal_format; - GLuint texture; - }; - struct CachedPipeline { - CachedPipeline(); - ~CachedPipeline(); - GLuint vertex_program; - GLuint fragment_program; - struct { - GLuint default_pipeline; - GLuint point_list_pipeline; - GLuint rect_list_pipeline; - GLuint quad_list_pipeline; - GLuint line_quad_list_pipeline; - // TODO(benvanik): others with geometry shaders. - } handles; - }; - - bool SetupContext() override; - void ShutdownContext() override; - GLuint CreateGeometryProgram(const std::string& source); - - void MakeCoherent() override; - void PrepareForWait() override; - void ReturnFromWait() override; - - 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 prim_type, uint32_t index_count, - IndexBufferInfo* index_buffer_info) override; - UpdateStatus UpdateShaders(PrimitiveType prim_type); - UpdateStatus UpdateRenderTargets(); - UpdateStatus UpdateState(PrimitiveType prim_type); - UpdateStatus UpdateViewportState(); - UpdateStatus UpdateRasterizerState(PrimitiveType prim_type); - UpdateStatus UpdateBlendState(); - UpdateStatus UpdateDepthStencilState(); - UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info); - UpdateStatus PopulateVertexBuffers(); - UpdateStatus PopulateSamplers(); - UpdateStatus PopulateSampler(const Shader::TextureBinding& texture_binding); - bool IssueCopy() override; - - CachedFramebuffer* GetFramebuffer(GLuint color_targets[4], - GLuint depth_target); - - GlslShaderTranslator shader_translator_; - GL4ShaderCache shader_cache_; - CachedFramebuffer* active_framebuffer_ = nullptr; - GLuint last_framebuffer_texture_ = 0; - - std::vector<CachedFramebuffer> cached_framebuffers_; - std::vector<CachedColorRenderTarget> cached_color_render_targets_; - std::vector<CachedDepthRenderTarget> cached_depth_render_targets_; - std::vector<std::unique_ptr<CachedPipeline>> all_pipelines_; - std::unordered_map<uint64_t, CachedPipeline*> cached_pipelines_; - GLuint point_list_geometry_program_ = 0; - GLuint rect_list_geometry_program_ = 0; - GLuint quad_list_geometry_program_ = 0; - GLuint line_quad_list_geometry_program_ = 0; - - TextureCache texture_cache_; - - DrawBatcher draw_batcher_; - xe::ui::gl::CircularBuffer scratch_buffer_; - - private: - 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 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_; - struct UpdateRasterizerStateRegisters { - uint32_t pa_su_sc_mode_cntl; - uint32_t pa_sc_screen_scissor_tl; - uint32_t pa_sc_screen_scissor_br; - uint32_t multi_prim_ib_reset_index; - uint32_t pa_sc_viz_query; - PrimitiveType prim_type; - - UpdateRasterizerStateRegisters() { Reset(); } - void Reset() { std::memset(this, 0, sizeof(*this)); } - } update_rasterizer_state_regs_; - struct UpdateBlendStateRegisters { - uint32_t rb_blendcontrol[4]; - float rb_blend_rgba[4]; - - UpdateBlendStateRegisters() { Reset(); } - void Reset() { std::memset(this, 0, sizeof(*this)); } - } update_blend_state_regs_; - 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_; - struct UpdateShadersRegisters { - PrimitiveType prim_type; - uint32_t pa_su_sc_mode_cntl; - uint32_t sq_program_cntl; - uint32_t sq_context_misc; - GL4Shader* vertex_shader; - GL4Shader* pixel_shader; - - UpdateShadersRegisters() { Reset(); } - void Reset() { - sq_program_cntl = 0; - vertex_shader = pixel_shader = nullptr; - } - } update_shaders_regs_; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_GL4_COMMAND_PROCESSOR_H_ diff --git a/src/xenia/gpu/gl4/gl4_gpu_flags.cc b/src/xenia/gpu/gl4/gl4_gpu_flags.cc deleted file mode 100644 index 3844bfc95..000000000 --- a/src/xenia/gpu/gl4/gl4_gpu_flags.cc +++ /dev/null @@ -1,17 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/gpu/gl4/gl4_gpu_flags.h" - -DEFINE_bool(disable_framebuffer_readback, false, - "Disable framebuffer readback."); -DEFINE_bool(disable_textures, false, "Disable textures and use colors only."); -DEFINE_string(shader_cache_dir, "", - "GL4 Shader cache directory (relative to Xenia). Specify an " - "empty string to disable the cache."); diff --git a/src/xenia/gpu/gl4/gl4_gpu_flags.h b/src/xenia/gpu/gl4/gl4_gpu_flags.h deleted file mode 100644 index 9f68287f9..000000000 --- a/src/xenia/gpu/gl4/gl4_gpu_flags.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_GPU_GL4_GL4_GPU_FLAGS_H_ -#define XENIA_GPU_GL4_GL4_GPU_FLAGS_H_ - -#include <gflags/gflags.h> - -DECLARE_bool(disable_framebuffer_readback); -DECLARE_bool(disable_textures); -DECLARE_string(shader_cache_dir); - -#define FINE_GRAINED_DRAW_SCOPES 0 - -#endif // XENIA_GPU_GL4_GL4_GPU_FLAGS_H_ diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.cc b/src/xenia/gpu/gl4/gl4_graphics_system.cc deleted file mode 100644 index efca2fa08..000000000 --- a/src/xenia/gpu/gl4/gl4_graphics_system.cc +++ /dev/null @@ -1,86 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/gpu/gl4/gl4_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/gl4/gl4_command_processor.h" -#include "xenia/gpu/gl4/gl4_gpu_flags.h" -#include "xenia/gpu/gpu_flags.h" -#include "xenia/ui/gl/gl_provider.h" -#include "xenia/ui/window.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -GL4GraphicsSystem::GL4GraphicsSystem() = default; - -GL4GraphicsSystem::~GL4GraphicsSystem() = default; - -X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor, - kernel::KernelState* kernel_state, - ui::Window* target_window) { - // Must create the provider so we can create contexts. - provider_ = xe::ui::gl::GLProvider::Create(target_window); - - auto result = GraphicsSystem::Setup(processor, kernel_state, target_window); - if (result) { - return result; - } - - display_context_ = - reinterpret_cast<xe::ui::gl::GLContext*>(target_window->context()); - - return X_STATUS_SUCCESS; -} - -void GL4GraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); } - -std::unique_ptr<CommandProcessor> GL4GraphicsSystem::CreateCommandProcessor() { - return std::unique_ptr<CommandProcessor>( - new GL4CommandProcessor(this, kernel_state_)); -} - -void GL4GraphicsSystem::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; - } - - // Blit the frontbuffer. - display_context_->blitter()->BlitTexture2D( - static_cast<GLuint>(swap_state.front_buffer_texture), - Rect2D(0, 0, swap_state.width, swap_state.height), - Rect2D(0, 0, target_window_->width(), target_window_->height()), - GL_LINEAR, false); -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.h b/src/xenia/gpu/gl4/gl4_graphics_system.h deleted file mode 100644 index 9644db075..000000000 --- a/src/xenia/gpu/gl4/gl4_graphics_system.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_GPU_GL4_GL4_GRAPHICS_SYSTEM_H_ -#define XENIA_GPU_GL4_GL4_GRAPHICS_SYSTEM_H_ - -#include <memory> - -#include "xenia/gpu/graphics_system.h" -#include "xenia/ui/gl/gl_context.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -class GL4GraphicsSystem : public GraphicsSystem { - public: - GL4GraphicsSystem(); - ~GL4GraphicsSystem() override; - - std::wstring name() const override { return L"GL4"; } - - 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::gl::GLContext* display_context_ = nullptr; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_GL4_GRAPHICS_SYSTEM_H_ diff --git a/src/xenia/gpu/gl4/gl4_shader.cc b/src/xenia/gpu/gl4/gl4_shader.cc deleted file mode 100644 index 7f34b1957..000000000 --- a/src/xenia/gpu/gl4/gl4_shader.cc +++ /dev/null @@ -1,298 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/gpu/gl4/gl4_shader.h" - -#include "xenia/base/logging.h" -#include "xenia/base/math.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -GL4Shader::GL4Shader(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) {} - -GL4Shader::~GL4Shader() { - glDeleteProgram(program_); - glDeleteVertexArrays(1, &vao_); -} - -bool GL4Shader::Prepare() { - // Build static vertex array descriptor. - if (!PrepareVertexArrayObject()) { - XELOGE("Unable to prepare vertex shader array object"); - return false; - } - - bool success = true; - if (!CompileShader()) { - host_error_log_ = GetShaderInfoLog(); - success = false; - } - if (success && !LinkProgram()) { - host_error_log_ = GetProgramInfoLog(); - success = false; - } - - if (success) { - host_binary_ = GetBinary(); - host_disassembly_ = GetHostDisasmNV(host_binary_); - } - is_valid_ = success; - - return success; -} - -bool GL4Shader::LoadFromBinary(const uint8_t* blob, GLenum binary_format, - size_t length) { - program_ = glCreateProgram(); - glProgramBinary(program_, binary_format, blob, GLsizei(length)); - - GLint link_status = 0; - glGetProgramiv(program_, GL_LINK_STATUS, &link_status); - if (!link_status) { - // Failed to link. Not fatal - just clean up so we can get generated later. - XELOGD("GL4Shader::LoadFromBinary failed. Log:\n%s", - GetProgramInfoLog().c_str()); - glDeleteProgram(program_); - program_ = 0; - - return false; - } - - // Build static vertex array descriptor. - if (!PrepareVertexArrayObject()) { - XELOGE("Unable to prepare vertex shader array object"); - return false; - } - - // Success! - host_binary_ = GetBinary(); - host_disassembly_ = GetHostDisasmNV(host_binary_); - - is_valid_ = true; - return true; -} - -bool GL4Shader::PrepareVertexArrayObject() { - glCreateVertexArrays(1, &vao_); - - for (const auto& vertex_binding : vertex_bindings()) { - for (const auto& attrib : vertex_binding.attributes) { - auto comp_count = GetVertexFormatComponentCount( - attrib.fetch_instr.attributes.data_format); - GLenum comp_type = 0; - bool is_signed = attrib.fetch_instr.attributes.is_signed; - switch (attrib.fetch_instr.attributes.data_format) { - case VertexFormat::k_8_8_8_8: - comp_type = is_signed ? GL_BYTE : GL_UNSIGNED_BYTE; - break; - case VertexFormat::k_2_10_10_10: - comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT; - comp_count = 1; - break; - case VertexFormat::k_10_11_11: - comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT; - comp_count = 1; - break; - case VertexFormat::k_11_11_10: - assert_true(is_signed); - comp_type = is_signed ? GL_R11F_G11F_B10F : 0; - break; - case VertexFormat::k_16_16: - comp_type = is_signed ? GL_SHORT : GL_UNSIGNED_SHORT; - break; - case VertexFormat::k_16_16_FLOAT: - comp_type = GL_HALF_FLOAT; - break; - case VertexFormat::k_16_16_16_16: - comp_type = is_signed ? GL_SHORT : GL_UNSIGNED_SHORT; - break; - case VertexFormat::k_16_16_16_16_FLOAT: - comp_type = GL_HALF_FLOAT; - break; - case VertexFormat::k_32: - comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT; - break; - case VertexFormat::k_32_32: - comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT; - break; - case VertexFormat::k_32_32_32_32: - comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT; - break; - case VertexFormat::k_32_FLOAT: - comp_type = GL_FLOAT; - break; - case VertexFormat::k_32_32_FLOAT: - comp_type = GL_FLOAT; - break; - case VertexFormat::k_32_32_32_FLOAT: - comp_type = GL_FLOAT; - break; - case VertexFormat::k_32_32_32_32_FLOAT: - comp_type = GL_FLOAT; - break; - default: - assert_unhandled_case(attrib.fetch_instr.attributes.data_format); - return false; - } - - glEnableVertexArrayAttrib(vao_, attrib.attrib_index); - glVertexArrayAttribBinding(vao_, attrib.attrib_index, - vertex_binding.binding_index); - glVertexArrayAttribFormat(vao_, attrib.attrib_index, comp_count, - comp_type, - !attrib.fetch_instr.attributes.is_integer, - attrib.fetch_instr.attributes.offset * 4); - } - } - - return true; -} - -bool GL4Shader::CompileShader() { - assert_zero(program_); - - shader_ = - glCreateShader(shader_type_ == ShaderType::kVertex ? GL_VERTEX_SHADER - : GL_FRAGMENT_SHADER); - if (!shader_) { - XELOGE("OpenGL could not create a shader object!"); - return false; - } - - auto source_str = GetTranslatedBinaryString(); - auto source_str_ptr = source_str.c_str(); - GLint source_length = GLint(source_str.length()); - glShaderSource(shader_, 1, &source_str_ptr, &source_length); - glCompileShader(shader_); - - GLint status = 0; - glGetShaderiv(shader_, GL_COMPILE_STATUS, &status); - - return status == GL_TRUE; -} - -bool GL4Shader::LinkProgram() { - program_ = glCreateProgram(); - if (!program_) { - XELOGE("OpenGL could not create a shader program!"); - return false; - } - - glAttachShader(program_, shader_); - - // Enable TFB - if (shader_type_ == ShaderType::kVertex) { - const GLchar* feedbackVaryings = "gl_Position"; - glTransformFeedbackVaryings(program_, 1, &feedbackVaryings, - GL_SEPARATE_ATTRIBS); - } - - glProgramParameteri(program_, GL_PROGRAM_SEPARABLE, GL_TRUE); - glLinkProgram(program_); - - GLint link_status = 0; - glGetProgramiv(program_, GL_LINK_STATUS, &link_status); - if (!link_status) { - assert_always("Unable to link generated shader"); - return false; - } - - return true; -} - -std::string GL4Shader::GetShaderInfoLog() { - if (!shader_) { - return "GL4Shader::GetShaderInfoLog(): Program is NULL"; - } - - std::string log; - GLint log_length = 0; - glGetShaderiv(shader_, GL_INFO_LOG_LENGTH, &log_length); - if (log_length > 0) { - log.resize(log_length - 1); - glGetShaderInfoLog(shader_, log_length, &log_length, &log[0]); - } - - return log; -} - -std::string GL4Shader::GetProgramInfoLog() { - if (!program_) { - return "GL4Shader::GetProgramInfoLog(): Program is NULL"; - } - - std::string log; - GLint log_length = 0; - glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &log_length); - if (log_length > 0) { - log.resize(log_length - 1); - glGetProgramInfoLog(program_, log_length, &log_length, &log[0]); - } - - return log; -} - -std::vector<uint8_t> GL4Shader::GetBinary(GLenum* binary_format) { - std::vector<uint8_t> binary; - - // Get program binary, if it's available. - GLint binary_length = 0; - glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &binary_length); - if (binary_length) { - binary.resize(binary_length); - GLenum binary_format_tmp = 0; - glGetProgramBinary(program_, binary_length, &binary_length, - &binary_format_tmp, binary.data()); - - if (binary_format) { - *binary_format = binary_format_tmp; - } - } - - return binary; -} - -std::string GL4Shader::GetHostDisasmNV(const std::vector<uint8_t>& binary) { - // If we are on nvidia, we can find the disassembly string. - // I haven't been able to figure out from the format how to do this - // without a search like this. - std::string disasm; - - const char* disasm_start = nullptr; - size_t search_offset = 0; - const char* search_start = reinterpret_cast<const char*>(binary.data()); - while (true) { - auto p = reinterpret_cast<const char*>(memchr( - binary.data() + search_offset, '!', binary.size() - search_offset)); - if (!p) { - break; - } - if (p[0] == '!' && p[1] == '!' && p[2] == 'N' && p[3] == 'V') { - disasm_start = p; - break; - } - search_offset = p - search_start; - ++search_offset; - } - if (disasm_start) { - disasm = std::string(disasm_start); - } else { - disasm = std::string("Shader disassembly not available."); - } - - return disasm; -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe diff --git a/src/xenia/gpu/gl4/gl4_shader.h b/src/xenia/gpu/gl4/gl4_shader.h deleted file mode 100644 index 1f884ebf2..000000000 --- a/src/xenia/gpu/gl4/gl4_shader.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_GPU_GL4_GL4_SHADER_H_ -#define XENIA_GPU_GL4_GL4_SHADER_H_ - -#include <string> - -#include "xenia/gpu/shader.h" -#include "xenia/ui/gl/gl_context.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -class GL4Shader : public Shader { - public: - GL4Shader(ShaderType shader_type, uint64_t data_hash, - const uint32_t* dword_ptr, uint32_t dword_count); - ~GL4Shader() override; - - GLuint program() const { return program_; } - GLuint shader() const { return shader_; } - GLuint vao() const { return vao_; } - - bool Prepare(); - bool LoadFromBinary(const uint8_t* blob, GLenum binary_format, size_t length); - std::vector<uint8_t> GetBinary(GLenum* binary_format = nullptr); - - protected: - bool PrepareVertexArrayObject(); - bool CompileShader(); - bool LinkProgram(); - - std::string GetShaderInfoLog(); - std::string GetProgramInfoLog(); - static std::string GetHostDisasmNV(const std::vector<uint8_t>& binary); - - GLuint program_ = 0; - GLuint shader_ = 0; - GLuint vao_ = 0; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_GL4_SHADER_H_ diff --git a/src/xenia/gpu/gl4/gl4_shader_cache.cc b/src/xenia/gpu/gl4/gl4_shader_cache.cc deleted file mode 100644 index 714de3e1d..000000000 --- a/src/xenia/gpu/gl4/gl4_shader_cache.cc +++ /dev/null @@ -1,187 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/gpu/gl4/gl4_shader_cache.h" - -#include <cinttypes> - -#include "xenia/base/filesystem.h" -#include "xenia/base/logging.h" -#include "xenia/base/mapped_memory.h" -#include "xenia/gpu/gl4/gl4_gpu_flags.h" -#include "xenia/gpu/gl4/gl4_shader.h" -#include "xenia/gpu/glsl_shader_translator.h" -#include "xenia/gpu/gpu_flags.h" - -#include "third_party/xxhash/xxhash.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -GL4ShaderCache::GL4ShaderCache(GlslShaderTranslator* shader_translator) - : shader_translator_(shader_translator) {} - -GL4ShaderCache::~GL4ShaderCache() {} - -void GL4ShaderCache::Reset() { - shader_map_.clear(); - all_shaders_.clear(); -} - -GL4Shader* GL4ShaderCache::LookupOrInsertShader(ShaderType shader_type, - const uint32_t* dwords, - uint32_t dword_count) { - // Hash the input memory and lookup the shader. - GL4Shader* shader_ptr = nullptr; - uint64_t hash = XXH64(dwords, dword_count * sizeof(uint32_t), 0); - auto it = shader_map_.find(hash); - if (it != shader_map_.end()) { - // Shader has been previously loaded. - // TODO(benvanik): compare bytes? Likelihood of collision is low. - shader_ptr = it->second; - } else { - // Check filesystem cache. - shader_ptr = FindCachedShader(shader_type, hash, dwords, dword_count); - if (shader_ptr) { - // Found! - XELOGGPU("Loaded %s shader from cache (hash: %.16" PRIX64 ")", - shader_type == ShaderType::kVertex ? "vertex" : "pixel", hash); - return shader_ptr; - } - - // Not found in cache - load from scratch. - auto shader = - std::make_unique<GL4Shader>(shader_type, hash, dwords, dword_count); - shader_ptr = shader.get(); - shader_map_.insert({hash, shader_ptr}); - all_shaders_.emplace_back(std::move(shader)); - - // Perform translation. - // If this fails the shader will be marked as invalid and ignored later. - if (shader_translator_->Translate(shader_ptr)) { - shader_ptr->Prepare(); - if (shader_ptr->is_valid()) { - CacheShader(shader_ptr); - - XELOGGPU("Generated %s shader at 0x%.16" PRIX64 " (%db):\n%s", - shader_type == ShaderType::kVertex ? "vertex" : "pixel", - dwords, dword_count * 4, - shader_ptr->ucode_disassembly().c_str()); - } - - // Dump shader files if desired. - if (!FLAGS_dump_shaders.empty()) { - shader_ptr->Dump(FLAGS_dump_shaders, "gl4"); - } - } else { - XELOGE("Shader failed translation"); - } - } - - return shader_ptr; -} - -void GL4ShaderCache::CacheShader(GL4Shader* shader) { - if (FLAGS_shader_cache_dir.empty()) { - // Cache disabled. - return; - } - - GLenum binary_format = 0; - auto binary = shader->GetBinary(&binary_format); - if (binary.size() == 0) { - // No binary returned. - return; - } - - auto cache_dir = xe::to_absolute_path(xe::to_wstring(FLAGS_shader_cache_dir)); - xe::filesystem::CreateFolder(cache_dir); - auto filename = - cache_dir + xe::format_string( - L"%.16" PRIX64 ".%s", shader->ucode_data_hash(), - shader->type() == ShaderType::kPixel ? L"frag" : L"vert"); - auto file = xe::filesystem::OpenFile(filename, "wb"); - if (!file) { - // Not fatal, but not too good. - return; - } - - std::vector<uint8_t> cached_shader_mem; - // Resize this vector to the final filesize (- 1 to account for dummy array - // in CachedShader) - cached_shader_mem.resize(sizeof(CachedShader) + binary.size() - 1); - auto cached_shader = - reinterpret_cast<CachedShader*>(cached_shader_mem.data()); - cached_shader->magic = xe::byte_swap('XSHD'); - cached_shader->version = 0; // TODO - cached_shader->shader_type = uint8_t(shader->type()); - cached_shader->binary_len = uint32_t(binary.size()); - cached_shader->binary_format = binary_format; - std::memcpy(cached_shader->binary, binary.data(), binary.size()); - - fwrite(cached_shader_mem.data(), cached_shader_mem.size(), 1, file); - fclose(file); -} - -GL4Shader* GL4ShaderCache::FindCachedShader(ShaderType shader_type, - uint64_t hash, - const uint32_t* dwords, - uint32_t dword_count) { - if (FLAGS_shader_cache_dir.empty()) { - // Cache disabled. - return nullptr; - } - - auto cache_dir = xe::to_absolute_path(xe::to_wstring(FLAGS_shader_cache_dir)); - auto filename = - cache_dir + - xe::format_string(L"%.16" PRIX64 ".%s", hash, - shader_type == ShaderType::kPixel ? L"frag" : L"vert"); - if (!xe::filesystem::PathExists(filename)) { - return nullptr; - } - - // Shader is cached. Open it up. - auto map = xe::MappedMemory::Open(filename, MappedMemory::Mode::kRead); - if (!map) { - // Should not fail - assert_always(); - return nullptr; - } - - auto cached_shader = reinterpret_cast<CachedShader*>(map->data()); - // TODO: Compare versions - if (cached_shader->magic != xe::byte_swap('XSHD')) { - return nullptr; - } - - auto shader = - std::make_unique<GL4Shader>(shader_type, hash, dwords, dword_count); - - // Gather the binding points. - // TODO: Make Shader do this on construction. - // TODO: Regenerate microcode disasm/etc on load. - shader_translator_->GatherAllBindingInformation(shader.get()); - if (!shader->LoadFromBinary(cached_shader->binary, - cached_shader->binary_format, - cached_shader->binary_len)) { - // Failed to load from binary. - return nullptr; - } - - auto shader_ptr = shader.get(); - shader_map_.insert({hash, shader_ptr}); - all_shaders_.emplace_back(std::move(shader)); - return shader_ptr; -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe diff --git a/src/xenia/gpu/gl4/gl4_shader_cache.h b/src/xenia/gpu/gl4/gl4_shader_cache.h deleted file mode 100644 index 9c5c77cb2..000000000 --- a/src/xenia/gpu/gl4/gl4_shader_cache.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_GPU_GL4_SHADER_CACHE_H_ -#define XENIA_GPU_GL4_SHADER_CACHE_H_ - -#include <cstdint> -#include <cstring> -#include <memory> -#include <unordered_map> -#include <vector> - -#include "xenia/gpu/xenos.h" - -namespace xe { -namespace gpu { -class GlslShaderTranslator; - -namespace gl4 { - -class GL4Shader; - -class GL4ShaderCache { - public: - GL4ShaderCache(GlslShaderTranslator* shader_translator); - ~GL4ShaderCache(); - - void Reset(); - GL4Shader* LookupOrInsertShader(ShaderType shader_type, - const uint32_t* dwords, uint32_t dword_count); - - private: - // Cached shader file format. - struct CachedShader { - uint32_t magic; - uint32_t version; // Version of the shader translator used. - uint8_t shader_type; // ShaderType enum - uint32_t binary_len; // Code length - uint32_t binary_format; // Binary format (from OpenGL) - uint8_t binary[1]; // Code - }; - - void CacheShader(GL4Shader* shader); - GL4Shader* FindCachedShader(ShaderType shader_type, uint64_t hash, - const uint32_t* dwords, uint32_t dword_count); - - GlslShaderTranslator* shader_translator_ = nullptr; - std::vector<std::unique_ptr<GL4Shader>> all_shaders_; - std::unordered_map<uint64_t, GL4Shader*> shader_map_; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_SHADER_CACHE_H_ diff --git a/src/xenia/gpu/gl4/gl4_trace_viewer_main.cc b/src/xenia/gpu/gl4/gl4_trace_viewer_main.cc deleted file mode 100644 index c7d24001c..000000000 --- a/src/xenia/gpu/gl4/gl4_trace_viewer_main.cc +++ /dev/null @@ -1,109 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/base/logging.h" -#include "xenia/base/main.h" -#include "xenia/gpu/gl4/gl4_command_processor.h" -#include "xenia/gpu/gl4/gl4_graphics_system.h" -#include "xenia/gpu/trace_viewer.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -using namespace xe::gpu::xenos; - -class GL4TraceViewer : public TraceViewer { - public: - std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() override { - return std::unique_ptr<gpu::GraphicsSystem>(new GL4GraphicsSystem()); - } - - uintptr_t GetColorRenderTarget(uint32_t pitch, MsaaSamples samples, - uint32_t base, - ColorRenderTargetFormat format) override { - auto command_processor = static_cast<GL4CommandProcessor*>( - graphics_system_->command_processor()); - return command_processor->GetColorRenderTarget(pitch, samples, base, - format); - } - - uintptr_t GetDepthRenderTarget(uint32_t pitch, MsaaSamples samples, - uint32_t base, - DepthRenderTargetFormat format) override { - auto command_processor = static_cast<GL4CommandProcessor*>( - graphics_system_->command_processor()); - return command_processor->GetDepthRenderTarget(pitch, samples, base, - format); - } - - uintptr_t GetTextureEntry(const TextureInfo& texture_info, - const SamplerInfo& sampler_info) override { - auto command_processor = static_cast<GL4CommandProcessor*>( - 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); - } - - size_t QueryVSOutputSize() override { - auto command_processor = static_cast<GL4CommandProcessor*>( - graphics_system_->command_processor()); - auto draw_batcher = command_processor->draw_batcher(); - - return draw_batcher->QueryTFBSize(); - } - - size_t QueryVSOutputElementSize() override { - // vec4 always has 4 elements. - return 4; - } - - bool QueryVSOutput(void* buffer, size_t size) override { - auto command_processor = static_cast<GL4CommandProcessor*>( - graphics_system_->command_processor()); - auto draw_batcher = command_processor->draw_batcher(); - - return draw_batcher->ReadbackTFB(buffer, size); - } - - bool Setup() override { - if (!TraceViewer::Setup()) { - return false; - } - - // Enable TFB - auto command_processor = static_cast<GL4CommandProcessor*>( - graphics_system_->command_processor()); - auto draw_batcher = command_processor->draw_batcher(); - draw_batcher->set_tfb_enabled(true); - - return true; - } - - private: -}; - -int trace_viewer_main(const std::vector<std::wstring>& args) { - GL4TraceViewer trace_viewer; - return trace_viewer.Main(args); -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -DEFINE_ENTRY_POINT(L"xenia-gpu-gl4-trace-viewer", - L"xenia-gpu-gl4-trace-viewer some.trace", - xe::gpu::gl4::trace_viewer_main); diff --git a/src/xenia/gpu/gl4/premake5.lua b/src/xenia/gpu/gl4/premake5.lua deleted file mode 100644 index 6c61b285a..000000000 --- a/src/xenia/gpu/gl4/premake5.lua +++ /dev/null @@ -1,88 +0,0 @@ -project_root = "../../../.." -include(project_root.."/tools/build") - -group("src") -project("xenia-gpu-gl4") - uuid("da10149d-efb0-44aa-924c-a76a46e1f04d") - kind("StaticLib") - language("C++") - links({ - "glew", - "xenia-base", - "xenia-gpu", - "xenia-ui", - "xenia-ui-gl", - "xxhash", - }) - defines({ - "GLEW_STATIC=1", - "GLEW_MX=1", - }) - includedirs({ - project_root.."/third_party/gflags/src", - }) - local_platform_files() - --- TODO(benvanik): kill this and move to the debugger UI. -group("src") -project("xenia-gpu-gl4-trace-viewer") - uuid("450f965b-a019-4ba5-bc6f-99901e5a4c8d") - kind("WindowedApp") - language("C++") - links({ - "capstone", - "gflags", - "glew", - "imgui", - "libavcodec", - "libavutil", - "snappy", - "xenia-apu", - "xenia-apu-nop", - "xenia-base", - "xenia-core", - "xenia-cpu", - "xenia-cpu-backend-x64", - "xenia-gpu", - "xenia-gpu-gl4", - "xenia-hid", - "xenia-hid-nop", - "xenia-kernel", - "xenia-ui", - "xenia-ui-gl", - "xenia-vfs", - "xxhash", - }) - flags({ - "WinMain", -- Use WinMain instead of main. - }) - defines({ - "GLEW_STATIC=1", - "GLEW_MX=1", - }) - includedirs({ - project_root.."/third_party/gflags/src", - }) - files({ - "gl4_trace_viewer_main.cc", - "../../base/main_"..platform_suffix..".cc", - }) - - filter("platforms:Windows") - links({ - "xenia-apu-xaudio2", - "xenia-hid-winkey", - "xenia-hid-xinput", - }) - - -- Only create the .user file if it doesn't already exist. - local user_file = project_root.."/build/xenia-gpu-gl4-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 -
\ No newline at end of file diff --git a/src/xenia/gpu/gl4/texture_cache.cc b/src/xenia/gpu/gl4/texture_cache.cc deleted file mode 100644 index affd012bd..000000000 --- a/src/xenia/gpu/gl4/texture_cache.cc +++ /dev/null @@ -1,1101 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/gpu/gl4/texture_cache.h" - -#include <algorithm> -#include <cstring> - -#include "xenia/base/assert.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" - -namespace xe { -namespace gpu { -namespace gl4 { - -struct TextureConfig { - TextureFormat texture_format; - GLenum internal_format; - GLenum format; - GLenum type; -}; - -// https://code.google.com/p/glsnewton/source/browse/trunk/Source/uDDSLoader.pas?r=62 -// http://dench.flatlib.jp/opengl/textures -// http://fossies.org/linux/WebKit/Source/ThirdParty/ANGLE/src/libGLESv2/formatutils.cpp -static const TextureConfig texture_configs[64] = { - {TextureFormat::k_1_REVERSE, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8, GL_R8, GL_RED, GL_UNSIGNED_BYTE}, - {TextureFormat::k_1_5_5_5, GL_RGB5_A1, GL_RGBA, - GL_UNSIGNED_SHORT_1_5_5_5_REV}, - {TextureFormat::k_5_6_5, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, - {TextureFormat::k_6_5_5, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, - {TextureFormat::k_2_10_10_10, GL_RGB10_A2, GL_RGBA, - GL_UNSIGNED_INT_2_10_10_10_REV}, - {TextureFormat::k_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_B, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_8, GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, - {TextureFormat::k_Cr_Y1_Cb_Y0, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_Y1_Cr_Y0_Cb, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_4_4_4_4, GL_RGBA4, GL_RGBA, - GL_UNSIGNED_SHORT_4_4_4_4_REV}, - {TextureFormat::k_10_11_11, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, // ? - {TextureFormat::k_11_11_10, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, // ? - {TextureFormat::k_DXT1, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT2_3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT4_5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_24_8, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, - GL_UNSIGNED_INT_24_8}, - {TextureFormat::k_24_8_FLOAT, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, - GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, - {TextureFormat::k_16, GL_R16, GL_RED, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16, GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16_16_16, GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_EXPAND, GL_R16, GL_RED, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16_EXPAND, GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16_16_16_EXPAND, GL_RGBA16, GL_RGBA, - GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT}, - {TextureFormat::k_16_16_FLOAT, GL_RG16F, GL_RG, GL_HALF_FLOAT}, - {TextureFormat::k_16_16_16_16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, - {TextureFormat::k_32, GL_R32I, GL_RED, GL_UNSIGNED_INT}, - {TextureFormat::k_32_32, GL_RG32I, GL_RG, GL_UNSIGNED_INT}, - {TextureFormat::k_32_32_32_32, GL_RGBA32I, GL_RGBA, GL_UNSIGNED_INT}, - {TextureFormat::k_32_FLOAT, GL_R32F, GL_RED, GL_FLOAT}, - {TextureFormat::k_32_32_FLOAT, GL_RG32F, GL_RG, GL_FLOAT}, - {TextureFormat::k_32_32_32_32_FLOAT, GL_RGBA32F, GL_RGBA, GL_FLOAT}, - {TextureFormat::k_32_AS_8, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_8, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_DXN, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, - GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8_AS_16_16_16_16, GL_RGBA8, GL_RGBA, - GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT1_AS_16_16_16_16, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, - GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT2_3_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT4_5_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_2_10_10_10_AS_16_16_16_16, GL_RGB10_A2, GL_RGBA, - GL_UNSIGNED_INT_2_10_10_10_REV}, - {TextureFormat::k_10_11_11_AS_16_16_16_16, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, - {TextureFormat::k_11_11_10_AS_16_16_16_16, GL_R11F_G11F_B10F, - GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_32_32_32_FLOAT, GL_RGB32F, GL_RGB, GL_FLOAT}, - {TextureFormat::k_DXT3A, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT5A, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_CTX1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_DXT3A_AS_1_1_1_1, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, -}; - -TextureCache::TextureCache() : memory_(nullptr), scratch_buffer_(nullptr) { - invalidated_textures_sets_[0].reserve(64); - invalidated_textures_sets_[1].reserve(64); - invalidated_textures_ = &invalidated_textures_sets_[0]; -} - -TextureCache::~TextureCache() { Shutdown(); } - -bool TextureCache::Initialize(Memory* memory, CircularBuffer* scratch_buffer) { - memory_ = memory; - scratch_buffer_ = scratch_buffer; - return true; -} - -void TextureCache::Shutdown() { Clear(); } - -void TextureCache::Scavenge() { - invalidated_textures_mutex_.lock(); - std::vector<TextureEntry*>& invalidated_textures = *invalidated_textures_; - if (invalidated_textures_ == &invalidated_textures_sets_[0]) { - invalidated_textures_ = &invalidated_textures_sets_[1]; - } else { - invalidated_textures_ = &invalidated_textures_sets_[0]; - } - invalidated_textures_mutex_.unlock(); - if (invalidated_textures.empty()) { - return; - } - - for (auto& entry : invalidated_textures) { - EvictTexture(entry); - } - invalidated_textures.clear(); -} - -void TextureCache::Clear() { - EvictAllTextures(); - - // Samplers must go last, as textures depend on them. - while (sampler_entries_.size()) { - auto entry = sampler_entries_.begin()->second; - EvictSampler(entry); - } -} - -void TextureCache::EvictAllTextures() { - // Kill all textures - some may be in the eviction list, but that's fine - // as we will clear that below. - while (!texture_entries_.empty()) { - auto entry = texture_entries_.begin()->second; - EvictTexture(entry); - } - - { - std::lock_guard<std::mutex> lock(invalidated_textures_mutex_); - invalidated_textures_sets_[0].clear(); - invalidated_textures_sets_[1].clear(); - } - - // Kill all readbuffer textures. - while (!read_buffer_textures_.empty()) { - auto it = --read_buffer_textures_.end(); - auto entry = *it; - glDeleteTextures(1, &entry->handle); - delete entry; - read_buffer_textures_.erase(it); - } -} - -TextureCache::TextureEntryView* TextureCache::Demand( - const TextureInfo& texture_info, const SamplerInfo& sampler_info) { - uint64_t texture_hash = texture_info.hash(); - auto texture_entry = LookupOrInsertTexture(texture_info, texture_hash); - if (!texture_entry) { - XELOGE("Failed to setup texture"); - return nullptr; - } - - // We likely have the sampler in the texture view listing, so scan for it. - uint64_t sampler_hash = sampler_info.hash(); - for (auto& it : texture_entry->views) { - if (it->sampler_hash == sampler_hash) { - // Found. - return it.get(); - } - } - - // No existing view found - build it. - auto sampler_entry = LookupOrInsertSampler(sampler_info, sampler_hash); - if (!sampler_entry) { - XELOGE("Failed to setup texture sampler"); - return nullptr; - } - - auto view = std::make_unique<TextureEntryView>(); - view->texture = texture_entry; - view->sampler = sampler_entry; - view->sampler_hash = sampler_hash; - view->texture_sampler_handle = 0; - - // Get the uvec2 handle to the texture/sampler pair and make it resident. - // The handle can be passed directly to the shader. - view->texture_sampler_handle = glGetTextureSamplerHandleARB( - texture_entry->handle, sampler_entry->handle); - if (!view->texture_sampler_handle) { - assert_always("Unable to get texture handle?"); - return nullptr; - } - glMakeTextureHandleResidentARB(view->texture_sampler_handle); - - // Entry takes ownership. - auto view_ptr = view.get(); - texture_entry->views.push_back(std::move(view)); - return view_ptr; -} - -TextureCache::SamplerEntry* TextureCache::LookupOrInsertSampler( - const SamplerInfo& sampler_info, uint64_t opt_hash) { - const uint64_t hash = opt_hash ? opt_hash : sampler_info.hash(); - for (auto it = sampler_entries_.find(hash); it != sampler_entries_.end(); - ++it) { - if (it->second->sampler_info == sampler_info) { - // Found in cache! - return it->second; - } - } - - // Not found, create. - auto entry = std::make_unique<SamplerEntry>(); - entry->sampler_info = sampler_info; - glCreateSamplers(1, &entry->handle); - - // TODO(benvanik): border color from texture fetch. - GLfloat border_color[4] = {0.0f}; - glSamplerParameterfv(entry->handle, GL_TEXTURE_BORDER_COLOR, border_color); - - // TODO(benvanik): setup LODs for mipmapping. - glSamplerParameterf(entry->handle, GL_TEXTURE_LOD_BIAS, 0.0f); - glSamplerParameterf(entry->handle, GL_TEXTURE_MIN_LOD, 0.0f); - glSamplerParameterf(entry->handle, GL_TEXTURE_MAX_LOD, 0.0f); - - // Texture wrapping modes. - // TODO(benvanik): not sure if the middle ones are correct. - static const GLenum wrap_map[] = { - GL_REPEAT, // - GL_MIRRORED_REPEAT, // - GL_CLAMP_TO_EDGE, // - GL_MIRROR_CLAMP_TO_EDGE, // - GL_CLAMP_TO_BORDER, // ? - GL_MIRROR_CLAMP_TO_BORDER_EXT, // ? - GL_CLAMP_TO_BORDER, // - GL_MIRROR_CLAMP_TO_BORDER_EXT, // - }; - glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_S, - wrap_map[static_cast<int>(sampler_info.clamp_u)]); - glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_T, - wrap_map[static_cast<int>(sampler_info.clamp_v)]); - glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_R, - wrap_map[static_cast<int>(sampler_info.clamp_w)]); - - // Texture level filtering. - GLenum min_filter; - switch (sampler_info.min_filter) { - case TextureFilter::kPoint: - switch (sampler_info.mip_filter) { - case TextureFilter::kBaseMap: - min_filter = GL_NEAREST; - break; - case TextureFilter::kPoint: - // min_filter = GL_NEAREST_MIPMAP_NEAREST; - min_filter = GL_NEAREST; - break; - case TextureFilter::kLinear: - // min_filter = GL_NEAREST_MIPMAP_LINEAR; - min_filter = GL_NEAREST; - break; - default: - assert_unhandled_case(sampler_info.mip_filter); - return nullptr; - } - break; - case TextureFilter::kLinear: - switch (sampler_info.mip_filter) { - case TextureFilter::kBaseMap: - min_filter = GL_LINEAR; - break; - case TextureFilter::kPoint: - // min_filter = GL_LINEAR_MIPMAP_NEAREST; - min_filter = GL_LINEAR; - break; - case TextureFilter::kLinear: - // min_filter = GL_LINEAR_MIPMAP_LINEAR; - min_filter = GL_LINEAR; - break; - default: - assert_unhandled_case(sampler_info.mip_filter); - return nullptr; - } - break; - default: - assert_unhandled_case(sampler_info.min_filter); - return nullptr; - } - GLenum mag_filter; - switch (sampler_info.mag_filter) { - case TextureFilter::kPoint: - mag_filter = GL_NEAREST; - break; - case TextureFilter::kLinear: - mag_filter = GL_LINEAR; - break; - default: - assert_unhandled_case(mag_filter); - return nullptr; - } - glSamplerParameteri(entry->handle, GL_TEXTURE_MIN_FILTER, min_filter); - glSamplerParameteri(entry->handle, GL_TEXTURE_MAG_FILTER, mag_filter); - - GLfloat aniso; - switch (sampler_info.aniso_filter) { - case AnisoFilter::kDisabled: - aniso = 0.0f; - break; - case AnisoFilter::kMax_1_1: - aniso = 1.0f; - break; - case AnisoFilter::kMax_2_1: - aniso = 2.0f; - break; - case AnisoFilter::kMax_4_1: - aniso = 4.0f; - break; - case AnisoFilter::kMax_8_1: - aniso = 8.0f; - break; - case AnisoFilter::kMax_16_1: - aniso = 16.0f; - break; - default: - assert_unhandled_case(aniso); - return nullptr; - } - - if (aniso) { - glSamplerParameterf(entry->handle, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso); - } - - // Add to map - map takes ownership. - auto entry_ptr = entry.get(); - sampler_entries_.insert({hash, entry.release()}); - return entry_ptr; -} - -void TextureCache::EvictSampler(SamplerEntry* entry) { - glDeleteSamplers(1, &entry->handle); - - for (auto it = sampler_entries_.find(entry->sampler_info.hash()); - it != sampler_entries_.end(); ++it) { - if (it->second == entry) { - sampler_entries_.erase(it); - break; - } - } - - delete entry; -} - -TextureCache::TextureEntry* TextureCache::LookupOrInsertTexture( - const TextureInfo& texture_info, uint64_t opt_hash) { - const uint64_t hash = opt_hash ? opt_hash : texture_info.hash(); - for (auto it = texture_entries_.find(hash); it != texture_entries_.end(); - ++it) { - if (it->second->pending_invalidation) { - // Whoa, we've been invalidated! Let's scavenge to cleanup and try again. - // TODO(benvanik): reuse existing texture storage. - Scavenge(); - break; - } - if (it->second->texture_info == texture_info) { - // Found in cache! - return it->second; - } - } - - // Not found, create. - auto entry = std::make_unique<TextureEntry>(); - entry->texture_info = texture_info; - entry->access_watch_handle = 0; - entry->pending_invalidation = false; - entry->handle = 0; - - // Check read buffer textures - there may be one waiting for us. - // TODO(benvanik): speed up existence check? - for (auto it = read_buffer_textures_.begin(); - it != read_buffer_textures_.end(); ++it) { - auto read_buffer_entry = *it; - if (read_buffer_entry->guest_address == texture_info.guest_address && - read_buffer_entry->block_width == texture_info.size_2d.block_width && - read_buffer_entry->block_height == texture_info.size_2d.block_height) { - // Found! Acquire the handle and remove the readbuffer entry. - read_buffer_textures_.erase(it); - entry->handle = read_buffer_entry->handle; - entry->access_watch_handle = read_buffer_entry->access_watch_handle; - delete read_buffer_entry; - // TODO(benvanik): set more texture properties? swizzle/etc? - auto entry_ptr = entry.get(); - texture_entries_.insert({hash, entry.release()}); - return entry_ptr; - } - } - - GLenum target; - switch (texture_info.dimension) { - case Dimension::k1D: - target = GL_TEXTURE_1D; - break; - case Dimension::k2D: - target = GL_TEXTURE_2D; - break; - case Dimension::k3D: - target = GL_TEXTURE_3D; - break; - case Dimension::kCube: - target = GL_TEXTURE_CUBE_MAP; - break; - } - - // Setup the base texture. - glCreateTextures(target, 1, &entry->handle); - - // TODO(benvanik): texture mip levels. - glTextureParameteri(entry->handle, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(entry->handle, GL_TEXTURE_MAX_LEVEL, 1); - - // Upload/convert. - bool uploaded = false; - switch (texture_info.dimension) { - case Dimension::k1D: - uploaded = UploadTexture1D(entry->handle, texture_info); - break; - case Dimension::k2D: - uploaded = UploadTexture2D(entry->handle, texture_info); - break; - case Dimension::kCube: - uploaded = UploadTextureCube(entry->handle, texture_info); - break; - case Dimension::k3D: - assert_unhandled_case(texture_info.dimension); - return nullptr; - } - if (!uploaded) { - XELOGE("Failed to convert/upload texture"); - return nullptr; - } - - // 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 - // driver is likely in a better position to pool that kind of stuff. - entry->access_watch_handle = memory_->AddPhysicalAccessWatch( - texture_info.guest_address, texture_info.input_length, - cpu::MMIOHandler::kWatchWrite, - [](void* context_ptr, void* data_ptr, uint32_t address) { - auto self = reinterpret_cast<TextureCache*>(context_ptr); - auto touched_entry = reinterpret_cast<TextureEntry*>(data_ptr); - // Clear watch handle first so we don't redundantly - // remove. - touched_entry->access_watch_handle = 0; - touched_entry->pending_invalidation = true; - // Add to pending list so Scavenge will clean it up. - self->invalidated_textures_mutex_.lock(); - self->invalidated_textures_->push_back(touched_entry); - self->invalidated_textures_mutex_.unlock(); - }, - this, entry.get()); - - // Add to map - map takes ownership. - auto entry_ptr = entry.get(); - texture_entries_.insert({hash, entry.release()}); - return entry_ptr; -} - -TextureCache::TextureEntry* TextureCache::LookupAddress(uint32_t guest_address, - uint32_t width, - uint32_t height, - TextureFormat format) { - // TODO(benvanik): worth speeding up? - for (auto it = texture_entries_.begin(); it != texture_entries_.end(); ++it) { - const auto& texture_info = it->second->texture_info; - if (texture_info.guest_address == guest_address && - texture_info.dimension == Dimension::k2D && - texture_info.size_2d.input_width == width && - texture_info.size_2d.input_height == height) { - return it->second; - } - } - return nullptr; -} - -GLuint TextureCache::CopyTexture(Blitter* blitter, uint32_t guest_address, - uint32_t logical_width, - uint32_t logical_height, uint32_t block_width, - uint32_t block_height, TextureFormat format, - bool swap_channels, GLuint src_texture, - Rect2D src_rect, Rect2D dest_rect) { - return ConvertTexture(blitter, guest_address, logical_width, logical_height, - block_width, block_height, format, swap_channels, - src_texture, src_rect, dest_rect); -} - -GLuint TextureCache::ConvertTexture(Blitter* blitter, uint32_t guest_address, - uint32_t logical_width, - uint32_t logical_height, - uint32_t block_width, uint32_t block_height, - TextureFormat format, bool swap_channels, - GLuint src_texture, Rect2D src_rect, - Rect2D dest_rect) { - const auto& config = texture_configs[uint32_t(format)]; - if (config.format == GL_INVALID_ENUM) { - assert_always("Unhandled destination texture format"); - return 0; - } - - // See if we have used a texture at this address before. If we have, we can - // reuse it. - // TODO(benvanik): better lookup matching format/etc? - auto texture_entry = - LookupAddress(guest_address, block_width, block_height, format); - if (texture_entry) { - // Have existing texture. - assert_false(texture_entry->pending_invalidation); - if (config.format == GL_DEPTH_STENCIL) { - blitter->CopyDepthTexture(src_texture, src_rect, texture_entry->handle, - dest_rect); - } else { - blitter->CopyColorTexture2D(src_texture, src_rect, texture_entry->handle, - dest_rect, GL_LINEAR, swap_channels); - } - - // Setup a read/write access watch. If the game tries to touch the memory - // we were supposed to populate with this texture, then we'll actually - // populate it. - if (texture_entry->access_watch_handle) { - memory_->CancelAccessWatch(texture_entry->access_watch_handle); - 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; - } - - // Check pending read buffer textures (for multiple resolves with no - // uploads inbetween). - for (auto it = read_buffer_textures_.begin(); - it != read_buffer_textures_.end(); ++it) { - const auto& entry = *it; - if (entry->guest_address == guest_address && - entry->logical_width == logical_width && - entry->logical_height == logical_height && entry->format == format) { - // Found an existing entry - just reupload. - if (config.format == GL_DEPTH_STENCIL) { - blitter->CopyDepthTexture(src_texture, src_rect, entry->handle, - dest_rect); - } else { - blitter->CopyColorTexture2D(src_texture, src_rect, entry->handle, - dest_rect, GL_LINEAR, swap_channels); - } - return entry->handle; - } - } - - // Need to create a new texture. - // As we don't know anything about this texture, we'll add it to the - // pending readbuffer list. If nobody claims it after a certain amount - // of time we'll dump it. - auto entry = std::make_unique<ReadBufferTexture>(); - entry->guest_address = guest_address; - entry->logical_width = logical_width; - entry->logical_height = logical_height; - entry->block_width = block_width; - entry->block_height = block_height; - 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); - glTextureParameteri(entry->handle, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(entry->handle, GL_TEXTURE_MAX_LEVEL, 1); - glTextureStorage2D(entry->handle, 1, config.internal_format, logical_width, - logical_height); - if (config.format == GL_DEPTH_STENCIL) { - blitter->CopyDepthTexture(src_texture, src_rect, entry->handle, dest_rect); - } else { - blitter->CopyColorTexture2D(src_texture, src_rect, entry->handle, dest_rect, - GL_LINEAR, swap_channels); - } - - GLuint handle = entry->handle; - read_buffer_textures_.push_back(entry.release()); - return handle; -} - -void TextureCache::EvictTexture(TextureEntry* entry) { - if (entry->access_watch_handle) { - memory_->CancelAccessWatch(entry->access_watch_handle); - entry->access_watch_handle = 0; - } - - for (auto& view : entry->views) { - glMakeTextureHandleNonResidentARB(view->texture_sampler_handle); - } - glDeleteTextures(1, &entry->handle); - - uint64_t texture_hash = entry->texture_info.hash(); - for (auto it = texture_entries_.find(texture_hash); - it != texture_entries_.end(); ++it) { - if (it->second == entry) { - texture_entries_.erase(it); - break; - } - } - - delete entry; -} - -struct HostTextureInfo { - uint32_t output_length; - - union { - struct { - uint32_t output_width; - uint32_t output_pitch; - } size_1d; - struct { - uint32_t output_width; - uint32_t output_height; - uint32_t output_pitch; - } size_2d; - struct { - } size_3d; - struct { - uint32_t output_width; - uint32_t output_height; - uint32_t output_pitch; - uint32_t output_face_length; - } size_cube; - }; - - static bool Setup(const TextureInfo& guest_info, HostTextureInfo* out_info) { - auto& info = *out_info; - auto format = guest_info.format_info(); - - switch (guest_info.dimension) { - case Dimension::k1D: { - uint32_t bytes_per_block = - format->block_width * format->bits_per_pixel / 8; - uint32_t block_width = xe::round_up(guest_info.size_1d.logical_width, - format->block_width) / - format->block_width; - info.size_1d.output_width = block_width * format->block_width; - info.size_1d.output_pitch = block_width * bytes_per_block; - info.output_length = info.size_1d.output_pitch; - return true; - } - case Dimension::k2D: { - uint32_t bytes_per_block = format->block_width * format->block_height * - format->bits_per_pixel / 8; - uint32_t block_width = xe::round_up(guest_info.size_2d.logical_width, - format->block_width) / - format->block_width; - uint32_t block_height = xe::round_up(guest_info.size_2d.logical_height, - format->block_height) / - format->block_height; - info.size_2d.output_width = block_width * format->block_width; - info.size_2d.output_height = block_height * format->block_height; - info.size_2d.output_pitch = block_width * bytes_per_block; - info.output_length = info.size_2d.output_pitch * block_height; - return true; - }; - case Dimension::k3D: { - return false; - } - case Dimension::kCube: { - uint32_t bytes_per_block = format->block_width * format->block_height * - format->bits_per_pixel / 8; - uint32_t block_width = xe::round_up(guest_info.size_cube.logical_width, - format->block_width) / - format->block_width; - uint32_t block_height = - xe::round_up(guest_info.size_cube.logical_height, - format->block_height) / - format->block_height; - info.size_cube.output_width = block_width * format->block_width; - info.size_cube.output_height = block_height * format->block_height; - info.size_cube.output_pitch = block_width * bytes_per_block; - info.size_cube.output_face_length = - info.size_cube.output_pitch * block_height; - info.output_length = info.size_cube.output_face_length * 6; - return true; - } - } - return false; - } -}; - -void TextureSwap(Endian endianness, void* dest, const void* src, - size_t length) { - switch (endianness) { - case Endian::k8in16: - xe::copy_and_swap_16_aligned(dest, src, length / 2); - break; - case Endian::k8in32: - xe::copy_and_swap_32_aligned(dest, src, length / 4); - break; - case Endian::k16in32: // Swap high and low 16 bits within a 32 bit word - xe::copy_and_swap_16_in_32_aligned(dest, src, length); - break; - default: - case Endian::kUnspecified: - std::memcpy(dest, src, length); - break; - } -} - -bool TextureCache::UploadTexture1D(GLuint texture, - const TextureInfo& texture_info) { - SCOPE_profile_cpu_f("gpu"); - const auto host_address = - memory_->TranslatePhysical(texture_info.guest_address); - - const auto& config = texture_configs[uint32_t(texture_info.texture_format)]; - if (config.format == GL_INVALID_ENUM) { - assert_always("Unhandled texture format"); - return false; - } - - HostTextureInfo host_info; - if (!HostTextureInfo::Setup(texture_info, &host_info)) { - assert_always("Failed to set up host texture info"); - return false; - } - - size_t unpack_length = host_info.output_length; - glTextureStorage1D(texture, 1, config.internal_format, - host_info.size_1d.output_width); - - auto allocation = scratch_buffer_->Acquire(unpack_length); - - if (!texture_info.is_tiled) { - if (texture_info.size_1d.input_pitch == host_info.size_1d.output_pitch) { - TextureSwap(texture_info.endianness, allocation.host_ptr, host_address, - unpack_length); - } else { - assert_always(); - } - } else { - assert_always(); - } - size_t unpack_offset = allocation.offset; - scratch_buffer_->Commit(std::move(allocation)); - // TODO(benvanik): avoid flush on entire buffer by using another texture - // buffer. - scratch_buffer_->Flush(); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratch_buffer_->handle()); - if (texture_info.is_compressed()) { - glCompressedTextureSubImage1D(texture, 0, 0, host_info.size_1d.output_width, - config.format, - static_cast<GLsizei>(unpack_length), - reinterpret_cast<void*>(unpack_offset)); - } else { - // Most of these don't seem to have an effect on compressed images. - // glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); - // glPixelStorei(GL_UNPACK_ALIGNMENT, texture_info.texel_pitch); - // glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_info.size_2d.input_width); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTextureSubImage1D(texture, 0, 0, host_info.size_1d.output_width, - config.format, config.type, - reinterpret_cast<void*>(unpack_offset)); - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - return true; -} - -bool TextureCache::UploadTexture2D(GLuint texture, - const TextureInfo& texture_info) { - SCOPE_profile_cpu_f("gpu"); - const auto host_address = - memory_->TranslatePhysical(texture_info.guest_address); - - const auto& config = - texture_configs[uint32_t(texture_info.format_info()->format)]; - if (config.format == GL_INVALID_ENUM) { - assert_always("Unhandled texture format"); - return false; - } - - HostTextureInfo host_info; - if (!HostTextureInfo::Setup(texture_info, &host_info)) { - assert_always("Failed to set up host texture info"); - return false; - } - - size_t unpack_length = host_info.output_length; - glTextureStorage2D(texture, 1, config.internal_format, - host_info.size_2d.output_width, - host_info.size_2d.output_height); - - auto allocation = scratch_buffer_->Acquire(unpack_length); - - if (!texture_info.is_tiled) { - uint32_t offset_x, offset_y; - if (texture_info.has_packed_mips && - TextureInfo::GetPackedTileOffset(texture_info, &offset_x, &offset_y)) { - uint32_t bytes_per_block = texture_info.format_info()->block_width * - texture_info.format_info()->block_height * - texture_info.format_info()->bits_per_pixel / 8; - const uint8_t* src = host_address; - // TODO(gibbed): this needs checking - src += offset_y * texture_info.size_2d.input_pitch; - src += offset_x * bytes_per_block; - uint8_t* dest = reinterpret_cast<uint8_t*>(allocation.host_ptr); - uint32_t pitch = std::min(texture_info.size_2d.input_pitch, - host_info.size_2d.output_pitch); - for (uint32_t y = 0; y < std::min(texture_info.size_2d.block_height, - texture_info.size_2d.logical_height); - y++) { - TextureSwap(texture_info.endianness, dest, src, pitch); - src += texture_info.size_2d.input_pitch; - dest += host_info.size_2d.output_pitch; - } - } else if (texture_info.size_2d.input_pitch == - host_info.size_2d.output_pitch) { - // Fast path copy entire image. - TextureSwap(texture_info.endianness, allocation.host_ptr, host_address, - unpack_length); - } else { - // Slow path copy row-by-row because strides differ. - // UNPACK_ROW_LENGTH only works for uncompressed images, and likely does - // this exact thing under the covers, so we just always do it here. - const uint8_t* src = host_address; - uint8_t* dest = reinterpret_cast<uint8_t*>(allocation.host_ptr); - uint32_t pitch = std::min(texture_info.size_2d.input_pitch, - host_info.size_2d.output_pitch); - for (uint32_t y = 0; y < std::min(texture_info.size_2d.block_height, - texture_info.size_2d.logical_height); - y++) { - TextureSwap(texture_info.endianness, dest, src, pitch); - src += texture_info.size_2d.input_pitch; - dest += host_info.size_2d.output_pitch; - } - } - } else { - // Untile image. - // We could do this in a shader to speed things up, as this is pretty slow. - - // TODO(benvanik): optimize this inner loop (or work by tiles). - const uint8_t* src = host_address; - uint8_t* dest = reinterpret_cast<uint8_t*>(allocation.host_ptr); - uint32_t bytes_per_block = texture_info.format_info()->block_width * - texture_info.format_info()->block_height * - texture_info.format_info()->bits_per_pixel / 8; - - // Tiled textures can be packed; get the offset into the packed texture. - uint32_t offset_x; - uint32_t offset_y; - TextureInfo::GetPackedTileOffset(texture_info, &offset_x, &offset_y); - - auto bpp = (bytes_per_block >> 2) + - ((bytes_per_block >> 1) >> (bytes_per_block >> 2)); - for (uint32_t y = 0, output_base_offset = 0; - y < std::min(texture_info.size_2d.block_height, - texture_info.size_2d.logical_height); - y++, output_base_offset += host_info.size_2d.output_pitch) { - auto input_base_offset = TextureInfo::TiledOffset2DOuter( - offset_y + y, - (texture_info.size_2d.input_width / - texture_info.format_info()->block_width), - bpp); - for (uint32_t x = 0, output_offset = output_base_offset; - x < texture_info.size_2d.block_width; - x++, output_offset += bytes_per_block) { - auto input_offset = - TextureInfo::TiledOffset2DInner(offset_x + x, offset_y + y, bpp, - input_base_offset) >> - bpp; - TextureSwap(texture_info.endianness, dest + output_offset, - src + input_offset * bytes_per_block, bytes_per_block); - } - } - } - size_t unpack_offset = allocation.offset; - scratch_buffer_->Commit(std::move(allocation)); - // TODO(benvanik): avoid flush on entire buffer by using another texture - // buffer. - scratch_buffer_->Flush(); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratch_buffer_->handle()); - if (texture_info.is_compressed()) { - glCompressedTextureSubImage2D( - texture, 0, 0, 0, host_info.size_2d.output_width, - host_info.size_2d.output_height, config.format, - static_cast<GLsizei>(unpack_length), - reinterpret_cast<void*>(unpack_offset)); - } else { - // Most of these don't seem to have an effect on compressed images. - // glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); - // glPixelStorei(GL_UNPACK_ALIGNMENT, texture_info.texel_pitch); - // glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_info.size_2d.input_width); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTextureSubImage2D(texture, 0, 0, 0, host_info.size_2d.output_width, - host_info.size_2d.output_height, config.format, - config.type, reinterpret_cast<void*>(unpack_offset)); - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - return true; -} - -bool TextureCache::UploadTextureCube(GLuint texture, - const TextureInfo& texture_info) { - SCOPE_profile_cpu_f("gpu"); - const auto host_address = - memory_->TranslatePhysical(texture_info.guest_address); - - const auto& config = - texture_configs[uint32_t(texture_info.format_info()->format)]; - if (config.format == GL_INVALID_ENUM) { - assert_always("Unhandled texture format"); - return false; - } - - HostTextureInfo host_info; - if (!HostTextureInfo::Setup(texture_info, &host_info)) { - assert_always("Failed to set up host texture info"); - return false; - } - - size_t unpack_length = host_info.output_length; - glTextureStorage2D(texture, 1, config.internal_format, - host_info.size_cube.output_width, - host_info.size_cube.output_height); - - auto allocation = scratch_buffer_->Acquire(unpack_length); - if (!texture_info.is_tiled) { - if (texture_info.size_cube.input_pitch == - host_info.size_cube.output_pitch) { - // Fast path copy entire image. - TextureSwap(texture_info.endianness, allocation.host_ptr, host_address, - unpack_length); - } else { - // Slow path copy row-by-row because strides differ. - // UNPACK_ROW_LENGTH only works for uncompressed images, and likely does - // this exact thing under the covers, so we just always do it here. - const uint8_t* src = host_address; - uint8_t* dest = reinterpret_cast<uint8_t*>(allocation.host_ptr); - for (int face = 0; face < 6; ++face) { - uint32_t pitch = std::min(texture_info.size_cube.input_pitch, - host_info.size_cube.output_pitch); - for (uint32_t y = 0; y < texture_info.size_cube.block_height; y++) { - TextureSwap(texture_info.endianness, dest, src, pitch); - src += texture_info.size_cube.input_pitch; - dest += host_info.size_cube.output_pitch; - } - } - } - } else { - // TODO(benvanik): optimize this inner loop (or work by tiles). - const uint8_t* src = host_address; - uint8_t* dest = reinterpret_cast<uint8_t*>(allocation.host_ptr); - uint32_t bytes_per_block = texture_info.format_info()->block_width * - texture_info.format_info()->block_height * - texture_info.format_info()->bits_per_pixel / 8; - // Tiled textures can be packed; get the offset into the packed texture. - uint32_t offset_x; - uint32_t offset_y; - TextureInfo::GetPackedTileOffset(texture_info, &offset_x, &offset_y); - auto bpp = (bytes_per_block >> 2) + - ((bytes_per_block >> 1) >> (bytes_per_block >> 2)); - for (int face = 0; face < 6; ++face) { - for (uint32_t y = 0, output_base_offset = 0; - y < texture_info.size_cube.block_height; - y++, output_base_offset += host_info.size_cube.output_pitch) { - auto input_base_offset = TextureInfo::TiledOffset2DOuter( - offset_y + y, - (texture_info.size_cube.input_width / - texture_info.format_info()->block_width), - bpp); - for (uint32_t x = 0, output_offset = output_base_offset; - x < texture_info.size_cube.block_width; - x++, output_offset += bytes_per_block) { - auto input_offset = - TextureInfo::TiledOffset2DInner(offset_x + x, offset_y + y, bpp, - input_base_offset) >> - bpp; - TextureSwap(texture_info.endianness, dest + output_offset, - src + input_offset * bytes_per_block, bytes_per_block); - } - } - src += texture_info.size_cube.input_face_length; - dest += host_info.size_cube.output_face_length; - } - } - size_t unpack_offset = allocation.offset; - scratch_buffer_->Commit(std::move(allocation)); - // TODO(benvanik): avoid flush on entire buffer by using another texture - // buffer. - scratch_buffer_->Flush(); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratch_buffer_->handle()); - if (texture_info.is_compressed()) { - glCompressedTextureSubImage3D( - texture, 0, 0, 0, 0, host_info.size_cube.output_width, - host_info.size_cube.output_height, 6, config.format, - static_cast<GLsizei>(unpack_length), - reinterpret_cast<void*>(unpack_offset)); - } else { - // Most of these don't seem to have an effect on compressed images. - // glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); - // glPixelStorei(GL_UNPACK_ALIGNMENT, texture_info.texel_pitch); - // glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_info.size_2d.input_width); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTextureSubImage3D(texture, 0, 0, 0, 0, host_info.size_cube.output_width, - host_info.size_cube.output_height, 6, config.format, - config.type, reinterpret_cast<void*>(unpack_offset)); - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - return true; -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe diff --git a/src/xenia/gpu/gl4/texture_cache.h b/src/xenia/gpu/gl4/texture_cache.h deleted file mode 100644 index 4f018c329..000000000 --- a/src/xenia/gpu/gl4/texture_cache.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_GPU_GL4_TEXTURE_CACHE_H_ -#define XENIA_GPU_GL4_TEXTURE_CACHE_H_ - -#include <mutex> -#include <unordered_map> -#include <vector> - -#include "xenia/gpu/sampler_info.h" -#include "xenia/gpu/texture_info.h" -#include "xenia/memory.h" -#include "xenia/ui/gl/blitter.h" -#include "xenia/ui/gl/circular_buffer.h" -#include "xenia/ui/gl/gl_context.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -using xe::ui::gl::Blitter; -using xe::ui::gl::CircularBuffer; -using xe::ui::gl::Rect2D; - -class TextureCache { - public: - struct TextureEntry; - struct SamplerEntry { - SamplerInfo sampler_info; - GLuint handle; - }; - struct TextureEntryView { - TextureEntry* texture; - SamplerEntry* sampler; - uint64_t sampler_hash; - GLuint64 texture_sampler_handle; - }; - struct TextureEntry { - TextureInfo texture_info; - uintptr_t access_watch_handle; - GLuint handle; - bool pending_invalidation; - std::vector<std::unique_ptr<TextureEntryView>> views; - }; - - TextureCache(); - ~TextureCache(); - - bool Initialize(Memory* memory, CircularBuffer* scratch_buffer); - void Shutdown(); - - void Scavenge(); - void Clear(); - void EvictAllTextures(); - - TextureEntryView* Demand(const TextureInfo& texture_info, - const SamplerInfo& sampler_info); - - GLuint CopyTexture(Blitter* blitter, uint32_t guest_address, - uint32_t logical_width, uint32_t logical_height, - uint32_t block_width, uint32_t block_height, - TextureFormat format, bool swap_channels, - GLuint src_texture, Rect2D src_rect, Rect2D dest_rect); - GLuint ConvertTexture(Blitter* blitter, uint32_t guest_address, - uint32_t logical_width, uint32_t logical_height, - uint32_t block_width, uint32_t block_height, - TextureFormat format, bool swap_channels, - GLuint src_texture, Rect2D src_rect, Rect2D dest_rect); - - TextureEntry* LookupAddress(uint32_t guest_address, uint32_t width, - uint32_t height, TextureFormat format); - - private: - struct ReadBufferTexture { - uintptr_t access_watch_handle; - uint32_t guest_address; - uint32_t logical_width; - uint32_t logical_height; - uint32_t block_width; - uint32_t block_height; - TextureFormat format; - GLuint handle; - }; - - SamplerEntry* LookupOrInsertSampler(const SamplerInfo& sampler_info, - uint64_t opt_hash = 0); - void EvictSampler(SamplerEntry* entry); - TextureEntry* LookupOrInsertTexture(const TextureInfo& texture_info, - uint64_t opt_hash = 0); - void EvictTexture(TextureEntry* entry); - - bool UploadTexture1D(GLuint texture, const TextureInfo& texture_info); - bool UploadTexture2D(GLuint texture, const TextureInfo& texture_info); - bool UploadTextureCube(GLuint texture, const TextureInfo& texture_info); - - Memory* memory_; - CircularBuffer* scratch_buffer_; - std::unordered_map<uint64_t, SamplerEntry*> sampler_entries_; - std::unordered_map<uint64_t, TextureEntry*> texture_entries_; - - std::vector<ReadBufferTexture*> read_buffer_textures_; - - std::mutex invalidated_textures_mutex_; - std::vector<TextureEntry*>* invalidated_textures_; - std::vector<TextureEntry*> invalidated_textures_sets_[2]; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_TEXTURE_CACHE_H_ diff --git a/src/xenia/gpu/glsl_shader_translator.cc b/src/xenia/gpu/glsl_shader_translator.cc index 2f89ab3df..32d26c1a2 100644 --- a/src/xenia/gpu/glsl_shader_translator.cc +++ b/src/xenia/gpu/glsl_shader_translator.cc @@ -959,6 +959,7 @@ void GlslShaderTranslator::EmitStoreResult(const InstructionResult& result, case InstructionStorageTarget::kDepth: EmitSourceDepth("gl_FragDepth"); break; + default: case InstructionStorageTarget::kNone: return; } diff --git a/src/xenia/gpu/graphics_system.cc b/src/xenia/gpu/graphics_system.cc index 56443db3a..7728e8eee 100644 --- a/src/xenia/gpu/graphics_system.cc +++ b/src/xenia/gpu/graphics_system.cc @@ -170,7 +170,7 @@ uint32_t GraphicsSystem::ReadRegister(uint32_t addr) { uint32_t r = (addr & 0xFFFF) / 4; switch (r) { - case 0x0F00: // ? + case 0x0F00: // RB_EDRAM_TIMING return 0x08100748; case 0x0F01: // RB_BC_CONTROL return 0x0000200E; @@ -211,7 +211,7 @@ void GraphicsSystem::WriteRegister(uint32_t addr, uint32_t value) { } void GraphicsSystem::InitializeRingBuffer(uint32_t ptr, uint32_t log2_size) { - command_processor_->InitializeRingBuffer(ptr, (log2_size | 0x2) + 1); + command_processor_->InitializeRingBuffer(ptr, log2_size + 0x3); } void GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr, diff --git a/src/xenia/gpu/sampler_info.cc b/src/xenia/gpu/sampler_info.cc index 0adefbd1b..c6fcf0985 100644 --- a/src/xenia/gpu/sampler_info.cc +++ b/src/xenia/gpu/sampler_info.cc @@ -44,6 +44,8 @@ bool SamplerInfo::Prepare(const xenos::xe_gpu_texture_fetch_t& fetch, out_info->border_color = static_cast<BorderColor>(fetch.border_color); out_info->lod_bias = (fetch.lod_bias) / 32.f; + out_info->mip_min_level = fetch.mip_min_level; + out_info->mip_max_level = fetch.mip_max_level; return true; } diff --git a/src/xenia/gpu/sampler_info.h b/src/xenia/gpu/sampler_info.h index 57e3ceeac..415c28bd2 100644 --- a/src/xenia/gpu/sampler_info.h +++ b/src/xenia/gpu/sampler_info.h @@ -26,6 +26,8 @@ struct SamplerInfo { AnisoFilter aniso_filter; BorderColor border_color; float lod_bias; + uint32_t mip_min_level; + uint32_t mip_max_level; static bool Prepare(const xenos::xe_gpu_texture_fetch_t& fetch, const ParsedTextureFetchInstruction& fetch_instr, @@ -36,7 +38,9 @@ struct SamplerInfo { return min_filter == other.min_filter && mag_filter == other.mag_filter && mip_filter == other.mip_filter && clamp_u == other.clamp_u && clamp_v == other.clamp_v && clamp_w == other.clamp_w && - aniso_filter == other.aniso_filter; + aniso_filter == other.aniso_filter && lod_bias == other.lod_bias && + mip_min_level == other.mip_min_level && + mip_max_level == other.mip_max_level; } }; diff --git a/src/xenia/gpu/shader.h b/src/xenia/gpu/shader.h index 7e0cd3ab2..ebea8b9f3 100644 --- a/src/xenia/gpu/shader.h +++ b/src/xenia/gpu/shader.h @@ -31,6 +31,11 @@ enum class InstructionStorageTarget { kPosition, // Result is stored to the point size export (gl_PointSize). kPointSize, + // Result is stored as memexport destination address. + // [physical >> 2, ??, ??, ??] + kExportAddress, + // Result is stored to memexport destination data. + kExportData, // Result is stored to a color target export indexed by storage_index [0-3]. kColorTarget, // Result is stored to the depth export (gl_FragDepth). diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index 4634ef34d..1f3e13dbc 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -1147,14 +1147,15 @@ void ShaderTranslator::ParseAluVectorInstruction( } else if (is_vertex_shader()) { switch (dest_num) { case 32: + i.result.storage_target = InstructionStorageTarget::kExportAddress; + break; case 33: case 34: case 35: case 36: case 37: - // TODO: Memexport registers - i.result.storage_target = InstructionStorageTarget::kNone; - i.result.storage_index = 0; + i.result.storage_index = dest_num - 33; + i.result.storage_target = InstructionStorageTarget::kExportData; break; case 62: i.result.storage_target = InstructionStorageTarget::kPosition; @@ -1198,14 +1199,15 @@ void ShaderTranslator::ParseAluVectorInstruction( i.result.storage_index = 3; break; case 32: + i.result.storage_target = InstructionStorageTarget::kExportAddress; + break; case 33: case 34: case 35: case 36: case 37: - // TODO: Memexport registers - i.result.storage_target = InstructionStorageTarget::kNone; - i.result.storage_index = 0; + i.result.storage_index = dest_num - 33; + i.result.storage_target = InstructionStorageTarget::kExportData; break; case 61: i.result.storage_target = InstructionStorageTarget::kDepth; @@ -1303,6 +1305,17 @@ void ShaderTranslator::ParseAluScalarInstruction( : InstructionStorageAddressingMode::kStatic; } else if (is_vertex_shader()) { switch (dest_num) { + case 32: + i.result.storage_target = InstructionStorageTarget::kExportAddress; + break; + case 33: + case 34: + case 35: + case 36: + case 37: + i.result.storage_index = dest_num - 33; + i.result.storage_target = InstructionStorageTarget::kExportData; + break; case 62: i.result.storage_target = InstructionStorageTarget::kPosition; break; @@ -1344,6 +1357,17 @@ void ShaderTranslator::ParseAluScalarInstruction( i.result.storage_target = InstructionStorageTarget::kColorTarget; i.result.storage_index = 3; break; + case 32: + i.result.storage_target = InstructionStorageTarget::kExportAddress; + break; + case 33: + case 34: + case 35: + case 36: + case 37: + i.result.storage_index = dest_num - 33; + i.result.storage_target = InstructionStorageTarget::kExportData; + break; case 61: i.result.storage_target = InstructionStorageTarget::kDepth; break; diff --git a/src/xenia/gpu/shader_translator_disasm.cc b/src/xenia/gpu/shader_translator_disasm.cc index b36267fb4..6a2007eaa 100644 --- a/src/xenia/gpu/shader_translator_disasm.cc +++ b/src/xenia/gpu/shader_translator_disasm.cc @@ -38,6 +38,13 @@ void DisassembleResultOperand(const InstructionResult& result, case InstructionStorageTarget::kPointSize: out->Append("oPts"); break; + case InstructionStorageTarget::kExportAddress: + out->Append("eA"); + break; + case InstructionStorageTarget::kExportData: + out->Append("eM"); + uses_storage_index = true; + break; case InstructionStorageTarget::kColorTarget: out->AppendFormat("oC"); uses_storage_index = true; diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index 376d04f17..17cec8f37 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -67,6 +67,7 @@ void SpirvShaderTranslator::StartTranslation() { vec2_int_type_ = b.makeVectorType(int_type_, 2); vec2_uint_type_ = b.makeVectorType(uint_type_, 2); vec2_float_type_ = b.makeVectorType(float_type_, 2); + vec3_int_type_ = b.makeVectorType(int_type_, 3); vec3_float_type_ = b.makeVectorType(float_type_, 3); vec4_float_type_ = b.makeVectorType(float_type_, 4); vec4_int_type_ = b.makeVectorType(int_type_, 4); @@ -482,8 +483,10 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() { mainFn, "main"); b.addExecutionMode(mainFn, spv::ExecutionModeOriginUpperLeft); - // FIXME(DrChat): We need to declare the DepthReplacing execution mode if - // we write depth, and we must unconditionally write depth if declared! + // If we write a new depth value, we must declare this mode! + if (writes_depth_) { + b.addExecutionMode(mainFn, spv::ExecutionModeDepthReplacing); + } for (auto id : interface_ids_) { entry->addIdOperand(id); @@ -527,10 +530,18 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() { // Reinsert w p = b.createCompositeInsert(p_w, p, vec4_float_type_, 3); + // Apply window offset + // pos.xy += window_scale.zw + auto window_offset = b.createOp(spv::Op::OpVectorShuffle, vec4_float_type_, + {window_scale, window_scale, 2, 3, 0, 1}); + auto p_offset = + b.createBinOp(spv::Op::OpFAdd, vec4_float_type_, p, window_offset); + // Apply window scaling // pos.xy *= window_scale.xy - auto p_scaled = - b.createBinOp(spv::Op::OpFMul, vec4_float_type_, p, window_scale); + auto p_scaled = b.createBinOp(spv::Op::OpFMul, vec4_float_type_, p_offset, + window_scale); + p = b.createOp(spv::Op::OpVectorShuffle, vec4_float_type_, {p, p_scaled, 4, 5, 2, 3}); @@ -608,6 +619,7 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() { // Cleanup builder. cf_blocks_.clear(); + writes_depth_ = false; loop_head_block_ = nullptr; loop_body_block_ = nullptr; loop_cont_block_ = nullptr; @@ -1786,60 +1798,10 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( tex_[dim_idx], std::vector<Id>({texture_index})); auto texture = b.createLoad(texture_ptr); - spv::Id size = 0; - if (instr.attributes.offset_x || instr.attributes.offset_y) { - auto image = - b.createUnaryOp(spv::OpImage, b.getImageType(texture), texture); - - spv::Builder::TextureParameters params; - std::memset(¶ms, 0, sizeof(params)); - params.sampler = image; - params.lod = b.makeIntConstant(0); - size = b.createTextureQueryCall(spv::Op::OpImageQuerySizeLod, params, - false); - - if (instr.dimension == TextureDimension::k1D) { - size = b.createUnaryOp(spv::Op::OpConvertSToF, float_type_, size); - } else if (instr.dimension == TextureDimension::k2D) { - size = - b.createUnaryOp(spv::Op::OpConvertSToF, vec2_float_type_, size); - } else if (instr.dimension == TextureDimension::k3D) { - size = - b.createUnaryOp(spv::Op::OpConvertSToF, vec3_float_type_, size); - } else if (instr.dimension == TextureDimension::kCube) { - size = - b.createUnaryOp(spv::Op::OpConvertSToF, vec4_float_type_, size); - } - } - if (instr.dimension == TextureDimension::k1D) { - src = b.createCompositeExtract(src, float_type_, 0); - if (instr.attributes.offset_x) { - auto offset = b.makeFloatConstant(instr.attributes.offset_x + 0.5f); - offset = b.createBinOp(spv::Op::OpFDiv, float_type_, offset, size); - src = b.createBinOp(spv::Op::OpFAdd, float_type_, src, offset); - } - - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb944006.aspx - // "Because the runtime does not support 1D textures, the compiler will - // use a 2D texture with the knowledge that the y-coordinate is - // unimportant." - src = b.createCompositeConstruct( - vec2_float_type_, - std::vector<Id>({src, b.makeFloatConstant(0.0f)})); - } else if (instr.dimension == TextureDimension::k2D) { - src = b.createRvalueSwizzle(spv::NoPrecision, vec2_float_type_, src, - std::vector<uint32_t>({0, 1})); - if (instr.attributes.offset_x || instr.attributes.offset_y) { - auto offset = b.makeCompositeConstant( - vec2_float_type_, - std::vector<Id>( - {b.makeFloatConstant(instr.attributes.offset_x + 0.5f), - b.makeFloatConstant(instr.attributes.offset_y + 0.5f)})); - offset = - b.createBinOp(spv::Op::OpFDiv, vec2_float_type_, offset, size); - src = b.createBinOp(spv::Op::OpFAdd, vec2_float_type_, src, offset); - } + // Upgrade 1D src coordinate into 2D + src = b.createCompositeConstruct(vec2_float_type_, + {src, b.makeFloatConstant(0.f)}); } spv::Builder::TextureParameters params = {0}; @@ -1848,6 +1810,50 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( if (instr.attributes.use_register_lod) { params.lod = b.createLoad(lod_); } + if (instr.attributes.offset_x || instr.attributes.offset_y || + instr.attributes.offset_z) { + float offset_x = instr.attributes.offset_x; + float offset_y = instr.attributes.offset_y; + float offset_z = instr.attributes.offset_z; + + // Round numbers away from zero. No effect if offset is 0. + offset_x += instr.attributes.offset_x < 0 ? -0.5f : 0.5f; + offset_y += instr.attributes.offset_y < 0 ? -0.5f : 0.5f; + offset_z += instr.attributes.offset_z < 0 ? -0.5f : 0.5f; + + Id offset = 0; + switch (instr.dimension) { + case TextureDimension::k1D: { + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb944006.aspx + // "Because the runtime does not support 1D textures, the compiler + // will use a 2D texture with the knowledge that the y-coordinate is + // unimportant." + offset = b.makeCompositeConstant( + vec2_int_type_, + {b.makeIntConstant(int(offset_x)), b.makeIntConstant(0)}); + } break; + case TextureDimension::k2D: { + offset = b.makeCompositeConstant( + vec2_int_type_, {b.makeIntConstant(int(offset_x)), + b.makeIntConstant(int(offset_y))}); + } break; + case TextureDimension::k3D: { + offset = b.makeCompositeConstant( + vec3_int_type_, {b.makeIntConstant(int(offset_x)), + b.makeIntConstant(int(offset_y)), + b.makeIntConstant(int(offset_z))}); + } break; + case TextureDimension::kCube: { + // FIXME(DrChat): Is this the correct dimension? I forget + offset = b.makeCompositeConstant( + vec3_int_type_, {b.makeIntConstant(int(offset_x)), + b.makeIntConstant(int(offset_y)), + b.makeIntConstant(int(offset_z))}); + } break; + } + + params.offset = offset; + } dest = b.createTextureCall(spv::NoPrecision, vec4_float_type_, false, false, @@ -1908,11 +1914,39 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( } } break; + case FetchOpcode::kGetTextureComputedLod: { + // TODO(DrChat): Verify if this implementation is correct. + // This is only valid in pixel shaders. + assert_true(is_pixel_shader()); + + auto texture_index = + b.makeUintConstant(tex_binding_map_[instr.operands[1].storage_index]); + auto texture_ptr = + b.createAccessChain(spv::StorageClass::StorageClassUniformConstant, + tex_[dim_idx], std::vector<Id>({texture_index})); + auto texture = b.createLoad(texture_ptr); + + if (instr.dimension == TextureDimension::k1D) { + // Upgrade 1D src coordinate into 2D + src = b.createCompositeConstruct(vec2_float_type_, + {src, b.makeFloatConstant(0.f)}); + } + + spv::Builder::TextureParameters params = {}; + params.sampler = texture; + params.coords = src; + auto lod = + b.createTextureQueryCall(spv::Op::OpImageQueryLod, params, false); + + dest = b.createCompositeExtract(lod, float_type_, 1); + dest = b.smearScalar(spv::NoPrecision, dest, vec4_float_type_); + } break; + case FetchOpcode::kSetTextureLod: { // <lod register> = src1.x (MIP level) // ... immediately after // tfetch UseRegisterLOD=true - b.createStore(b.createCompositeExtract(src, float_type_, 0), lod_); + b.createStore(src, lod_); } break; default: @@ -2210,8 +2244,8 @@ void SpirvShaderTranslator::ProcessVectorAluInstruction( auto src1_y = b.createCompositeExtract(sources[1], float_type_, 1); auto dst_y = b.createBinOp(spv::Op::OpFMul, float_type_, src0_y, src1_y); - auto src0_z = b.createCompositeExtract(sources[0], float_type_, 3); - auto src1_w = b.createCompositeExtract(sources[1], float_type_, 4); + auto src0_z = b.createCompositeExtract(sources[0], float_type_, 2); + auto src1_w = b.createCompositeExtract(sources[1], float_type_, 3); dest = b.createCompositeConstruct( vec4_float_type_, std::vector<Id>({b.makeFloatConstant(1.f), dst_y, src0_z, src1_w})); @@ -3267,7 +3301,9 @@ void SpirvShaderTranslator::StoreToResult(Id source_value_id, storage_type = float_type_; storage_offsets.push_back(0); storage_array = false; + writes_depth_ = true; break; + default: case InstructionStorageTarget::kNone: assert_unhandled_case(result.storage_target); break; diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index ec822e8e2..caa288353 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -29,7 +29,7 @@ namespace gpu { // supported size). struct SpirvPushConstants { // Accessible to vertex shader only: - float window_scale[4]; // scale x/y, viewport width/height (pixels) + float window_scale[4]; // scale x/y, offset x/y (pixels) float vtx_fmt[4]; // Accessible to geometry shader only: @@ -132,7 +132,7 @@ class SpirvShaderTranslator : public ShaderTranslator { // Types. spv::Id float_type_ = 0, bool_type_ = 0, int_type_ = 0, uint_type_ = 0; - spv::Id vec2_int_type_ = 0, vec2_uint_type_ = 0; + spv::Id vec2_int_type_ = 0, vec2_uint_type_ = 0, vec3_int_type_ = 0; spv::Id vec2_float_type_ = 0, vec3_float_type_ = 0, vec4_float_type_ = 0; spv::Id vec4_int_type_ = 0, vec4_uint_type_ = 0; spv::Id vec2_bool_type_ = 0, vec3_bool_type_ = 0, vec4_bool_type_ = 0; @@ -163,6 +163,8 @@ class SpirvShaderTranslator : public ShaderTranslator { spv::Id vtx_ = 0; // Vertex buffer array (32 runtime arrays) std::unordered_map<uint32_t, uint32_t> vtx_binding_map_; + bool writes_depth_ = false; + // SPIR-V IDs that are part of the in/out interface. std::vector<spv::Id> interface_ids_; diff --git a/src/xenia/gpu/texture_info.cc b/src/xenia/gpu/texture_info.cc index 13cf23c81..46caa54f3 100644 --- a/src/xenia/gpu/texture_info.cc +++ b/src/xenia/gpu/texture_info.cc @@ -15,6 +15,7 @@ #include "xenia/base/logging.h" #include "xenia/base/math.h" +#include "xenia/base/memory.h" #include "third_party/xxhash/xxhash.h" @@ -59,6 +60,8 @@ bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch, info.endianness = static_cast<Endian>(fetch.endianness); info.is_tiled = fetch.tiled; info.has_packed_mips = fetch.packed_mips; + info.mip_address = fetch.mip_address << 12; + info.mip_levels = fetch.packed_mips ? fetch.mip_max_level + 1 : 1; info.input_length = 0; // Populated below. if (info.format_info()->format == TextureFormat::kUnknown) { @@ -70,15 +73,16 @@ bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch, // Must be called here when we know the format. switch (info.dimension) { case Dimension::k1D: { - assert_always(); + info.CalculateTextureSizes1D(fetch.size_1d.width + 1); } break; case Dimension::k2D: { info.CalculateTextureSizes2D(fetch.size_2d.width + 1, fetch.size_2d.height + 1); } break; case Dimension::k3D: { - // TODO(benvanik): calculate size. - return false; + info.CalculateTextureSizes3D(fetch.size_3d.width + 1, + fetch.size_3d.height + 1, + fetch.size_3d.depth + 1); } case Dimension::kCube: { info.CalculateTextureSizesCube(fetch.size_stack.width + 1, @@ -106,6 +110,8 @@ bool TextureInfo::PrepareResolve(uint32_t physical_address, info.endianness = endian; info.is_tiled = true; info.has_packed_mips = false; + info.mip_address = 0; + info.mip_levels = 1; info.input_length = 0; if (info.format_info()->format == TextureFormat::kUnknown) { @@ -117,14 +123,46 @@ bool TextureInfo::PrepareResolve(uint32_t physical_address, return true; } +void TextureInfo::CalculateTextureSizes1D(uint32_t width) { + size_1d.logical_width = width; + + auto format = format_info(); + + // width in blocks. + uint32_t block_width = + xe::round_up(size_1d.logical_width, format->block_width) / + format->block_width; + + if (is_tiled) { + // If the texture is tiled, its dimensions must be a multiple of tile + // dimensions (32x32 blocks). + size_1d.block_width = xe::round_up(block_width, 32); + } else { + size_1d.block_width = block_width; + } + + uint32_t bytes_per_block = format->block_width * format->bits_per_pixel / 8; + uint32_t byte_pitch = size_1d.block_width * bytes_per_block; + + uint32_t texel_width; + if (!is_tiled) { + // Each row must be a multiple of 256 in linear textures. + byte_pitch = xe::round_up(byte_pitch, 256); + texel_width = (byte_pitch / bytes_per_block) * format->block_width; + } else { + texel_width = size_2d.block_width * format->block_width; + } + + size_1d.input_width = texel_width; + size_1d.input_pitch = byte_pitch; + + input_length = size_1d.input_pitch; +} + void TextureInfo::CalculateTextureSizes2D(uint32_t width, uint32_t height) { size_2d.logical_width = width; size_2d.logical_height = height; - // Here be dragons. The values here are used in texture_cache.cc to copy - // images and create GL textures. Changes here will impact that code. - // TODO(benvanik): generic texture copying utility. - auto format = format_info(); // w/h in blocks. @@ -135,11 +173,15 @@ void TextureInfo::CalculateTextureSizes2D(uint32_t width, uint32_t height) { xe::round_up(size_2d.logical_height, format->block_height) / format->block_height; - // Tiles are 32x32 blocks. The pitch of all textures must a multiple of tile - // dimensions. - uint32_t tile_width = xe::round_up(block_width, 32) / 32; - size_2d.block_width = tile_width * 32; - size_2d.block_height = block_height; + if (is_tiled) { + // If the texture is tiled, its dimensions must be a multiple of tile + // dimensions (32x32 blocks). + size_2d.block_width = xe::round_up(block_width, 32); + size_2d.block_height = xe::round_up(block_height, 32); + } else { + size_2d.block_width = block_width; + size_2d.block_height = block_height; + } uint32_t bytes_per_block = format->block_width * format->block_height * format->bits_per_pixel / 8; @@ -161,6 +203,52 @@ void TextureInfo::CalculateTextureSizes2D(uint32_t width, uint32_t height) { input_length = size_2d.input_pitch * size_2d.block_height; } +void TextureInfo::CalculateTextureSizes3D(uint32_t width, uint32_t height, + uint32_t depth) { + size_3d.logical_width = width; + size_3d.logical_height = height; + + auto format = format_info(); + + // w/h in blocks must be a multiple of block size. + uint32_t block_width = + xe::round_up(size_3d.logical_width, format->block_width) / + format->block_width; + uint32_t block_height = + xe::round_up(size_3d.logical_height, format->block_height) / + format->block_height; + + if (is_tiled) { + // If the texture is tiled, its dimensions must be a multiple of tile + // dimensions (32x32 blocks). + size_3d.block_width = xe::round_up(block_width, 32); + size_3d.block_height = xe::round_up(block_height, 32); + } else { + size_3d.block_width = block_width; + size_3d.block_height = block_height; + } + + uint32_t bytes_per_block = + format->block_width * format->block_height * format->bits_per_pixel / 8; + uint32_t byte_pitch = size_3d.block_width * bytes_per_block; + + uint32_t texel_width; + if (!is_tiled) { + // Each row must be a multiple of 256 in linear textures. + byte_pitch = xe::round_up(byte_pitch, 256); + texel_width = (byte_pitch / bytes_per_block) * format->block_width; + } else { + texel_width = size_3d.block_width * format->block_width; + } + + size_3d.input_width = texel_width; + size_3d.input_height = size_3d.block_height * format->block_height; + size_3d.input_pitch = byte_pitch; + + size_3d.input_face_length = size_3d.input_pitch * size_3d.block_height; + input_length = size_3d.input_face_length * depth; +} + void TextureInfo::CalculateTextureSizesCube(uint32_t width, uint32_t height, uint32_t depth) { assert_true(depth == 6); @@ -177,11 +265,15 @@ void TextureInfo::CalculateTextureSizesCube(uint32_t width, uint32_t height, xe::round_up(size_cube.logical_height, format->block_height) / format->block_height; - // Tiles are 32x32 blocks. All textures must be multiples of tile dimensions. - uint32_t tile_width = xe::round_up(block_width, 32) / 32; - uint32_t tile_height = xe::round_up(block_height, 32) / 32; - size_cube.block_width = tile_width * 32; - size_cube.block_height = tile_height * 32; + if (is_tiled) { + // If the texture is tiled, its dimensions must be a multiple of tile + // dimensions (32x32 blocks). + size_cube.block_width = xe::round_up(block_width, 32); + size_cube.block_height = xe::round_up(block_height, 32); + } else { + size_cube.block_width = block_width; + size_cube.block_height = block_height; + } uint32_t bytes_per_block = format->block_width * format->block_height * format->bits_per_pixel / 8; @@ -204,7 +296,194 @@ void TextureInfo::CalculateTextureSizesCube(uint32_t width, uint32_t height, input_length = size_cube.input_face_length * 6; } -bool TextureInfo::GetPackedTileOffset(const TextureInfo& texture_info, +static void TextureSwap(Endian endianness, void* dest, const void* src, + size_t length) { + switch (endianness) { + case Endian::k8in16: + xe::copy_and_swap_16_unaligned(dest, src, length / 2); + break; + case Endian::k8in32: + xe::copy_and_swap_32_unaligned(dest, src, length / 4); + break; + case Endian::k16in32: // Swap high and low 16 bits within a 32 bit word + xe::copy_and_swap_16_in_32_unaligned(dest, src, length); + break; + default: + case Endian::kUnspecified: + std::memcpy(dest, src, length); + break; + } +} + +static void ConvertTexelCTX1(uint8_t* dest, size_t dest_pitch, + const uint8_t* src, Endian src_endianness) { + // http://fileadmin.cs.lth.se/cs/Personal/Michael_Doggett/talks/unc-xenos-doggett.pdf + union { + uint8_t data[8]; + struct { + uint8_t r0, g0, r1, g1; + uint32_t xx; + }; + } block; + static_assert(sizeof(block) == 8, "CTX1 block mismatch"); + + const uint32_t bytes_per_block = 8; + TextureSwap(src_endianness, block.data, src, bytes_per_block); + + uint8_t cr[4] = { + block.r0, block.r1, + static_cast<uint8_t>(2.f / 3.f * block.r0 + 1.f / 3.f * block.r1), + static_cast<uint8_t>(1.f / 3.f * block.r0 + 2.f / 3.f * block.r1)}; + uint8_t cg[4] = { + block.g0, block.g1, + static_cast<uint8_t>(2.f / 3.f * block.g0 + 1.f / 3.f * block.g1), + static_cast<uint8_t>(1.f / 3.f * block.g0 + 2.f / 3.f * block.g1)}; + + for (uint32_t oy = 0; oy < 4; ++oy) { + for (uint32_t ox = 0; ox < 4; ++ox) { + uint8_t xx = (block.xx >> (((ox + (oy * 4)) * 2))) & 3; + dest[(oy * dest_pitch) + (ox * 2) + 0] = cr[xx]; + dest[(oy * dest_pitch) + (ox * 2) + 1] = cg[xx]; + } + } +} + +void TextureInfo::ConvertTiled(uint8_t* dest, const uint8_t* src, Endian endian, + const FormatInfo* format_info, uint32_t offset_x, + uint32_t offset_y, uint32_t block_pitch, + uint32_t width, uint32_t height, + uint32_t output_width) { + // TODO(benvanik): optimize this inner loop (or work by tiles). + uint32_t bytes_per_block = format_info->block_width * + format_info->block_height * + format_info->bits_per_pixel / 8; + + uint32_t output_pitch = + output_width * format_info->block_width * format_info->bits_per_pixel / 8; + + uint32_t output_row_height = 1; + if (format_info->format == TextureFormat::k_CTX1) { + // TODO: Can we calculate this? + output_row_height = 4; + } + + // logical w/h in blocks. + uint32_t block_width = + xe::round_up(width, format_info->block_width) / format_info->block_width; + uint32_t block_height = xe::round_up(height, format_info->block_height) / + format_info->block_height; + + // Bytes per pixel + auto log2_bpp = + (bytes_per_block / 4) + ((bytes_per_block / 2) >> (bytes_per_block / 4)); + + // Offset to the current row, in bytes. + uint32_t output_row_offset = 0; + for (uint32_t y = 0; y < block_height; y++) { + auto input_row_offset = + TextureInfo::TiledOffset2DOuter(offset_y + y, block_pitch, log2_bpp); + + // Go block-by-block on this row. + uint32_t output_offset = output_row_offset; + for (uint32_t x = 0; x < block_width; x++) { + auto input_offset = TextureInfo::TiledOffset2DInner( + offset_x + x, offset_y + y, log2_bpp, input_row_offset); + input_offset >>= log2_bpp; + + if (format_info->format == TextureFormat::k_CTX1) { + // Convert to R8G8. + ConvertTexelCTX1(&dest[output_offset], output_pitch, src, endian); + } else { + // Generic swap to destination. + TextureSwap(endian, dest + output_offset, + src + input_offset * bytes_per_block, bytes_per_block); + } + + output_offset += bytes_per_block; + } + + output_row_offset += output_pitch * output_row_height; + } +} + +uint32_t TextureInfo::GetMaxMipLevels(uint32_t width, uint32_t height, + uint32_t depth) { + return 1 + xe::log2_floor(std::max({width, height, depth})); +} + +uint32_t TextureInfo::GetMipLocation(const TextureInfo& src, uint32_t mip, + uint32_t* offset_x, uint32_t* offset_y) { + if (mip == 0) { + // Short-circuit. Mip 0 is always stored in guest_address. + GetPackedTileOffset(src, offset_x, offset_y); + return src.guest_address; + } + + // If the texture is <= 16 pixels w/h, the mips are packed with the base + // texture. Otherwise, they're stored beginning from mip_address. + uint32_t address_base = std::min(src.width, src.height) < 16 + ? src.guest_address + : src.mip_address; + uint32_t address_offset = 0; + + // Walk forward to find the address of the mip. + for (uint32_t i = 1; i < mip; i++) { + uint32_t logical_width = std::max(xe::next_pow2(src.width + 1) >> i, 1u); + uint32_t logical_height = std::max(xe::next_pow2(src.height + 1) >> i, 1u); + if (std::min(logical_width, logical_height) <= 16) { + // We've reached the point where the mips are packed into a single tile. + break; + } + + address_offset += GetMipSize(src, i); + } + + // Now, check if the mip is packed at an offset. + GetPackedTileOffset(xe::next_pow2(src.width + 1) >> mip, + xe::next_pow2(src.height + 1) >> mip, src.format_info(), + offset_x, offset_y); + return address_base + address_offset; +} + +uint32_t TextureInfo::GetMipSize(const TextureInfo& src, uint32_t mip) { + if (mip == 0) { + return src.input_length; + } + + uint32_t bytes_per_block = src.format_info()->block_width * + src.format_info()->block_height * + src.format_info()->bits_per_pixel / 8; + + uint32_t logical_width = xe::next_pow2(src.width + 1) >> mip; + uint32_t logical_height = xe::next_pow2(src.height + 1) >> mip; + + // w/h in blocks + uint32_t block_width = + xe::round_up(logical_width, src.format_info()->block_width) / + src.format_info()->block_width; + uint32_t block_height = + xe::round_up(logical_height, src.format_info()->block_height) / + src.format_info()->block_height; + + uint32_t size = block_width * block_height * bytes_per_block; + + // Minimum of one tile, which is 32x32 blocks. + uint32_t tile_size = 32 * 32 * bytes_per_block; + return std::max(size, tile_size) * (src.depth + 1); +} + +uint32_t TextureInfo::GetMipLinearSize(const TextureInfo& src, uint32_t mip) { + uint32_t bytes_per_block = src.format_info()->block_width * + src.format_info()->block_height * + src.format_info()->bits_per_pixel / 8; + uint32_t size = src.input_length >> (mip * 2); + + // The size is a multiple of the block size. + return xe::round_up(size, bytes_per_block); +} + +bool TextureInfo::GetPackedTileOffset(uint32_t width, uint32_t height, + const FormatInfo* format_info, uint32_t* out_offset_x, uint32_t* out_offset_y) { // Tile size is 32x32, and once textures go <=16 they are packed into a @@ -226,6 +505,13 @@ bool TextureInfo::GetPackedTileOffset(const TextureInfo& texture_info, // This only works for square textures, or textures that are some non-pot // <= square. As soon as the aspect ratio goes weird, the textures start to // stretch across tiles. + // + // The 2x2 and 1x1 squares are packed in their specific positions because + // each square is the size of at least one block (which is 4x4 pixels max) + // 4x4: x = width & ~0x3 + // 2x2: y = (width & 0x3) << 2 + // 1x1: y = (width & 0x3) << 2 + // // if (tile_aligned(w) > tile_aligned(h)) { // // wider than tall, so packed horizontally // } else if (tile_aligned(w) < tile_aligned(h)) { @@ -238,44 +524,57 @@ bool TextureInfo::GetPackedTileOffset(const TextureInfo& texture_info, // The minimum dimension is what matters most: if either width or height // is <= 16 this mode kicks in. - if (std::min(texture_info.size_2d.logical_width, - texture_info.size_2d.logical_height) > 16) { + uint32_t log2_width = xe::log2_ceil(width); + uint32_t log2_height = xe::log2_ceil(height); + if (std::min(log2_width, log2_height) > 4) { // Too big, not packed. *out_offset_x = 0; *out_offset_y = 0; return false; } - if (xe::log2_ceil(texture_info.size_2d.logical_width) > - xe::log2_ceil(texture_info.size_2d.logical_height)) { + // Find the block offset of the mip. + if (log2_width > log2_height) { // Wider than tall. Laid out vertically. - *out_offset_x = 0; - *out_offset_y = 16; + *out_offset_y = log2_height > 0x1 ? 1 << log2_height : 0; + *out_offset_x = log2_height <= 0x1 ? 1 << (log2_width + 2) : 0; } else { // Taller than wide. Laid out horizontally. - *out_offset_x = 16; - *out_offset_y = 0; + *out_offset_x = log2_width > 0x1 ? 1 << log2_width : 0; + *out_offset_y = log2_width <= 0x1 ? 1 << (log2_height + 2) : 0; } - *out_offset_x /= texture_info.format_info()->block_width; - *out_offset_y /= texture_info.format_info()->block_height; + + *out_offset_x /= format_info->block_width; + *out_offset_y /= format_info->block_height; return true; } +bool TextureInfo::GetPackedTileOffset(const TextureInfo& texture_info, + uint32_t* out_offset_x, + uint32_t* out_offset_y) { + return GetPackedTileOffset(xe::next_pow2(texture_info.size_2d.logical_width), + xe::next_pow2(texture_info.size_2d.logical_height), + texture_info.format_info(), out_offset_x, + out_offset_y); +} + // https://github.com/BinomialLLC/crunch/blob/ea9b8d8c00c8329791256adafa8cf11e4e7942a2/inc/crn_decomp.h#L4108 uint32_t TextureInfo::TiledOffset2DOuter(uint32_t y, uint32_t width, - uint32_t log_bpp) { - uint32_t macro = ((y >> 5) * (width >> 5)) << (log_bpp + 7); - uint32_t micro = ((y & 6) << 2) << log_bpp; - return macro + ((micro & ~15) << 1) + (micro & 15) + - ((y & 8) << (3 + log_bpp)) + ((y & 1) << 4); + uint32_t log2_bpp) { + uint32_t macro = ((y / 32) * (width / 32)) << (log2_bpp + 7); + uint32_t micro = ((y & 6) << 2) << log2_bpp; + return macro + ((micro & ~0xF) << 1) + (micro & 0xF) + + ((y & 8) << (3 + log2_bpp)) + ((y & 1) << 4); } -uint32_t TextureInfo::TiledOffset2DInner(uint32_t x, uint32_t y, uint32_t bpp, +uint32_t TextureInfo::TiledOffset2DInner(uint32_t x, uint32_t y, + uint32_t log2_bpp, uint32_t base_offset) { - uint32_t macro = (x >> 5) << (bpp + 7); - uint32_t micro = (x & 7) << bpp; - uint32_t offset = base_offset + (macro + ((micro & ~15) << 1) + (micro & 15)); - return ((offset & ~511) << 3) + ((offset & 448) << 2) + (offset & 63) + + uint32_t macro = (x / 32) << (log2_bpp + 7); + uint32_t micro = (x & 7) << log2_bpp; + uint32_t offset = + base_offset + (macro + ((micro & ~0xF) << 1) + (micro & 0xF)); + return ((offset & ~0x1FF) << 3) + ((offset & 0x1C0) << 2) + (offset & 0x3F) + ((y & 16) << 7) + (((((y & 8) >> 2) + (x >> 3)) & 3) << 6); } diff --git a/src/xenia/gpu/texture_info.h b/src/xenia/gpu/texture_info.h index 3228a4768..cfce4cc43 100644 --- a/src/xenia/gpu/texture_info.h +++ b/src/xenia/gpu/texture_info.h @@ -256,6 +256,8 @@ struct TextureInfo { Endian endianness; bool is_tiled; bool has_packed_mips; + uint32_t mip_address; + uint32_t mip_levels; uint32_t input_length; const FormatInfo* format_info() const { @@ -283,8 +285,6 @@ struct TextureInfo { uint32_t input_pitch; // byte pitch } size_2d; struct { - } size_3d; - struct { uint32_t logical_width; uint32_t logical_height; uint32_t block_width; // # of horizontal blocks @@ -293,7 +293,7 @@ struct TextureInfo { uint32_t input_height; // texel height uint32_t input_pitch; // byte pitch uint32_t input_face_length; // byte pitch of face - } size_cube; + } size_3d, size_cube; }; static bool Prepare(const xenos::xe_gpu_texture_fetch_t& fetch, @@ -304,12 +304,33 @@ struct TextureInfo { uint32_t width, uint32_t height, TextureInfo* out_info); + static void ConvertTiled(uint8_t* dest, const uint8_t* src, Endian endian, + const FormatInfo* format_info, uint32_t offset_x, + uint32_t offset_y, uint32_t block_pitch, + uint32_t width, uint32_t height, + uint32_t output_width); + + static uint32_t GetMaxMipLevels(uint32_t width, uint32_t height, + uint32_t depth); + + // Get the memory location of a mip. offset_x and offset_y are in blocks. + static uint32_t GetMipLocation(const TextureInfo& src, uint32_t mip, + uint32_t* offset_x, uint32_t* offset_y); + static uint32_t GetMipSize(const TextureInfo& src, uint32_t mip); + + // Get the byte size of a MIP when stored linearly. + static uint32_t GetMipLinearSize(const TextureInfo& src, uint32_t mip); + + static bool GetPackedTileOffset(uint32_t width, uint32_t height, + const FormatInfo* format_info, + uint32_t* out_offset_x, + uint32_t* out_offset_y); static bool GetPackedTileOffset(const TextureInfo& texture_info, uint32_t* out_offset_x, uint32_t* out_offset_y); static uint32_t TiledOffset2DOuter(uint32_t y, uint32_t width, - uint32_t log_bpp); - static uint32_t TiledOffset2DInner(uint32_t x, uint32_t y, uint32_t bpp, + uint32_t log2_bpp); + static uint32_t TiledOffset2DInner(uint32_t x, uint32_t y, uint32_t log2_bpp, uint32_t base_offset); uint64_t hash() const; @@ -318,7 +339,9 @@ struct TextureInfo { } private: + void CalculateTextureSizes1D(uint32_t width); void CalculateTextureSizes2D(uint32_t width, uint32_t height); + void CalculateTextureSizes3D(uint32_t width, uint32_t height, uint32_t depth); void CalculateTextureSizesCube(uint32_t width, uint32_t height, uint32_t depth); }; diff --git a/src/xenia/gpu/vulkan/buffer_cache.cc b/src/xenia/gpu/vulkan/buffer_cache.cc index f975ec09f..0a0162d35 100644 --- a/src/xenia/gpu/vulkan/buffer_cache.cc +++ b/src/xenia/gpu/vulkan/buffer_cache.cc @@ -15,8 +15,7 @@ #include "xenia/base/profiling.h" #include "xenia/gpu/gpu_flags.h" #include "xenia/gpu/vulkan/vulkan_gpu_flags.h" - -#include "third_party/vulkan/vk_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_mem_alloc.h" using namespace xe::gpu::xenos; @@ -104,7 +103,7 @@ BufferCache::BufferCache(RegisterFile* register_file, Memory* memory, device_, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - capacity, 4096); + capacity, 256); } BufferCache::~BufferCache() { Shutdown(); } @@ -120,9 +119,13 @@ VkResult BufferCache::Initialize() { } // Create a memory allocator for textures. + VmaVulkanFunctions vulkan_funcs = {}; + ui::vulkan::FillVMAVulkanFunctions(&vulkan_funcs); + VmaAllocatorCreateInfo alloc_info = { - 0, *device_, *device_, 0, 0, nullptr, nullptr, + 0, *device_, *device_, 0, 0, nullptr, nullptr, 0, nullptr, &vulkan_funcs, }; + status = vmaCreateAllocator(&alloc_info, &mem_allocator_); if (status != VK_SUCCESS) { return status; @@ -147,10 +150,10 @@ VkResult xe::gpu::vulkan::BufferCache::CreateVertexDescriptorPool() { std::vector<VkDescriptorPoolSize> pool_sizes; pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - 65536, + 32 * 16384, }); - vertex_descriptor_pool_ = - std::make_unique<ui::vulkan::DescriptorPool>(*device_, 65536, pool_sizes); + vertex_descriptor_pool_ = std::make_unique<ui::vulkan::DescriptorPool>( + *device_, 32 * 16384, pool_sizes); // 32 storage buffers available to vertex shader. // TODO(DrChat): In the future, this could hold memexport staging data. @@ -287,7 +290,8 @@ VkResult BufferCache::CreateConstantDescriptorSet() { return VK_SUCCESS; } -void xe::gpu::vulkan::BufferCache::FreeConstantDescriptorSet() { + +void BufferCache::FreeConstantDescriptorSet() { if (constant_descriptor_set_) { vkFreeDescriptorSets(*device_, constant_descriptor_pool_, 1, &constant_descriptor_set_); diff --git a/src/xenia/gpu/vulkan/pipeline_cache.cc b/src/xenia/gpu/vulkan/pipeline_cache.cc index 4d5594276..e4ba55ba7 100644 --- a/src/xenia/gpu/vulkan/pipeline_cache.cc +++ b/src/xenia/gpu/vulkan/pipeline_cache.cc @@ -569,17 +569,13 @@ bool PipelineCache::SetDynamicState(VkCommandBuffer command_buffer, // http://ftp.tku.edu.tw/NetBSD/NetBSD-current/xsrc/external/mit/xf86-video-ati/dist/src/r600_reg_auto_r6xx.h // See r200UpdateWindow: // https://github.com/freedreno/mesa/blob/master/src/mesa/drivers/dri/r200/r200_state.c - int16_t window_offset_x = 0; - int16_t window_offset_y = 0; - if ((regs.pa_su_sc_mode_cntl >> 16) & 1) { - window_offset_x = regs.pa_sc_window_offset & 0x7FFF; - window_offset_y = (regs.pa_sc_window_offset >> 16) & 0x7FFF; - if (window_offset_x & 0x4000) { - window_offset_x |= 0x8000; - } - if (window_offset_y & 0x4000) { - window_offset_y |= 0x8000; - } + int16_t window_offset_x = regs.pa_sc_window_offset & 0x7FFF; + int16_t window_offset_y = (regs.pa_sc_window_offset >> 16) & 0x7FFF; + if (window_offset_x & 0x4000) { + window_offset_x |= 0x8000; + } + if (window_offset_y & 0x4000) { + window_offset_y |= 0x8000; } // VK_DYNAMIC_STATE_SCISSOR @@ -593,8 +589,11 @@ bool PipelineCache::SetDynamicState(VkCommandBuffer command_buffer, int32_t ws_y = (regs.pa_sc_window_scissor_tl >> 16) & 0x7FFF; int32_t ws_w = (regs.pa_sc_window_scissor_br & 0x7FFF) - ws_x; int32_t ws_h = ((regs.pa_sc_window_scissor_br >> 16) & 0x7FFF) - ws_y; - ws_x += window_offset_x; - ws_y += window_offset_y; + if (!(regs.pa_sc_window_scissor_tl & 0x80000000)) { + // ! WINDOW_OFFSET_DISABLE + ws_x += window_offset_x; + ws_y += window_offset_y; + } int32_t adj_x = ws_x - std::max(ws_x, 0); int32_t adj_y = ws_y - std::max(ws_y, 0); @@ -657,6 +656,11 @@ bool PipelineCache::SetDynamicState(VkCommandBuffer command_buffer, vport_zscale_enable == vport_xoffset_enable == vport_yoffset_enable == vport_zoffset_enable); + int16_t vtx_window_offset_x = + (regs.pa_su_sc_mode_cntl >> 16) & 1 ? window_offset_x : 0; + int16_t vtx_window_offset_y = + (regs.pa_su_sc_mode_cntl >> 16) & 1 ? window_offset_y : 0; + float vpw, vph, vpx, vpy; if (vport_xscale_enable) { float vox = vport_xoffset_enable ? regs.pa_cl_vport_xoffset : 0; @@ -667,25 +671,21 @@ bool PipelineCache::SetDynamicState(VkCommandBuffer command_buffer, window_width_scalar = window_height_scalar = 1; vpw = 2 * window_width_scalar * vsx; vph = -2 * window_height_scalar * vsy; - vpx = window_width_scalar * vox - vpw / 2 + window_offset_x; - vpy = window_height_scalar * voy - vph / 2 + window_offset_y; + vpx = window_width_scalar * vox - vpw / 2 + vtx_window_offset_x; + vpy = window_height_scalar * voy - vph / 2 + vtx_window_offset_y; } else { - vpw = 2 * 2560.0f * window_width_scalar; - vph = 2 * 2560.0f * window_height_scalar; - vpx = -2560.0f * window_width_scalar + window_offset_x; - vpy = -2560.0f * window_height_scalar + window_offset_y; + // TODO(DrChat): This should be the width/height of the target picture + vpw = 2560.0f; + vph = 2560.0f; + vpx = vtx_window_offset_x; + vpy = vtx_window_offset_y; } if (viewport_state_dirty) { - // float texel_offset_x = regs.pa_su_sc_vtx_cntl & 0x01 ? 0.5f : 0.f; - // float texel_offset_y = regs.pa_su_sc_vtx_cntl & 0x01 ? 0.5f : 0.f; - float texel_offset_x = 0.f; - float texel_offset_y = 0.f; - VkViewport viewport_rect; std::memset(&viewport_rect, 0, sizeof(VkViewport)); - viewport_rect.x = vpx + texel_offset_x; - viewport_rect.y = vpy + texel_offset_y; + viewport_rect.x = vpx; + viewport_rect.y = vpy; viewport_rect.width = vpw; viewport_rect.height = vph; @@ -766,18 +766,21 @@ bool PipelineCache::SetDynamicState(VkCommandBuffer command_buffer, program_cntl.vs_export_mode == 7); assert_false(program_cntl.gen_index_vtx); - SpirvPushConstants push_constants; + SpirvPushConstants push_constants = {}; // Done in VS, no need to flush state. - if ((regs.pa_cl_vte_cntl & (1 << 0)) > 0) { + if (vport_xscale_enable) { push_constants.window_scale[0] = 1.0f; push_constants.window_scale[1] = -1.0f; + push_constants.window_scale[2] = 0.f; + push_constants.window_scale[3] = 0.f; } else { - push_constants.window_scale[0] = 1.0f / 2560.0f; - push_constants.window_scale[1] = 1.0f / 2560.0f; + // 1 / unscaled viewport w/h + push_constants.window_scale[0] = window_width_scalar / 1280.f; + push_constants.window_scale[1] = window_height_scalar / 1280.f; + push_constants.window_scale[2] = (-1280.f / window_width_scalar) + 0.5f; + push_constants.window_scale[3] = (-1280.f / window_height_scalar) + 0.5f; } - push_constants.window_scale[2] = vpw; - push_constants.window_scale[3] = vph; // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf // VTX_XY_FMT = true: the incoming XY have already been multiplied by 1/W0. diff --git a/src/xenia/gpu/vulkan/premake5.lua b/src/xenia/gpu/vulkan/premake5.lua index 3e41ca44d..b399914a8 100644 --- a/src/xenia/gpu/vulkan/premake5.lua +++ b/src/xenia/gpu/vulkan/premake5.lua @@ -7,7 +7,7 @@ project("xenia-gpu-vulkan") kind("StaticLib") language("C++") links({ - "vulkan-loader", + "volk", "xenia-base", "xenia-gpu", "xenia-ui", @@ -40,7 +40,7 @@ project("xenia-gpu-vulkan-trace-viewer") "libavutil", "snappy", "spirv-tools", - "vulkan-loader", + "volk", "xenia-apu", "xenia-apu-nop", "xenia-base", @@ -71,6 +71,15 @@ project("xenia-gpu-vulkan-trace-viewer") "../../base/main_"..platform_suffix..".cc", }) + filter("platforms:Linux") + links({ + "X11", + "xcb", + "X11-xcb", + "GL", + "vulkan", + }) + filter("platforms:Windows") links({ "xenia-apu-xaudio2", @@ -103,7 +112,7 @@ project("xenia-gpu-vulkan-trace-dump") "libavutil", "snappy", "spirv-tools", - "vulkan-loader", + "volk", "xenia-apu", "xenia-apu-nop", "xenia-base", @@ -131,6 +140,15 @@ project("xenia-gpu-vulkan-trace-dump") "../../base/main_"..platform_suffix..".cc", }) + filter("platforms:Linux") + links({ + "X11", + "xcb", + "X11-xcb", + "GL", + "vulkan", + }) + 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" diff --git a/src/xenia/gpu/vulkan/render_cache.cc b/src/xenia/gpu/vulkan/render_cache.cc index abae7b95c..5cdb848c0 100644 --- a/src/xenia/gpu/vulkan/render_cache.cc +++ b/src/xenia/gpu/vulkan/render_cache.cc @@ -87,6 +87,8 @@ class CachedFramebuffer { CachedTileView* color_attachments[4] = {nullptr}; // Reference to depth/stencil attachment, if used. CachedTileView* depth_stencil_attachment = nullptr; + // Associated render pass + VkRenderPass render_pass = nullptr; CachedFramebuffer(VkDevice device, VkRenderPass render_pass, uint32_t surface_width, uint32_t surface_height, @@ -94,6 +96,8 @@ class CachedFramebuffer { CachedTileView* target_depth_stencil_attachment); ~CachedFramebuffer(); + VkResult Initialize(); + bool IsCompatible(const RenderConfiguration& desired_config) const; private: @@ -117,6 +121,8 @@ class CachedRenderPass { CachedRenderPass(VkDevice device, const RenderConfiguration& desired_config); ~CachedRenderPass(); + VkResult Initialize(); + bool IsCompatible(const RenderConfiguration& desired_config) const; private: @@ -124,10 +130,21 @@ class CachedRenderPass { }; CachedTileView::CachedTileView(ui::vulkan::VulkanDevice* device, - VkCommandBuffer command_buffer, VkDeviceMemory edram_memory, TileViewKey view_key) - : device_(*device), key(std::move(view_key)) { + : device_(device), key(std::move(view_key)) {} + +CachedTileView::~CachedTileView() { + VK_SAFE_DESTROY(vkDestroyImageView, *device_, image_view, nullptr); + VK_SAFE_DESTROY(vkDestroyImageView, *device_, image_view_depth, nullptr); + VK_SAFE_DESTROY(vkDestroyImageView, *device_, image_view_stencil, nullptr); + VK_SAFE_DESTROY(vkDestroyImage, *device_, image, nullptr); + VK_SAFE_DESTROY(vkFreeMemory, *device_, memory, nullptr); +} + +VkResult CachedTileView::Initialize(VkCommandBuffer command_buffer) { + VkResult status = VK_SUCCESS; + // Map format to Vulkan. VkFormat vulkan_format = VK_FORMAT_UNDEFINED; uint32_t bpp = 4; @@ -196,22 +213,26 @@ CachedTileView::CachedTileView(ui::vulkan::VulkanDevice* device, image_info.queueFamilyIndexCount = 0; image_info.pQueueFamilyIndices = nullptr; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - auto err = vkCreateImage(device_, &image_info, nullptr, &image); - CheckResult(err, "vkCreateImage"); + status = vkCreateImage(*device_, &image_info, nullptr, &image); + if (status != VK_SUCCESS) { + return status; + } - device->DbgSetObjectName( + device_->DbgSetObjectName( reinterpret_cast<uint64_t>(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, - xe::format_string("%.8X pitch %.8X(%d)", key.tile_offset, key.tile_width, + xe::format_string("RT %.8X %.8X(%d)", key.tile_offset, key.tile_width, key.tile_width)); VkMemoryRequirements memory_requirements; - vkGetImageMemoryRequirements(*device, image, &memory_requirements); + vkGetImageMemoryRequirements(*device_, image, &memory_requirements); // Bind to a newly allocated chunk. // TODO: Alias from a really big buffer? - memory = device->AllocateMemory(memory_requirements, 0); - err = vkBindImageMemory(device_, image, memory, 0); - CheckResult(err, "vkBindImageMemory"); + memory = device_->AllocateMemory(memory_requirements, 0); + status = vkBindImageMemory(*device_, image, memory, 0); + if (status != VK_SUCCESS) { + return status; + } // Create the image view we'll use to attach it to a framebuffer. VkImageViewCreateInfo image_view_info; @@ -235,20 +256,26 @@ CachedTileView::CachedTileView(ui::vulkan::VulkanDevice* device, image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; } - err = vkCreateImageView(device_, &image_view_info, nullptr, &image_view); - CheckResult(err, "vkCreateImageView"); + status = vkCreateImageView(*device_, &image_view_info, nullptr, &image_view); + if (status != VK_SUCCESS) { + return status; + } // Create separate depth/stencil views. if (key.color_or_depth == 0) { image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - err = vkCreateImageView(device_, &image_view_info, nullptr, - &image_view_depth); - CheckResult(err, "vkCreateImageView"); + status = vkCreateImageView(*device_, &image_view_info, nullptr, + &image_view_depth); + if (status != VK_SUCCESS) { + return status; + } image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - err = vkCreateImageView(device_, &image_view_info, nullptr, - &image_view_stencil); - CheckResult(err, "vkCreateImageView"); + status = vkCreateImageView(*device_, &image_view_info, nullptr, + &image_view_stencil); + if (status != VK_SUCCESS) { + return status; + } } // TODO(benvanik): transition to general layout? @@ -279,12 +306,7 @@ CachedTileView::CachedTileView(ui::vulkan::VulkanDevice* device, 0, 0, nullptr, 0, nullptr, 1, &image_barrier); image_layout = image_barrier.newLayout; -} - -CachedTileView::~CachedTileView() { - vkDestroyImageView(device_, image_view, nullptr); - vkDestroyImage(device_, image, nullptr); - vkFreeMemory(device_, memory, nullptr); + return VK_SUCCESS; } CachedFramebuffer::CachedFramebuffer( @@ -294,11 +316,18 @@ CachedFramebuffer::CachedFramebuffer( : device_(device), width(surface_width), height(surface_height), - depth_stencil_attachment(target_depth_stencil_attachment) { + depth_stencil_attachment(target_depth_stencil_attachment), + render_pass(render_pass) { for (int i = 0; i < 4; ++i) { color_attachments[i] = target_color_attachments[i]; } +} + +CachedFramebuffer::~CachedFramebuffer() { + VK_SAFE_DESTROY(vkDestroyFramebuffer, device_, handle, nullptr); +} +VkResult CachedFramebuffer::Initialize() { // Create framebuffer. VkImageView image_views[5] = {nullptr}; int image_view_count = 0; @@ -320,12 +349,7 @@ CachedFramebuffer::CachedFramebuffer( framebuffer_info.width = width; framebuffer_info.height = height; framebuffer_info.layers = 1; - auto err = vkCreateFramebuffer(device_, &framebuffer_info, nullptr, &handle); - CheckResult(err, "vkCreateFramebuffer"); -} - -CachedFramebuffer::~CachedFramebuffer() { - vkDestroyFramebuffer(device_, handle, nullptr); + return vkCreateFramebuffer(device_, &framebuffer_info, nullptr, &handle); } bool CachedFramebuffer::IsCompatible( @@ -372,10 +396,21 @@ CachedRenderPass::CachedRenderPass(VkDevice device, const RenderConfiguration& desired_config) : device_(device) { std::memcpy(&config, &desired_config, sizeof(config)); +} + +CachedRenderPass::~CachedRenderPass() { + for (auto framebuffer : cached_framebuffers) { + delete framebuffer; + } + cached_framebuffers.clear(); + VK_SAFE_DESTROY(vkDestroyRenderPass, device_, handle, nullptr); +} + +VkResult CachedRenderPass::Initialize() { VkSampleCountFlagBits sample_count; if (FLAGS_vulkan_native_msaa) { - switch (desired_config.surface_msaa) { + switch (config.surface_msaa) { case MsaaSamples::k1X: sample_count = VK_SAMPLE_COUNT_1_BIT; break; @@ -386,7 +421,7 @@ CachedRenderPass::CachedRenderPass(VkDevice device, sample_count = VK_SAMPLE_COUNT_4_BIT; break; default: - assert_unhandled_case(desired_config.surface_msaa); + assert_unhandled_case(config.surface_msaa); break; } } else { @@ -480,17 +515,7 @@ CachedRenderPass::CachedRenderPass(VkDevice device, render_pass_info.dependencyCount = 1; render_pass_info.pDependencies = dependencies; - auto err = vkCreateRenderPass(device_, &render_pass_info, nullptr, &handle); - CheckResult(err, "vkCreateRenderPass"); -} - -CachedRenderPass::~CachedRenderPass() { - for (auto framebuffer : cached_framebuffers) { - delete framebuffer; - } - cached_framebuffers.clear(); - - vkDestroyRenderPass(device_, handle, nullptr); + return vkCreateRenderPass(device_, &render_pass_info, nullptr, &handle); } bool CachedRenderPass::IsCompatible( @@ -842,6 +867,14 @@ bool RenderCache::ConfigureRenderPass(VkCommandBuffer command_buffer, // If no render pass was found in the cache create a new one. if (!render_pass) { render_pass = new CachedRenderPass(*device_, *config); + VkResult status = render_pass->Initialize(); + if (status != VK_SUCCESS) { + XELOGE("%s: Failed to create render pass, status %s", __func__, + ui::vulkan::to_string(status)); + delete render_pass; + return false; + } + cached_render_passes_.push_back(render_pass); } @@ -913,6 +946,14 @@ bool RenderCache::ConfigureRenderPass(VkCommandBuffer command_buffer, framebuffer = new CachedFramebuffer( *device_, render_pass->handle, surface_pitch_px, surface_height_px, target_color_attachments, target_depth_stencil_attachment); + VkResult status = framebuffer->Initialize(); + if (status != VK_SUCCESS) { + XELOGE("%s: Failed to create framebuffer, status %s", __func__, + ui::vulkan::to_string(status)); + delete framebuffer; + return false; + } + render_pass->cached_framebuffers.push_back(framebuffer); } @@ -969,10 +1010,17 @@ CachedTileView* RenderCache::FindOrCreateTileView( } // Create a new tile and add to the cache. - tile_view = - new CachedTileView(device_, command_buffer, edram_memory_, view_key); - cached_tile_views_.push_back(tile_view); + tile_view = new CachedTileView(device_, edram_memory_, view_key); + VkResult status = tile_view->Initialize(command_buffer); + if (status != VK_SUCCESS) { + XELOGE("%s: Failed to create tile view, status %s", __func__, + ui::vulkan::to_string(status)); + delete tile_view; + return nullptr; + } + + cached_tile_views_.push_back(tile_view); return tile_view; } diff --git a/src/xenia/gpu/vulkan/render_cache.h b/src/xenia/gpu/vulkan/render_cache.h index 03c500de0..432dc5992 100644 --- a/src/xenia/gpu/vulkan/render_cache.h +++ b/src/xenia/gpu/vulkan/render_cache.h @@ -68,11 +68,12 @@ class CachedTileView { // (if a depth view) Image view of stencil aspect VkImageView image_view_stencil = nullptr; - CachedTileView(ui::vulkan::VulkanDevice* device, - VkCommandBuffer command_buffer, VkDeviceMemory edram_memory, + CachedTileView(ui::vulkan::VulkanDevice* device, VkDeviceMemory edram_memory, TileViewKey view_key); ~CachedTileView(); + VkResult Initialize(VkCommandBuffer command_buffer); + bool IsEqual(const TileViewKey& other_key) const { auto a = reinterpret_cast<const uint64_t*>(&key); auto b = reinterpret_cast<const uint64_t*>(&other_key); @@ -88,7 +89,7 @@ class CachedTileView { } private: - VkDevice device_ = nullptr; + ui::vulkan::VulkanDevice* device_ = nullptr; }; // Parsed render configuration from the current render state. diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index 07bbc409f..bccb1b7b8 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -17,8 +17,7 @@ #include "xenia/gpu/sampler_info.h" #include "xenia/gpu/texture_info.h" #include "xenia/gpu/vulkan/vulkan_gpu_flags.h" - -#include "third_party/vulkan/vk_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_mem_alloc.h" namespace xe { namespace gpu { @@ -62,7 +61,7 @@ static const TextureConfig texture_configs[64] = { // TODO: D24 unsupported on AMD. /* k_24_8 */ {VK_FORMAT_D24_UNORM_S8_UINT}, - /* k_24_8_FLOAT */ {VK_FORMAT_D24_UNORM_S8_UINT}, + /* k_24_8_FLOAT */ {VK_FORMAT_D32_SFLOAT_S8_UINT}, /* k_16 */ {VK_FORMAT_R16_UNORM}, /* k_16_16 */ {VK_FORMAT_R16G16_UNORM}, /* k_16_16_16_16 */ {VK_FORMAT_R16G16B16A16_UNORM}, @@ -198,8 +197,11 @@ VkResult TextureCache::Initialize() { } // Create a memory allocator for textures. + VmaVulkanFunctions vulkan_funcs = {}; + ui::vulkan::FillVMAVulkanFunctions(&vulkan_funcs); + VmaAllocatorCreateInfo alloc_info = { - 0, *device_, *device_, 0, 0, nullptr, nullptr, + 0, *device_, *device_, 0, 0, nullptr, nullptr, 0, nullptr, &vulkan_funcs, }; status = vmaCreateAllocator(&alloc_info, &mem_allocator_); if (status != VK_SUCCESS) { @@ -256,6 +258,18 @@ TextureCache::TextureRegion* TextureCache::AllocateTextureRegion( return nullptr; } + assert_not_null(texture->texture_info.format_info()); + auto& config = + texture_configs[int(texture->texture_info.format_info()->format)]; + VkFormat format = config.host_format; + if (format == VK_FORMAT_UNDEFINED) { + XELOGE( + "Texture Cache: Attempted to allocate texture format %s, which is " + "defined as VK_FORMAT_UNDEFINED!", + texture->texture_info.format_info()->name); + return nullptr; + } + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; @@ -270,7 +284,6 @@ TextureCache::TextureRegion* TextureCache::AllocateTextureRegion( "(0x%.8X != 0x%.8X)", texture->texture_info.format_info()->name, texture->format, (props.optimalTilingFeatures & required_flags), required_flags); - assert_always(); } if (texture->texture_info.dimension != Dimension::kCube && @@ -296,7 +309,7 @@ TextureCache::TextureRegion* TextureCache::AllocateTextureRegion( image_info.format = texture->format; image_info.extent = region_size; - image_info.mipLevels = 1; + image_info.mipLevels = texture->texture_info.mip_levels; image_info.arrayLayers = texture->texture_info.depth + 1; image_info.samples = VK_SAMPLE_COUNT_1_BIT; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -327,6 +340,7 @@ TextureCache::TextureRegion* TextureCache::AllocateTextureRegion( region->image = image; region->image_layout = image_info.initialLayout; + region->usage_flags = image_info.usage; region->allocation = allocation; region->allocation_info = vma_info; @@ -453,8 +467,7 @@ TextureCache::Texture* TextureCache::DemandResolveTexture( // No texture at this location. Make a new one. auto texture = AllocateTexture(texture_info, required_flags); if (!texture) { - // Failed to allocate texture (out of memory?) - assert_always(); + // Failed to allocate texture (out of memory) XELOGE("Vulkan Texture Cache: Failed to allocate texture!"); return nullptr; } @@ -595,8 +608,7 @@ TextureCache::TextureRegion* TextureCache::DemandRegion( auto texture = AllocateTexture(texture_info, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); if (!texture) { - // Failed to allocate texture (out of memory?) - assert_always(); + // Failed to allocate texture (out of memory) XELOGE("Vulkan Texture Cache: Failed to allocate texture!"); return nullptr; } @@ -664,6 +676,13 @@ TextureCache::TextureRegionView* TextureCache::DemandTextureRegionView( VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_IDENTITY, }; + if (region->texture->texture_info.texture_format == + TextureFormat::k_4_4_4_4) { + swiz_component_map[0] = VK_COMPONENT_SWIZZLE_A; + swiz_component_map[1] = VK_COMPONENT_SWIZZLE_B; + swiz_component_map[2] = VK_COMPONENT_SWIZZLE_G; + swiz_component_map[3] = VK_COMPONENT_SWIZZLE_R; + } view_info.components = { swiz_component_map[(swizzle >> 0) & 0x7], @@ -671,7 +690,8 @@ TextureCache::TextureRegionView* TextureCache::DemandTextureRegionView( swiz_component_map[(swizzle >> 6) & 0x7], swiz_component_map[(swizzle >> 9) & 0x7], }; - view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, + region->texture->texture_info.mip_levels, 0, 1}; if (region->texture->format == VK_FORMAT_D16_UNORM_S8_UINT || region->texture->format == VK_FORMAT_D24_UNORM_S8_UINT || region->texture->format == VK_FORMAT_D32_SFLOAT_S8_UINT) { @@ -786,8 +806,6 @@ TextureCache::Sampler* TextureCache::Demand(const SamplerInfo& sampler_info) { sampler_create_info.addressModeW = address_mode_map[static_cast<int>(sampler_info.clamp_w)]; - sampler_create_info.mipLodBias = sampler_info.lod_bias; - float aniso = 0.f; switch (sampler_info.aniso_filter) { case AnisoFilter::kDisabled: @@ -819,8 +837,9 @@ TextureCache::Sampler* TextureCache::Demand(const SamplerInfo& sampler_info) { sampler_create_info.compareEnable = VK_FALSE; sampler_create_info.compareOp = VK_COMPARE_OP_NEVER; - sampler_create_info.minLod = 0.0f; - sampler_create_info.maxLod = 0.0f; + sampler_create_info.mipLodBias = sampler_info.lod_bias; + sampler_create_info.minLod = float(sampler_info.mip_min_level); + sampler_create_info.maxLod = float(sampler_info.mip_max_level); sampler_create_info.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; sampler_create_info.unnormalizedCoordinates = VK_FALSE; VkSampler vk_sampler; @@ -938,13 +957,13 @@ void TextureSwap(Endian endianness, void* dest, const void* src, size_t length) { switch (endianness) { case Endian::k8in16: - xe::copy_and_swap_16_aligned(dest, src, length / 2); + xe::copy_and_swap_16_unaligned(dest, src, length / 2); break; case Endian::k8in32: - xe::copy_and_swap_32_aligned(dest, src, length / 4); + xe::copy_and_swap_32_unaligned(dest, src, length / 4); break; case Endian::k16in32: // Swap high and low 16 bits within a 32 bit word - xe::copy_and_swap_16_in_32_aligned(dest, src, length); + xe::copy_and_swap_16_in_32_unaligned(dest, src, length); break; default: case Endian::kUnspecified: @@ -989,144 +1008,56 @@ void TextureCache::FlushPendingCommands(VkCommandBuffer setup_buffer, vkBeginCommandBuffer(setup_buffer, &begin_info); } -void TextureCache::ConvertTexelCTX1(uint8_t* dest, size_t dest_pitch, - const uint8_t* src, Endian src_endianness) { - // http://fileadmin.cs.lth.se/cs/Personal/Michael_Doggett/talks/unc-xenos-doggett.pdf - union { - uint8_t data[8]; - struct { - uint8_t r0, g0, r1, g1; - uint32_t xx; - }; - } block; - static_assert(sizeof(block) == 8, "CTX1 block mismatch"); - - const uint32_t bytes_per_block = 8; - TextureSwap(src_endianness, block.data, src, bytes_per_block); - - uint8_t cr[4] = { - block.r0, block.r1, - static_cast<uint8_t>(2.f / 3.f * block.r0 + 1.f / 3.f * block.r1), - static_cast<uint8_t>(1.f / 3.f * block.r0 + 2.f / 3.f * block.r1)}; - uint8_t cg[4] = { - block.g0, block.g1, - static_cast<uint8_t>(2.f / 3.f * block.g0 + 1.f / 3.f * block.g1), - static_cast<uint8_t>(1.f / 3.f * block.g0 + 2.f / 3.f * block.g1)}; - - for (uint32_t oy = 0; oy < 4; ++oy) { - for (uint32_t ox = 0; ox < 4; ++ox) { - uint8_t xx = (block.xx >> (((ox + (oy * 4)) * 2))) & 3; - dest[(oy * dest_pitch) + (ox * 2) + 0] = cr[xx]; - dest[(oy * dest_pitch) + (ox * 2) + 1] = cg[xx]; - } - } -} - bool TextureCache::ConvertTexture2D(uint8_t* dest, VkBufferImageCopy* copy_region, - const TextureInfo& src) { - void* host_address = memory_->TranslatePhysical(src.guest_address); + uint32_t mip, const TextureInfo& src) { + uint32_t offset_x = 0; + uint32_t offset_y = 0; + uint32_t address = + TextureInfo::GetMipLocation(src, mip, &offset_x, &offset_y); + void* host_address = memory_->TranslatePhysical(address); + + // Pitch of the source texture in blocks. + uint32_t block_width = mip == 0 + ? src.size_2d.block_width + : xe::next_pow2(src.size_2d.block_width) >> mip; + uint32_t logical_width = src.size_2d.logical_width >> mip; + uint32_t logical_height = src.size_2d.logical_height >> mip; + uint32_t input_width = src.size_2d.input_width >> mip; + uint32_t input_height = src.size_2d.input_height >> mip; + + // All dimensions must be a multiple of block w/h + logical_width = xe::round_up(logical_width, src.format_info()->block_width); + logical_height = + xe::round_up(logical_height, src.format_info()->block_height); + input_width = xe::round_up(input_width, src.format_info()->block_width); + input_height = xe::round_up(input_height, src.format_info()->block_height); + if (!src.is_tiled) { - uint32_t offset_x, offset_y; - if (src.has_packed_mips && - TextureInfo::GetPackedTileOffset(src, &offset_x, &offset_y)) { - uint32_t bytes_per_block = src.format_info()->block_width * - src.format_info()->block_height * - src.format_info()->bits_per_pixel / 8; - - const uint8_t* src_mem = reinterpret_cast<const uint8_t*>(host_address); - src_mem += offset_y * src.size_2d.input_pitch; - src_mem += offset_x * bytes_per_block; - for (uint32_t y = 0; - y < std::min(src.size_2d.block_height, src.size_2d.logical_height); - y++) { - TextureSwap(src.endianness, dest, src_mem, src.size_2d.input_pitch); - src_mem += src.size_2d.input_pitch; - dest += src.size_2d.input_pitch; - } - copy_region->bufferRowLength = src.size_2d.input_width; - copy_region->bufferImageHeight = src.size_2d.input_height; - copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - copy_region->imageExtent = {src.size_2d.logical_width, - src.size_2d.logical_height, 1}; - return true; - } else { - // Fast path copy entire image. - TextureSwap(src.endianness, dest, host_address, src.input_length); - copy_region->bufferRowLength = src.size_2d.input_width; - copy_region->bufferImageHeight = src.size_2d.input_height; - copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - copy_region->imageExtent = {src.size_2d.logical_width, - src.size_2d.logical_height, 1}; - return true; - } + uint32_t bytes_per_block = src.format_info()->block_width * + src.format_info()->block_height * + src.format_info()->bits_per_pixel / 8; + + const uint8_t* src_mem = reinterpret_cast<const uint8_t*>(host_address); + src_mem += offset_y * src.size_2d.input_pitch; + src_mem += offset_x * bytes_per_block; + TextureSwap(src.endianness, dest, src_mem, + src.size_2d.input_pitch * src.size_2d.logical_height); } else { // Untile image. // We could do this in a shader to speed things up, as this is pretty // slow. - - // TODO(benvanik): optimize this inner loop (or work by tiles). const uint8_t* src_mem = reinterpret_cast<const uint8_t*>(host_address); - uint32_t bytes_per_block = src.format_info()->block_width * - src.format_info()->block_height * - src.format_info()->bits_per_pixel / 8; - - uint32_t output_pitch = src.size_2d.input_width * - src.format_info()->block_width * - src.format_info()->bits_per_pixel / 8; - - uint32_t output_row_height = 1; - if (src.texture_format == TextureFormat::k_CTX1) { - // TODO: Can we calculate this? - output_row_height = 4; - } - - // Tiled textures can be packed; get the offset into the packed texture. - uint32_t offset_x; - uint32_t offset_y; - TextureInfo::GetPackedTileOffset(src, &offset_x, &offset_y); - auto log2_bpp = (bytes_per_block >> 2) + - ((bytes_per_block >> 1) >> (bytes_per_block >> 2)); - - // Offset to the current row, in bytes. - uint32_t output_row_offset = 0; - for (uint32_t y = 0; y < src.size_2d.block_height; y++) { - auto input_row_offset = TextureInfo::TiledOffset2DOuter( - offset_y + y, src.size_2d.block_width, log2_bpp); - - // Go block-by-block on this row. - uint32_t output_offset = output_row_offset; - for (uint32_t x = 0; x < src.size_2d.block_width; x++) { - auto input_offset = TextureInfo::TiledOffset2DInner( - offset_x + x, offset_y + y, log2_bpp, input_row_offset); - input_offset >>= log2_bpp; - - if (src.texture_format == TextureFormat::k_CTX1) { - // Convert to R8G8. - ConvertTexelCTX1(&dest[output_offset], output_pitch, src_mem, - src.endianness); - } else { - // Generic swap to destination. - TextureSwap(src.endianness, dest + output_offset, - src_mem + input_offset * bytes_per_block, - bytes_per_block); - } - - output_offset += bytes_per_block; - } - - output_row_offset += output_pitch * output_row_height; - } - - copy_region->bufferRowLength = src.size_2d.input_width; - copy_region->bufferImageHeight = src.size_2d.input_height; - copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; - copy_region->imageExtent = {src.size_2d.logical_width, - src.size_2d.logical_height, 1}; - return true; + TextureInfo::ConvertTiled(dest, src_mem, src.endianness, src.format_info(), + offset_x, offset_y, block_width, logical_width, + logical_height, input_width); } - return false; + copy_region->bufferRowLength = input_width; + copy_region->bufferImageHeight = input_height; + copy_region->imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, mip, 0, 1}; + copy_region->imageExtent = {logical_width, logical_height, 1}; + return true; } bool TextureCache::ConvertTextureCube(uint8_t* dest, @@ -1189,13 +1120,13 @@ bool TextureCache::ConvertTextureCube(uint8_t* dest, } bool TextureCache::ConvertTexture(uint8_t* dest, VkBufferImageCopy* copy_region, - const TextureInfo& src) { + uint32_t mip, const TextureInfo& src) { switch (src.dimension) { case Dimension::k1D: assert_always(); break; case Dimension::k2D: - return ConvertTexture2D(dest, copy_region, src); + return ConvertTexture2D(dest, copy_region, mip, src); case Dimension::k3D: assert_always(); break; @@ -1205,107 +1136,7 @@ bool TextureCache::ConvertTexture(uint8_t* dest, VkBufferImageCopy* copy_region, return false; } -bool TextureCache::ComputeTextureStorage(size_t* output_length, - const TextureInfo& src) { - if (src.texture_format == TextureFormat::k_CTX1) { - switch (src.dimension) { - case Dimension::k1D: { - assert_always(); - } break; - case Dimension::k2D: { - *output_length = src.size_2d.input_width * src.size_2d.input_height * 2; - return true; - } - case Dimension::k3D: { - assert_always(); - } break; - case Dimension::kCube: { - *output_length = - src.size_cube.input_width * src.size_cube.input_height * 2 * 6; - return true; - } - } - return false; - } else { - *output_length = src.input_length; - return true; - } -} - -void TextureCache::WritebackTexture(Texture* texture) { - VkResult status = VK_SUCCESS; - VkFence fence = wb_command_pool_->BeginBatch(); - auto alloc = wb_staging_buffer_.Acquire( - texture->base_region->allocation_info.size, fence); - if (!alloc) { - wb_command_pool_->EndBatch(); - return; - } - - auto command_buffer = wb_command_pool_->AcquireEntry(); - - VkCommandBufferBeginInfo begin_info = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - nullptr, - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - nullptr, - }; - vkBeginCommandBuffer(command_buffer, &begin_info); - - // TODO: Transition the texture to a transfer source. - - VkBufferImageCopy region = { - alloc->offset, - 0, - 0, - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, - {0, 0, 0}, - {texture->texture_info.width + 1, texture->texture_info.height + 1, 1}, - }; - - vkCmdCopyImageToBuffer(command_buffer, texture->base_region->image, - VK_IMAGE_LAYOUT_GENERAL, - wb_staging_buffer_.gpu_buffer(), 1, ®ion); - - // TODO: Transition the texture back to a shader resource. - - vkEndCommandBuffer(command_buffer); - - // Submit the command buffer. - // Submit commands and wait. - { - std::lock_guard<std::mutex>(device_->primary_queue_mutex()); - VkSubmitInfo submit_info = { - VK_STRUCTURE_TYPE_SUBMIT_INFO, - nullptr, - 0, - nullptr, - nullptr, - 1, - &command_buffer, - 0, - nullptr, - }; - status = vkQueueSubmit(device_->primary_queue(), 1, &submit_info, fence); - CheckResult(status, "vkQueueSubmit"); - - if (status == VK_SUCCESS) { - status = vkQueueWaitIdle(device_->primary_queue()); - CheckResult(status, "vkQueueWaitIdle"); - } - } - - wb_command_pool_->EndBatch(); - - auto dest = memory_->TranslatePhysical(texture->texture_info.guest_address); - if (status == VK_SUCCESS) { - std::memcpy(dest, alloc->host_ptr, texture->texture_info.input_length); - } - - wb_staging_buffer_.Scavenge(); -} - -bool TextureCache::UploadTexture(VkCommandBuffer setup_buffer, +bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, VkFence completion_fence, Texture* dest, const TextureInfo& src) { #if FINE_GRAINED_DRAW_SCOPES @@ -1318,26 +1149,36 @@ bool TextureCache::UploadTexture(VkCommandBuffer setup_buffer, return false; } - if (!staging_buffer_.CanAcquire(unpack_length)) { + size_t total_unpack_length = unpack_length; + for (uint32_t i = 1; i < src.mip_levels; i++) { + // Add in more space for mips. + total_unpack_length += TextureInfo::GetMipLinearSize(src, i); + } + + if (!staging_buffer_.CanAcquire(total_unpack_length)) { // Need to have unique memory for every upload for at least one frame. If we // run out of memory, we need to flush all queued upload commands to the // GPU. - FlushPendingCommands(setup_buffer, completion_fence); + FlushPendingCommands(command_buffer, completion_fence); // Uploads have been flushed. Continue. - if (!staging_buffer_.CanAcquire(unpack_length)) { + if (!staging_buffer_.CanAcquire(total_unpack_length)) { // The staging buffer isn't big enough to hold this texture. XELOGE( "TextureCache staging buffer is too small! (uploading 0x%.8X bytes)", - unpack_length); + total_unpack_length); assert_always(); return false; } } // Grab some temporary memory for staging. - auto alloc = staging_buffer_.Acquire(unpack_length, completion_fence); + auto alloc = staging_buffer_.Acquire(total_unpack_length, completion_fence); assert_not_null(alloc); + if (!alloc) { + XELOGE("%s: Failed to acquire staging memory", __func__); + return false; + } // DEBUG: Check the source address. If it's completely zero'd out, print it. bool valid = false; @@ -1352,8 +1193,8 @@ bool TextureCache::UploadTexture(VkCommandBuffer setup_buffer, if (!valid) { XELOGW( "Warning: Uploading blank texture at address 0x%.8X " - "(length: 0x%.8X, format: %d)", - src.guest_address, src.input_length, src.texture_format); + "(length: 0x%.8X, format: %s)", + src.guest_address, src.input_length, src.format_info()->name); } // Invalidate contents of all regions for this texture, except for the base @@ -1368,12 +1209,28 @@ bool TextureCache::UploadTexture(VkCommandBuffer setup_buffer, // TODO: If the GPU supports it, we can submit a compute batch to convert the // texture and copy it to its destination. Otherwise, fallback to conversion // on the CPU. - VkBufferImageCopy copy_region; - if (!ConvertTexture(reinterpret_cast<uint8_t*>(alloc->host_ptr), ©_region, - src)) { + std::vector<VkBufferImageCopy> copy_regions(src.mip_levels); + + // Base MIP + if (!ConvertTexture(reinterpret_cast<uint8_t*>(alloc->host_ptr), + ©_regions[0], 0, src)) { XELOGW("Failed to convert texture"); return false; } + copy_regions[0].bufferOffset = alloc->offset; + copy_regions[0].imageOffset = {0, 0, 0}; + + // Now upload all the MIPs + VkDeviceSize buffer_offset = unpack_length; + for (uint32_t mip = 1; mip < src.mip_levels; mip++) { + uint8_t* dest = reinterpret_cast<uint8_t*>(alloc->host_ptr) + buffer_offset; + ConvertTexture(dest, ©_regions[mip], mip, src); + copy_regions[mip].bufferOffset = alloc->offset + buffer_offset; + copy_regions[mip].imageOffset = {0, 0, 0}; + + // With each mip, the length is divided by 4. + buffer_offset += TextureInfo::GetMipLinearSize(src, mip); + } // Transition the texture into a transfer destination layout. VkImageMemoryBarrier barrier; @@ -1386,9 +1243,9 @@ bool TextureCache::UploadTexture(VkCommandBuffer setup_buffer, barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = dest->base_region->image; - barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, - copy_region.imageSubresource.baseArrayLayer, - copy_region.imageSubresource.layerCount}; + barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, src.mip_levels, + copy_regions[0].imageSubresource.baseArrayLayer, + copy_regions[0].imageSubresource.layerCount}; if (dest->format == VK_FORMAT_D16_UNORM_S8_UINT || dest->format == VK_FORMAT_D24_UNORM_S8_UINT || dest->format == VK_FORMAT_D32_SFLOAT_S8_UINT) { @@ -1396,29 +1253,29 @@ bool TextureCache::UploadTexture(VkCommandBuffer setup_buffer, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; } - vkCmdPipelineBarrier(setup_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); // Now move the converted texture into the destination. - copy_region.bufferOffset = alloc->offset; - copy_region.imageOffset = {0, 0, 0}; if (dest->format == VK_FORMAT_D16_UNORM_S8_UINT || dest->format == VK_FORMAT_D24_UNORM_S8_UINT || dest->format == VK_FORMAT_D32_SFLOAT_S8_UINT) { // Do just a depth upload (for now). - copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + // This assumes depth buffers don't have mips (hopefully they don't) + copy_regions[0].imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; } - vkCmdCopyBufferToImage(setup_buffer, staging_buffer_.gpu_buffer(), + vkCmdCopyBufferToImage(command_buffer, staging_buffer_.gpu_buffer(), dest->base_region->image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, src.mip_levels, + copy_regions.data()); // Now transition the texture into a shader readonly source. barrier.srcAccessMask = barrier.dstAccessMask; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; barrier.oldLayout = barrier.newLayout; barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - vkCmdPipelineBarrier(setup_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); @@ -1427,6 +1284,106 @@ bool TextureCache::UploadTexture(VkCommandBuffer setup_buffer, return true; } +bool TextureCache::ComputeTextureStorage(size_t* output_length, + const TextureInfo& src) { + if (src.texture_format == TextureFormat::k_CTX1) { + switch (src.dimension) { + case Dimension::k1D: { + assert_always(); + } break; + case Dimension::k2D: { + *output_length = src.size_2d.input_width * src.size_2d.input_height * 2; + return true; + } + case Dimension::k3D: { + assert_always(); + } break; + case Dimension::kCube: { + *output_length = + src.size_cube.input_width * src.size_cube.input_height * 2 * 6; + return true; + } + } + return false; + } else { + *output_length = src.input_length; + return true; + } +} + +void TextureCache::WritebackTexture(Texture* texture) { + VkResult status = VK_SUCCESS; + VkFence fence = wb_command_pool_->BeginBatch(); + auto alloc = wb_staging_buffer_.Acquire( + texture->base_region->allocation_info.size, fence); + if (!alloc) { + wb_command_pool_->EndBatch(); + return; + } + + auto command_buffer = wb_command_pool_->AcquireEntry(); + + VkCommandBufferBeginInfo begin_info = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + nullptr, + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + nullptr, + }; + vkBeginCommandBuffer(command_buffer, &begin_info); + + // TODO: Transition the texture to a transfer source. + + VkBufferImageCopy region = { + alloc->offset, + 0, + 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, + {texture->texture_info.width + 1, texture->texture_info.height + 1, 1}, + }; + + vkCmdCopyImageToBuffer(command_buffer, texture->base_region->image, + VK_IMAGE_LAYOUT_GENERAL, + wb_staging_buffer_.gpu_buffer(), 1, ®ion); + + // TODO: Transition the texture back to a shader resource. + + vkEndCommandBuffer(command_buffer); + + // Submit the command buffer. + // Submit commands and wait. + { + std::lock_guard<std::mutex>(device_->primary_queue_mutex()); + VkSubmitInfo submit_info = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + nullptr, + 0, + nullptr, + nullptr, + 1, + &command_buffer, + 0, + nullptr, + }; + status = vkQueueSubmit(device_->primary_queue(), 1, &submit_info, fence); + CheckResult(status, "vkQueueSubmit"); + + if (status == VK_SUCCESS) { + status = vkQueueWaitIdle(device_->primary_queue()); + CheckResult(status, "vkQueueWaitIdle"); + } + } + + wb_command_pool_->EndBatch(); + + auto dest = memory_->TranslatePhysical(texture->texture_info.guest_address); + if (status == VK_SUCCESS) { + std::memcpy(dest, alloc->host_ptr, texture->texture_info.input_length); + } + + wb_staging_buffer_.Scavenge(); +} + void TextureCache::HashTextureBindings( XXH64_state_t* hash_state, uint32_t& fetch_mask, const std::vector<Shader::TextureBinding>& bindings) { diff --git a/src/xenia/gpu/vulkan/texture_cache.h b/src/xenia/gpu/vulkan/texture_cache.h index c19a58f3e..01716b864 100644 --- a/src/xenia/gpu/vulkan/texture_cache.h +++ b/src/xenia/gpu/vulkan/texture_cache.h @@ -65,6 +65,7 @@ class TextureCache { VkImage image; VkImageLayout image_layout; + VkImageUsageFlags usage_flags; VmaAllocation allocation; VmaAllocationInfo allocation_info; @@ -172,15 +173,12 @@ class TextureCache { void FlushPendingCommands(VkCommandBuffer setup_buffer, VkFence completion_fence); - static void ConvertTexelCTX1(uint8_t* dest, size_t dest_pitch, - const uint8_t* src, Endian src_endianness); - bool ConvertTexture2D(uint8_t* dest, VkBufferImageCopy* copy_region, - const TextureInfo& src); - bool ConvertTextureCube(uint8_t* dest, VkBufferImageCopy* copy_region, + uint32_t mip, const TextureInfo& src); + bool ConvertTextureCube(uint8_t* dest, VkBufferImageCopy* copy_regions, const TextureInfo& src); bool ConvertTexture(uint8_t* dest, VkBufferImageCopy* copy_region, - const TextureInfo& src); + uint32_t mip, const TextureInfo& src); bool ComputeTextureStorage(size_t* output_length, const TextureInfo& src); // Writes a texture back into guest memory. This call is (mostly) asynchronous diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index 559fad74d..335a312d6 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -915,15 +915,8 @@ bool VulkanCommandProcessor::IssueCopy() { // vtx_window_offset_enable assert_true(regs[XE_GPU_REG_PA_SU_SC_MODE_CNTL].u32 & 0x00010000); uint32_t window_offset = regs[XE_GPU_REG_PA_SC_WINDOW_OFFSET].u32; - int16_t window_offset_x = window_offset & 0x7FFF; - int16_t window_offset_y = (window_offset >> 16) & 0x7FFF; - // Sign-extension - if (window_offset_x & 0x4000) { - window_offset_x |= 0x8000; - } - if (window_offset_y & 0x4000) { - window_offset_y |= 0x8000; - } + int32_t window_offset_x = window_regs->window_offset.window_x_offset; + int32_t window_offset_y = window_regs->window_offset.window_y_offset; uint32_t dest_texel_size = uint32_t(GetTexelSize(copy_dest_format)); @@ -1027,7 +1020,18 @@ bool VulkanCommandProcessor::IssueCopy() { std::max(1u, dest_logical_height), &texture_info); auto texture = texture_cache_->DemandResolveTexture(texture_info); - assert_not_null(texture); + if (!texture) { + // Out of memory. + return false; + } + + if (!(texture->base_region->usage_flags & + (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))) { + // Resolve image doesn't support drawing, and we don't support conversion. + return false; + } + texture->in_flight_fence = current_batch_fence_; // For debugging purposes only (trace viewer) @@ -1188,35 +1192,50 @@ bool VulkanCommandProcessor::IssueCopy() { resolve_extent, }; - // By offsetting the destination texture by the window offset, we've - // already handled it and need to subtract the window offset from the - // destination rectangle. VkRect2D dst_rect = { - {resolve_offset.x + window_offset_x, - resolve_offset.y + window_offset_y}, + {resolve_offset.x, resolve_offset.y}, resolve_extent, }; + // If the destination rectangle lies outside the window, make it start + // inside. The Xenos does not copy pixel data at any offset in screen + // coordinates. + int32_t dst_adj_x = + std::max(dst_rect.offset.x, -window_offset_x) - dst_rect.offset.x; + int32_t dst_adj_y = + std::max(dst_rect.offset.y, -window_offset_y) - dst_rect.offset.y; + + if (uint32_t(dst_adj_x) > dst_rect.extent.width || + uint32_t(dst_adj_y) > dst_rect.extent.height) { + // No-op? + break; + } + + dst_rect.offset.x += dst_adj_x; + dst_rect.offset.y += dst_adj_y; + dst_rect.extent.width -= dst_adj_x; + dst_rect.extent.height -= dst_adj_y; + src_rect.extent.width -= dst_adj_x; + src_rect.extent.height -= dst_adj_y; + VkViewport viewport = { - float(-window_offset_x), - float(-window_offset_y), - float(copy_dest_pitch), - float(copy_dest_height), - 0.f, - 1.f, + 0.f, 0.f, float(copy_dest_pitch), float(copy_dest_height), 0.f, 1.f, }; + uint32_t scissor_tl_x = window_regs->window_scissor_tl.tl_x; + uint32_t scissor_br_x = window_regs->window_scissor_br.br_x; + uint32_t scissor_tl_y = window_regs->window_scissor_tl.tl_y; + uint32_t scissor_br_y = window_regs->window_scissor_br.br_y; + + // Clamp the values to destination dimensions. + scissor_tl_x = std::min(scissor_tl_x, copy_dest_pitch); + scissor_br_x = std::min(scissor_br_x, copy_dest_pitch); + scissor_tl_y = std::min(scissor_tl_y, copy_dest_height); + scissor_br_y = std::min(scissor_br_y, copy_dest_height); + VkRect2D scissor = { - { - int32_t(window_regs->window_scissor_tl.tl_x.value()), - int32_t(window_regs->window_scissor_tl.tl_y.value()), - }, - { - window_regs->window_scissor_br.br_x.value() - - window_regs->window_scissor_tl.tl_x.value(), - window_regs->window_scissor_br.br_y.value() - - window_regs->window_scissor_tl.tl_y.value(), - }, + {int32_t(scissor_tl_x), int32_t(scissor_tl_y)}, + {scissor_br_x - scissor_tl_x, scissor_br_y - scissor_tl_y}, }; blitter_->BlitTexture2D( diff --git a/src/xenia/gpu/xenos.h b/src/xenia/gpu/xenos.h index e0dd8e35c..d6a24a856 100644 --- a/src/xenia/gpu/xenos.h +++ b/src/xenia/gpu/xenos.h @@ -601,20 +601,18 @@ enum Type3Opcode { }; // clang-format on -template <uint16_t index, uint16_t count, bool one_reg = false> -constexpr inline uint32_t MakePacketType0() { +inline uint32_t MakePacketType0(uint16_t index, uint16_t count, + bool one_reg = false) { // ttcccccc cccccccc oiiiiiii iiiiiiii - static_assert(index <= 0x7FFF, "index must be <= 0x7FFF"); - static_assert(count >= 1 && count <= 0x4000, - "count must be >= 1 and <= 0x4000"); + assert(index <= 0x7FFF); + assert(count >= 1 && count <= 0x4000); return (0u << 30) | (((count - 1) & 0x3FFF) << 16) | (index & 0x7FFF); } -template <uint16_t index_1, uint16_t index_2> -constexpr inline uint32_t MakePacketType1() { +inline uint32_t MakePacketType1(uint16_t index_1, uint16_t index_2) { // tt?????? ??222222 22222111 11111111 - static_assert(index_1 <= 0x7FF, "index_1 must be <= 0x7FF"); - static_assert(index_2 <= 0x7FF, "index_2 must be <= 0x7FF"); + assert(index_1 <= 0x7FF); + assert(index_2 <= 0x7FF); return (1u << 30) | ((index_2 & 0x7FF) << 11) | (index_1 & 0x7FF); } @@ -623,12 +621,11 @@ constexpr inline uint32_t MakePacketType2() { return (2u << 30); } -template <Type3Opcode opcode, uint16_t count, bool predicate = false> -constexpr inline uint32_t MakePacketType3() { +inline uint32_t MakePacketType3(Type3Opcode opcode, uint16_t count, + bool predicate = false) { // ttcccccc cccccccc ?ooooooo ???????p - static_assert(opcode <= 0x7F, "opcode must be <= 0x7F"); - static_assert(count >= 1 && count <= 0x4000, - "count must be >= 1 and <= 0x4000"); + assert(opcode <= 0x7F); + assert(count >= 1 && count <= 0x4000); return (3u << 30) | (((count - 1) & 0x3FFF) << 16) | ((opcode & 0x7F) << 8) | (predicate ? 1 : 0); } diff --git a/src/xenia/hid/hid_demo.cc b/src/xenia/hid/hid_demo.cc index 70c84a35f..5afe6eef4 100644 --- a/src/xenia/hid/hid_demo.cc +++ b/src/xenia/hid/hid_demo.cc @@ -17,8 +17,8 @@ #include "xenia/base/main.h" #include "xenia/base/threading.h" #include "xenia/hid/input_system.h" -#include "xenia/ui/gl/gl_provider.h" #include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/vulkan/vulkan_provider.h" #include "xenia/ui/window.h" // Available input drivers: @@ -67,7 +67,7 @@ std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers( std::unique_ptr<xe::ui::GraphicsProvider> CreateDemoGraphicsProvider( xe::ui::Window* window) { - return xe::ui::gl::GLProvider::Create(window); + return xe::ui::vulkan::VulkanProvider::Create(window); } void DrawInputStatus(); diff --git a/src/xenia/hid/premake5.lua b/src/xenia/hid/premake5.lua index 69ea6c197..a348160f6 100644 --- a/src/xenia/hid/premake5.lua +++ b/src/xenia/hid/premake5.lua @@ -26,12 +26,23 @@ project("xenia-hid-demo") "gflags", "glew", "imgui", + "volk", "xenia-base", "xenia-hid", "xenia-hid-nop", "xenia-ui", - "xenia-ui-gl", + "xenia-ui-vulkan", }) + filter("platforms:Linux") + links({ + "X11", + "xcb", + "X11-xcb", + "GL", + "vulkan", + }) + filter() + flags({ "WinMain", -- Use WinMain instead of main. }) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc index 560cec820..92f9ac2d3 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc @@ -160,13 +160,13 @@ DECLARE_XBOXKRNL_EXPORT(VdSetDisplayModeOverride, ExportTag::kVideo | ExportTag::kStub); dword_result_t VdInitializeEngines(unknown_t unk0, function_t callback, - lpvoid_t arg, lpunknown_t unk2_ptr, - lpunknown_t unk3_ptr) { + lpvoid_t arg, lpdword_t pfp_ptr, + lpdword_t me_ptr) { // r3 = 0x4F810000 // r4 = function ptr (cleanup callback?) // r5 = function arg - // r6 = register init cmds(?) - // r7 = gpu init cmds(?) + // r6 = PFP Microcode + // r7 = ME Microcode return 1; } DECLARE_XBOXKRNL_EXPORT(VdInitializeEngines, @@ -205,12 +205,6 @@ DECLARE_XBOXKRNL_EXPORT(VdSetGraphicsInterruptCallback, ExportTag::kVideo); void VdInitializeRingBuffer(lpvoid_t ptr, int_t log2_size) { // r3 = result of MmGetPhysicalAddress // r4 = log2(size) - // r4 is or'd with 0x802 and then stuffed into CP_RB_CNTL - // according to AMD docs, this corresponds with RB_BUFSZ, which is log2 - // actual size. - // 0x8 is RB_BLKSZ, or number of words gpu will read before updating the - // host read pointer. - // So being or'd with 0x2 makes the ring buffer size always a multiple of 4. // Buffer pointers are from MmAllocatePhysicalMemory with WRITE_COMBINE. auto graphics_system = kernel_state()->emulator()->graphics_system(); graphics_system->InitializeRingBuffer(ptr, log2_size); @@ -374,7 +368,7 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer // Write in the texture fetch. dwords[offset++] = - xenos::MakePacketType0<gpu::XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0, 6>(); + xenos::MakePacketType0(gpu::XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0, 6); dwords[offset++] = fetch.dword_0; dwords[offset++] = fetch.dword_1; dwords[offset++] = fetch.dword_2; @@ -382,7 +376,7 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer dwords[offset++] = fetch.dword_4; dwords[offset++] = fetch.dword_5; - dwords[offset++] = xenos::MakePacketType3<xenos::PM4_XE_SWAP, 4>(); + dwords[offset++] = xenos::MakePacketType3(xenos::PM4_XE_SWAP, 4); dwords[offset++] = 'SWAP'; dwords[offset++] = (*frontbuffer_ptr) & 0x1FFFFFFF; diff --git a/src/xenia/ui/gl/blitter.cc b/src/xenia/ui/gl/blitter.cc deleted file mode 100644 index 7501758ca..000000000 --- a/src/xenia/ui/gl/blitter.cc +++ /dev/null @@ -1,315 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/gl/blitter.h" - -#include <string> - -#include "xenia/base/assert.h" -#include "xenia/base/math.h" -#include "xenia/ui/gl/gl_context.h" - -namespace xe { -namespace ui { -namespace gl { - -Blitter::Blitter() - : vertex_program_(0), - color_fragment_program_(0), - depth_fragment_program_(0), - color_pipeline_(0), - depth_pipeline_(0), - vbo_(0), - vao_(0), - nearest_sampler_(0), - linear_sampler_(0), - scratch_framebuffer_(0) {} - -Blitter::~Blitter() = default; - -bool Blitter::Initialize() { - const std::string header = - R"( -#version 450 -#extension GL_ARB_explicit_uniform_location : require -#extension GL_ARB_shading_language_420pack : require -precision highp float; -precision highp int; -layout(std140, column_major) uniform; -layout(std430, column_major) buffer; -)"; - const std::string vs_source = header + - R"( -layout(location = 0) uniform vec4 src_uv; -out gl_PerVertex { - vec4 gl_Position; - float gl_PointSize; - float gl_ClipDistance[]; -}; -layout(location = 0) in vec2 vfetch_pos; -layout(location = 0) out vec2 vtx_uv; -void main() { - gl_Position = vec4(vfetch_pos.xy * vec2(2.0, -2.0) - - vec2(1.0, -1.0), 0.0, 1.0); - vtx_uv = vfetch_pos.xy * src_uv.zw + src_uv.xy; -})"; - const std::string color_fs_source = header + - R"( -layout(location = 1) uniform sampler2D src_texture; -layout(location = 2) uniform bool swap; -layout(location = 0) in vec2 vtx_uv; -layout(location = 0) out vec4 oC; -void main() { - oC = texture(src_texture, vtx_uv); - if (!swap) oC = oC.bgra; -})"; - const std::string depth_fs_source = header + - R"( -layout(location = 1) uniform sampler2D src_texture; -layout(location = 0) in vec2 vtx_uv; -layout(location = 0) out vec4 oC; -void main() { - gl_FragDepth = texture(src_texture, vtx_uv).r; -})"; - - auto vs_source_str = vs_source.c_str(); - vertex_program_ = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &vs_source_str); - auto color_fs_source_str = color_fs_source.c_str(); - color_fragment_program_ = - glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &color_fs_source_str); - auto depth_fs_source_str = depth_fs_source.c_str(); - depth_fragment_program_ = - glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &depth_fs_source_str); - glCreateProgramPipelines(1, &color_pipeline_); - glUseProgramStages(color_pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_); - glUseProgramStages(color_pipeline_, GL_FRAGMENT_SHADER_BIT, - color_fragment_program_); - glCreateProgramPipelines(1, &depth_pipeline_); - glUseProgramStages(depth_pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_); - glUseProgramStages(depth_pipeline_, GL_FRAGMENT_SHADER_BIT, - depth_fragment_program_); - - glCreateBuffers(1, &vbo_); - static const GLfloat vbo_data[] = { - 0, 0, 1, 0, 0, 1, 1, 1, - }; - glNamedBufferStorage(vbo_, sizeof(vbo_data), vbo_data, 0); - - glCreateVertexArrays(1, &vao_); - glEnableVertexArrayAttrib(vao_, 0); - glVertexArrayAttribBinding(vao_, 0, 0); - glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE, 0); - glVertexArrayVertexBuffer(vao_, 0, vbo_, 0, sizeof(GLfloat) * 2); - - glCreateSamplers(1, &nearest_sampler_); - glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glCreateSamplers(1, &linear_sampler_); - glSamplerParameteri(linear_sampler_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(linear_sampler_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glCreateFramebuffers(1, &scratch_framebuffer_); - - return true; -} - -void Blitter::Shutdown() { - glDeleteFramebuffers(1, &scratch_framebuffer_); - glDeleteProgram(vertex_program_); - glDeleteProgram(color_fragment_program_); - glDeleteProgram(depth_fragment_program_); - glDeleteProgramPipelines(1, &color_pipeline_); - glDeleteProgramPipelines(1, &depth_pipeline_); - glDeleteBuffers(1, &vbo_); - glDeleteVertexArrays(1, &vao_); - glDeleteSamplers(1, &nearest_sampler_); - glDeleteSamplers(1, &linear_sampler_); -} - -struct SavedState { - GLboolean scissor_test_enabled; - GLboolean depth_test_enabled; - GLboolean depth_mask_enabled; - GLint depth_func; - GLboolean stencil_test_enabled; - GLboolean cull_face_enabled; - GLint cull_face; - GLint front_face; - GLint polygon_mode; - GLboolean color_mask_0_enabled[4]; - GLboolean blend_0_enabled; - GLint draw_buffer; - GLfloat viewport[4]; - GLint program_pipeline; - GLint vertex_array; - GLint texture_0; - GLint sampler_0; - - void Save() { - scissor_test_enabled = glIsEnabled(GL_SCISSOR_TEST); - depth_test_enabled = glIsEnabled(GL_DEPTH_TEST); - glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask_enabled); - glGetIntegerv(GL_DEPTH_FUNC, &depth_func); - stencil_test_enabled = glIsEnabled(GL_STENCIL_TEST); - cull_face_enabled = glIsEnabled(GL_CULL_FACE); - glGetIntegerv(GL_CULL_FACE_MODE, &cull_face); - glGetIntegerv(GL_FRONT_FACE, &front_face); - glGetIntegerv(GL_POLYGON_MODE, &polygon_mode); - glGetBooleani_v(GL_COLOR_WRITEMASK, 0, - reinterpret_cast<GLboolean*>(&color_mask_0_enabled)); - blend_0_enabled = glIsEnabledi(GL_BLEND, 0); - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_buffer); - glGetFloati_v(GL_VIEWPORT, 0, viewport); - glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &program_pipeline); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vertex_array); - glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture_0); - glGetIntegerv(GL_SAMPLER_BINDING, &sampler_0); - } - - void Restore() { - scissor_test_enabled ? glEnable(GL_SCISSOR_TEST) - : glDisable(GL_SCISSOR_TEST); - depth_test_enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST); - glDepthMask(depth_mask_enabled); - glDepthFunc(depth_func); - stencil_test_enabled ? glEnable(GL_STENCIL_TEST) - : glDisable(GL_STENCIL_TEST); - cull_face_enabled ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE); - glCullFace(cull_face); - glFrontFace(front_face); - glPolygonMode(GL_FRONT_AND_BACK, polygon_mode); - glColorMaski(0, color_mask_0_enabled[0], color_mask_0_enabled[1], - color_mask_0_enabled[2], color_mask_0_enabled[3]); - blend_0_enabled ? glEnablei(GL_BLEND, 0) : glDisablei(GL_BLEND, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_buffer); - glViewportIndexedf(0, viewport[0], viewport[1], viewport[2], viewport[3]); - glBindProgramPipeline(program_pipeline); - glBindVertexArray(vertex_array); - glBindTexture(GL_TEXTURE_2D, texture_0); - glBindSampler(0, sampler_0); - } -}; - -void Blitter::Draw(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect, - GLenum filter) { - assert_not_zero(src_texture); - - glDisable(GL_SCISSOR_TEST); - glDisable(GL_STENCIL_TEST); - glDisablei(GL_BLEND, 0); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CW); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glBindVertexArray(vao_); - glBindTextures(0, 1, &src_texture); - switch (filter) { - default: - case GL_NEAREST: - glBindSampler(0, nearest_sampler_); - break; - case GL_LINEAR: - glBindSampler(0, linear_sampler_); - break; - } - - glViewportIndexedf(0, GLfloat(dest_rect.x), GLfloat(dest_rect.y), - GLfloat(dest_rect.width), GLfloat(dest_rect.height)); - - // TODO(benvanik): avoid this? - GLint src_texture_width; - glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_WIDTH, - &src_texture_width); - GLint src_texture_height; - glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_HEIGHT, - &src_texture_height); - glProgramUniform4f(vertex_program_, 0, - src_rect.x / static_cast<float>(src_texture_width), - src_rect.y / static_cast<float>(src_texture_height), - src_rect.width / static_cast<float>(src_texture_width), - src_rect.height / static_cast<float>(src_texture_height)); - - // Useful for seeing the entire framebuffer/etc: - // glProgramUniform4f(vertex_program_, 0, 0.0f, 0.0f, 1.0f, 1.0f); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); -} - -void Blitter::BlitTexture2D(GLuint src_texture, Rect2D src_rect, - Rect2D dest_rect, GLenum filter, - bool swap_channels) { - SavedState state; - state.Save(); - - glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glStencilMask(0xFF); - glBindProgramPipeline(color_pipeline_); - - glProgramUniform1i(color_fragment_program_, 2, swap_channels ? 1 : 0); - - Draw(src_texture, src_rect, dest_rect, filter); - - state.Restore(); -} - -void Blitter::CopyColorTexture2D(GLuint src_texture, Rect2D src_rect, - GLuint dest_texture, Rect2D dest_rect, - GLenum filter, bool swap_channels) { - SavedState state; - state.Save(); - - glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glBindProgramPipeline(color_pipeline_); - - glProgramUniform1i(color_fragment_program_, 2, swap_channels ? 1 : 0); - - glNamedFramebufferTexture(scratch_framebuffer_, GL_COLOR_ATTACHMENT0, - dest_texture, 0); - glNamedFramebufferDrawBuffer(scratch_framebuffer_, GL_COLOR_ATTACHMENT0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, scratch_framebuffer_); - Draw(src_texture, src_rect, dest_rect, filter); - glNamedFramebufferDrawBuffer(scratch_framebuffer_, GL_NONE); - glNamedFramebufferTexture(scratch_framebuffer_, GL_COLOR_ATTACHMENT0, GL_NONE, - 0); - - state.Restore(); -} - -void Blitter::CopyDepthTexture(GLuint src_texture, Rect2D src_rect, - GLuint dest_texture, Rect2D dest_rect) { - SavedState state; - state.Save(); - - glColorMaski(0, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_TRUE); - glBindProgramPipeline(depth_pipeline_); - - glNamedFramebufferTexture(scratch_framebuffer_, GL_DEPTH_STENCIL_ATTACHMENT, - dest_texture, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, scratch_framebuffer_); - Draw(src_texture, src_rect, dest_rect, GL_NEAREST); - glNamedFramebufferTexture(scratch_framebuffer_, GL_DEPTH_STENCIL_ATTACHMENT, - GL_NONE, 0); - - state.Restore(); -} - -} // namespace gl -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/gl/blitter.h b/src/xenia/ui/gl/blitter.h deleted file mode 100644 index 3080c6aaa..000000000 --- a/src/xenia/ui/gl/blitter.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GL_BLITTER_H_ -#define XENIA_UI_GL_BLITTER_H_ - -#include <memory> - -#include "xenia/ui/gl/gl.h" - -namespace xe { -namespace ui { -namespace gl { - -struct Rect2D { - int32_t x; - int32_t y; - int32_t width; - int32_t height; - Rect2D() : x(0), y(0), width(0), height(0) {} - Rect2D(int32_t x_, int32_t y_, int32_t width_, int32_t height_) - : x(x_), y(y_), width(width_), height(height_) {} - int32_t right() const { return x + width; } - int32_t bottom() const { return y + height; } -}; - -class Blitter { - public: - Blitter(); - ~Blitter(); - - bool Initialize(); - void Shutdown(); - - void BlitTexture2D(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect, - GLenum filter, bool swap_channels); - - void CopyColorTexture2D(GLuint src_texture, Rect2D src_rect, - GLuint dest_texture, Rect2D dest_rect, GLenum filter, - bool swap_channels); - void CopyDepthTexture(GLuint src_texture, Rect2D src_rect, - GLuint dest_texture, Rect2D dest_rect); - - private: - void Draw(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect, - GLenum filter); - - GLuint vertex_program_; - GLuint color_fragment_program_; - GLuint depth_fragment_program_; - GLuint color_pipeline_; - GLuint depth_pipeline_; - GLuint vbo_; - GLuint vao_; - GLuint nearest_sampler_; - GLuint linear_sampler_; - GLuint scratch_framebuffer_; -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_BLITTER_H_ diff --git a/src/xenia/ui/gl/circular_buffer.cc b/src/xenia/ui/gl/circular_buffer.cc deleted file mode 100644 index 863b1bd2d..000000000 --- a/src/xenia/ui/gl/circular_buffer.cc +++ /dev/null @@ -1,139 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/gl/circular_buffer.h" - -#include <algorithm> - -#include "xenia/base/assert.h" -#include "xenia/base/math.h" - -namespace xe { -namespace ui { -namespace gl { - -CircularBuffer::CircularBuffer(size_t capacity, size_t alignment) - : capacity_(capacity), - alignment_(alignment), - write_head_(0), - dirty_start_(UINT64_MAX), - dirty_end_(0), - buffer_(0), - gpu_base_(0), - host_base_(nullptr) {} - -CircularBuffer::~CircularBuffer() { Shutdown(); } - -bool CircularBuffer::Initialize() { - glCreateBuffers(1, &buffer_); - glNamedBufferStorage(buffer_, capacity_, nullptr, - GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT); - host_base_ = reinterpret_cast<uint8_t*>(glMapNamedBufferRange( - buffer_, 0, capacity_, - GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_PERSISTENT_BIT)); - assert_not_null(host_base_); - if (!host_base_) { - return false; - } - - return true; -} - -void CircularBuffer::Shutdown() { - if (!buffer_) { - return; - } - glUnmapNamedBuffer(buffer_); - glDeleteBuffers(1, &buffer_); - buffer_ = 0; -} - -bool CircularBuffer::CanAcquire(size_t length) { - size_t aligned_length = xe::round_up(length, alignment_); - return write_head_ + aligned_length <= capacity_; -} - -CircularBuffer::Allocation CircularBuffer::Acquire(size_t length) { - // Addresses must always be % 256. - size_t aligned_length = xe::round_up(length, alignment_); - assert_true(aligned_length <= capacity_, "Request too large"); - if (write_head_ + aligned_length > capacity_) { - // Flush and wait. - WaitUntilClean(); - } - - Allocation allocation; - allocation.host_ptr = host_base_ + write_head_; - allocation.gpu_ptr = gpu_base_ + write_head_; - allocation.offset = write_head_; - allocation.length = length; - allocation.aligned_length = aligned_length; - allocation.cache_key = 0; - write_head_ += aligned_length; - return allocation; -} - -bool CircularBuffer::AcquireCached(uint32_t key, size_t length, - Allocation* out_allocation) { - uint64_t full_key = key | (length << 32); - auto it = allocation_cache_.find(full_key); - if (it != allocation_cache_.end()) { - uintptr_t write_head = it->second; - size_t aligned_length = xe::round_up(length, alignment_); - out_allocation->host_ptr = host_base_ + write_head; - out_allocation->gpu_ptr = gpu_base_ + write_head; - out_allocation->offset = write_head; - out_allocation->length = length; - out_allocation->aligned_length = aligned_length; - out_allocation->cache_key = full_key; - return true; - } else { - *out_allocation = Acquire(length); - out_allocation->cache_key = full_key; - return false; - } -} - -void CircularBuffer::Discard(Allocation allocation) { - write_head_ -= allocation.aligned_length; -} - -void CircularBuffer::Commit(Allocation allocation) { - uintptr_t start = allocation.gpu_ptr - gpu_base_; - uintptr_t end = start + allocation.aligned_length; - dirty_start_ = std::min(dirty_start_, start); - dirty_end_ = std::max(dirty_end_, end); - assert_true(dirty_end_ <= capacity_); - if (allocation.cache_key) { - allocation_cache_.insert({allocation.cache_key, allocation.offset}); - } -} - -void CircularBuffer::Flush() { - if (dirty_start_ == dirty_end_ || dirty_start_ == UINT64_MAX) { - return; - } - glFlushMappedNamedBufferRange(buffer_, dirty_start_, - dirty_end_ - dirty_start_); - dirty_start_ = UINT64_MAX; - dirty_end_ = 0; -} - -void CircularBuffer::ClearCache() { allocation_cache_.clear(); } - -void CircularBuffer::WaitUntilClean() { - Flush(); - glFinish(); - write_head_ = 0; - ClearCache(); -} - -} // namespace gl -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/gl/circular_buffer.h b/src/xenia/ui/gl/circular_buffer.h deleted file mode 100644 index 3b9c119f8..000000000 --- a/src/xenia/ui/gl/circular_buffer.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GL_CIRCULAR_BUFFER_H_ -#define XENIA_UI_GL_CIRCULAR_BUFFER_H_ - -#include <unordered_map> - -#include "xenia/ui/gl/gl.h" - -namespace xe { -namespace ui { -namespace gl { - -// TODO(benvanik): uh, make this circular. -// TODO(benvanik): fences to prevent this from ever flushing. -class CircularBuffer { - public: - CircularBuffer(size_t capacity, size_t alignment = 256); - ~CircularBuffer(); - - struct Allocation { - void* host_ptr; - GLuint64 gpu_ptr; - size_t offset; - size_t length; - size_t aligned_length; - uint64_t cache_key; // 0 if caching disabled. - }; - - bool Initialize(); - void Shutdown(); - - GLuint handle() const { return buffer_; } - GLuint64 gpu_handle() const { return gpu_base_; } - size_t capacity() const { return capacity_; } - - bool CanAcquire(size_t length); - Allocation Acquire(size_t length); - bool AcquireCached(uint32_t key, size_t length, Allocation* out_allocation); - void Discard(Allocation allocation); - void Commit(Allocation allocation); - void Flush(); - void ClearCache(); - - void WaitUntilClean(); - - private: - size_t capacity_; - size_t alignment_; - uintptr_t write_head_; - uintptr_t dirty_start_; - uintptr_t dirty_end_; - GLuint buffer_; - GLuint64 gpu_base_; - uint8_t* host_base_; - - std::unordered_map<uint64_t, uintptr_t> allocation_cache_; -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_CIRCULAR_BUFFER_H_ diff --git a/src/xenia/ui/gl/gl.h b/src/xenia/ui/gl/gl.h deleted file mode 100644 index 796248803..000000000 --- a/src/xenia/ui/gl/gl.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GL_GL_H_ -#define XENIA_UI_GL_GL_H_ - -#include "xenia/base/platform.h" - -#include "third_party/GL/glew.h" - -typedef struct GLEWContextStruct GLEWContext; -extern "C" GLEWContext* glewGetContext(); - -#if XE_PLATFORM_WIN32 -// We avoid including wglew.h here as it includes windows.h and pollutes the -// global namespace. As we don't need wglew most places we only do that as -// required. -typedef struct WGLEWContextStruct WGLEWContext; -extern "C" WGLEWContext* wglewGetContext(); -#elif XE_PLATFORM_LINUX -typedef struct GLXEWContextStruct GLXEWContext; -extern "C" GLXEWContext* glxewGetContext(); - -#endif - -#endif // XENIA_UI_GL_GL_H_ diff --git a/src/xenia/ui/gl/gl_context.cc b/src/xenia/ui/gl/gl_context.cc deleted file mode 100644 index afae0de6e..000000000 --- a/src/xenia/ui/gl/gl_context.cc +++ /dev/null @@ -1,268 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/gl/gl_context.h" - -#include <gflags/gflags.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/gl/gl_immediate_drawer.h" -#include "xenia/ui/window.h" - -DEFINE_bool(thread_safe_gl, false, - "Only allow one GL context to be active at a time."); - -DEFINE_bool(disable_gl_context_reset, false, - "Do not aggressively reset the GL context (helps with capture " - "programs such as OBS or FRAPS)."); - -DEFINE_bool(random_clear_color, false, "Randomizes GL clear color."); - -DEFINE_bool(gl_debug, false, "Enable OpenGL debug validation layer."); -DEFINE_bool(gl_debug_output, false, "Dump ARB_debug_output to stderr."); -DEFINE_bool(gl_debug_output_synchronous, true, - "ARB_debug_output will synchronize to be thread safe."); - -namespace xe { -namespace ui { -namespace gl { - -std::recursive_mutex GLContext::global_gl_mutex_; - -void GLContext::FatalGLError(std::string error) { - xe::FatalError( - error + - "\nEnsure you have the latest drivers for your GPU and that it supports " - "OpenGL 4.5. See http://xenia.jp/faq/ for more information and a list" - "of supported GPUs."); -} - -GLContext::GLContext(GraphicsProvider* provider, Window* target_window) - : GraphicsContext(provider, target_window) {} - -GLContext::~GLContext() {} - -void GLContext::AssertExtensionsPresent() { - if (!MakeCurrent()) { - FatalGLError("Unable to make GL context current."); - return; - } - - // Check shader version at least 4.5 (matching GL 4.5). - auto glsl_version_raw = - reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)); - std::string glsl_version(glsl_version_raw); - if (glsl_version.find("4.5") == std::string::npos && - glsl_version.find("4.6") == std::string::npos) { - FatalGLError("OpenGL GLSL version 4.50 or higher is required."); - return; - } - - if (!GLEW_ARB_bindless_texture || !glMakeTextureHandleResidentARB) { - FatalGLError("OpenGL extension ARB_bindless_texture is required."); - return; - } - - if (!GLEW_ARB_fragment_coord_conventions) { - FatalGLError( - "OpenGL extension ARB_fragment_coord_conventions is required."); - return; - } - - ClearCurrent(); -} - -void GLContext::DebugMessage(GLenum source, GLenum type, GLuint id, - GLenum severity, GLsizei length, - const GLchar* message) { - const char* source_name = nullptr; - switch (source) { - case GL_DEBUG_SOURCE_API_ARB: - source_name = "OpenGL"; - break; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: - source_name = "Windows"; - break; - case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: - source_name = "Shader Compiler"; - break; - case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: - source_name = "Third Party"; - break; - case GL_DEBUG_SOURCE_APPLICATION_ARB: - source_name = "Application"; - break; - case GL_DEBUG_SOURCE_OTHER_ARB: - source_name = "Other"; - break; - default: - source_name = "(unknown source)"; - break; - } - - const char* type_name = nullptr; - switch (type) { - case GL_DEBUG_TYPE_ERROR: - type_name = "error"; - break; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: - type_name = "deprecated behavior"; - break; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: - type_name = "undefined behavior"; - break; - case GL_DEBUG_TYPE_PORTABILITY: - type_name = "portability"; - break; - case GL_DEBUG_TYPE_PERFORMANCE: - type_name = "performance"; - break; - case GL_DEBUG_TYPE_OTHER: - type_name = "message"; - break; - case GL_DEBUG_TYPE_MARKER: - type_name = "marker"; - break; - case GL_DEBUG_TYPE_PUSH_GROUP: - type_name = "push group"; - break; - case GL_DEBUG_TYPE_POP_GROUP: - type_name = "pop group"; - break; - default: - type_name = "(unknown type)"; - break; - } - - const char* severity_name = nullptr; - switch (severity) { - case GL_DEBUG_SEVERITY_HIGH_ARB: - severity_name = "high"; - break; - case GL_DEBUG_SEVERITY_MEDIUM_ARB: - severity_name = "medium"; - break; - case GL_DEBUG_SEVERITY_LOW_ARB: - severity_name = "low"; - break; - case GL_DEBUG_SEVERITY_NOTIFICATION: - severity_name = "notification"; - break; - default: - severity_name = "(unknown severity)"; - break; - } - - XELOGE("GL4 %s: %s(%s) %d: %s", source_name, type_name, severity_name, id, - message); -} - -void GLAPIENTRY GLContext::DebugMessageThunk(GLenum source, GLenum type, - GLuint id, GLenum severity, - GLsizei length, - const GLchar* message, - GLvoid* user_param) { - reinterpret_cast<GLContext*>(user_param) - ->DebugMessage(source, type, id, severity, length, message); -} - -void GLContext::SetupDebugging() { - if (!FLAGS_gl_debug || !FLAGS_gl_debug_output) { - return; - } - - glEnable(GL_DEBUG_OUTPUT); - - // Synchronous output hurts, but is required if we want to line up the logs. - if (FLAGS_gl_debug_output_synchronous) { - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - } else { - glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - } - - // Enable everything by default. - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, - GL_TRUE); - - // Disable annoying messages. - GLuint disable_message_ids[] = { - 0x00020004, // Usage warning: Generic vertex attribute array 0 uses a - // pointer with a small value (0x0000000000000000). Is this - // intended to be used as an offset into a buffer object? - }; - glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE, - GLsizei(xe::countof(disable_message_ids)), - disable_message_ids, GL_FALSE); - - // Callback will be made from driver threads. - glDebugMessageCallback(reinterpret_cast<GLDEBUGPROC>(&DebugMessageThunk), - this); -} - -ImmediateDrawer* GLContext::immediate_drawer() { - return immediate_drawer_.get(); -} - -bool GLContext::WasLost() { - if (!robust_access_supported_) { - // Can't determine if we lost the context. - return false; - } - - if (context_lost_) { - return true; - } - - auto status = glGetGraphicsResetStatusARB(); - if (status != GL_NO_ERROR) { - // Graphics card reset. - XELOGE("============= TDR detected on context %p! Context %s =============", - handle(), status == GL_GUILTY_CONTEXT_RESET ? "guilty" : "innocent"); - context_lost_ = true; - return true; - } - - return false; -} - -std::unique_ptr<RawImage> GLContext::Capture() { - GraphicsContextLock lock(this); - - std::unique_ptr<RawImage> raw_image(new RawImage()); - raw_image->width = target_window_->width(); - raw_image->stride = raw_image->width * 4; - raw_image->height = target_window_->height(); - raw_image->data.resize(raw_image->stride * raw_image->height); - - glReadPixels(0, 0, target_window_->width(), target_window_->height(), GL_RGBA, - GL_UNSIGNED_BYTE, raw_image->data.data()); - - // Flip vertically in-place. - size_t yt = 0; - size_t yb = (raw_image->height - 1) * raw_image->stride; - while (yt < yb) { - for (size_t i = 0; i < raw_image->stride; ++i) { - std::swap(raw_image->data[yt + i], raw_image->data[yb + i]); - } - yt += raw_image->stride; - yb -= raw_image->stride; - } - - return raw_image; -} - -} // namespace gl -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/gl/gl_context.h b/src/xenia/ui/gl/gl_context.h deleted file mode 100644 index d71e209b2..000000000 --- a/src/xenia/ui/gl/gl_context.h +++ /dev/null @@ -1,93 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GL_GL_CONTEXT_H_ -#define XENIA_UI_GL_GL_CONTEXT_H_ - -#include <gflags/gflags.h> - -#include <memory> -#include <mutex> - -#include "xenia/ui/gl/blitter.h" -#include "xenia/ui/gl/gl.h" -#include "xenia/ui/graphics_context.h" - -DECLARE_bool(thread_safe_gl); - -DECLARE_bool(disable_gl_context_reset); - -DECLARE_bool(random_clear_color); - -DECLARE_bool(gl_debug); -DECLARE_bool(gl_debug_output); -DECLARE_bool(gl_debug_output_synchronous); - -namespace xe { -namespace ui { -namespace gl { - -class GLImmediateDrawer; -class GLProvider; - -class GLContext : public GraphicsContext { - public: - ~GLContext() override; - - ImmediateDrawer* immediate_drawer() override; - - virtual bool is_current() override = 0; - virtual bool MakeCurrent() override = 0; - virtual void ClearCurrent() override = 0; - bool WasLost() override; - - virtual void BeginSwap() override = 0; - virtual void EndSwap() override = 0; - std::unique_ptr<RawImage> Capture() override; - - Blitter* blitter() { return &blitter_; } - - protected: - Blitter blitter_; - std::unique_ptr<GLImmediateDrawer> immediate_drawer_; - - static std::recursive_mutex global_gl_mutex_; - bool context_lost_ = false; - bool robust_access_supported_ = false; - static void FatalGLError(std::string error); - virtual bool Initialize(GLContext* share_context) = 0; - virtual void* handle() = 0; - GLContext(GraphicsProvider* provider, Window* target_window); - void SetupDebugging(); - void AssertExtensionsPresent(); - void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const GLchar* message); - - private: - friend class GLProvider; - - static std::unique_ptr<GLContext> Create(GraphicsProvider* provider, - Window* target_window, - GLContext* share_context = nullptr); - static std::unique_ptr<GLContext> CreateOffscreen(GraphicsProvider* provider, - GLContext* parent_context); - - private: - static void GLAPIENTRY DebugMessageThunk(GLenum source, GLenum type, - GLuint id, GLenum severity, - GLsizei length, - const GLchar* message, - GLvoid* user_param); -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_GL_CONTEXT_H_ diff --git a/src/xenia/ui/gl/gl_context_win.cc b/src/xenia/ui/gl/gl_context_win.cc deleted file mode 100644 index 0ca66146c..000000000 --- a/src/xenia/ui/gl/gl_context_win.cc +++ /dev/null @@ -1,315 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/gl/gl_context_win.h" - -#include <gflags/gflags.h> - -#include <mutex> -#include <string> - -#include "xenia/base/assert.h" -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/platform_win.h" -#include "xenia/base/profiling.h" -#include "xenia/ui/gl/gl_immediate_drawer.h" -#include "xenia/ui/window.h" - -#include "third_party/GL/wglew.h" - -namespace xe { -namespace ui { -namespace gl { - -thread_local GLEWContext* tls_glew_context_ = nullptr; -thread_local WGLEWContext* tls_wglew_context_ = nullptr; -extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; } -extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; } - -std::unique_ptr<GLContext> GLContext::Create(GraphicsProvider* provider, - Window* target_window, - GLContext* share_context) { - auto context = - std::unique_ptr<GLContext>(new WGLContext(provider, target_window)); - if (!context->Initialize(share_context)) { - return nullptr; - } - context->AssertExtensionsPresent(); - return context; -} - -std::unique_ptr<GLContext> GLContext::CreateOffscreen( - GraphicsProvider* provider, GLContext* parent_context) { - return WGLContext::CreateOffscreen(provider, - static_cast<WGLContext*>(parent_context)); -} - -WGLContext::WGLContext(GraphicsProvider* provider, Window* target_window) - : GLContext(provider, target_window) { - glew_context_.reset(new GLEWContext()); - wglew_context_.reset(new WGLEWContext()); -} - -WGLContext::~WGLContext() { - MakeCurrent(); - blitter_.Shutdown(); - immediate_drawer_.reset(); - ClearCurrent(); - if (glrc_) { - wglDeleteContext(glrc_); - } - if (dc_) { - ReleaseDC(HWND(target_window_->native_handle()), dc_); - } -} - -bool WGLContext::Initialize(GLContext* share_context_) { - WGLContext* share_context = static_cast<WGLContext*>(share_context_); - dc_ = GetDC(HWND(target_window_->native_handle())); - - PIXELFORMATDESCRIPTOR pfd = {0}; - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 32; - pfd.cDepthBits = 32; - pfd.iLayerType = PFD_MAIN_PLANE; - int pixel_format = ChoosePixelFormat(dc_, &pfd); - if (!pixel_format) { - FatalGLError("Unable to choose pixel format."); - return false; - } - if (!SetPixelFormat(dc_, pixel_format, &pfd)) { - FatalGLError("Unable to set pixel format."); - return false; - } - - HGLRC temp_context = wglCreateContext(dc_); - if (!temp_context) { - FatalGLError("Unable to create temporary GL context."); - return false; - } - wglMakeCurrent(dc_, temp_context); - - tls_glew_context_ = glew_context_.get(); - tls_wglew_context_ = wglew_context_.get(); - if (glewInit() != GLEW_OK) { - FatalGLError("Unable to initialize GLEW."); - return false; - } - if (wglewInit() != GLEW_OK) { - FatalGLError("Unable to initialize WGLEW."); - return false; - } - - if (!WGLEW_ARB_create_context) { - FatalGLError("WGL_ARG_create_context not supported by GL ICD."); - return false; - } - - if (GLEW_ARB_robustness) { - robust_access_supported_ = true; - } - - int context_flags = 0; - if (FLAGS_gl_debug) { - context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; - } - if (robust_access_supported_) { - context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; - } - - int attrib_list[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, - 4, - WGL_CONTEXT_MINOR_VERSION_ARB, - 5, - WGL_CONTEXT_FLAGS_ARB, - context_flags, - WGL_CONTEXT_PROFILE_MASK_ARB, - WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - robust_access_supported_ ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0, - 0}; - - glrc_ = wglCreateContextAttribsARB( - dc_, share_context ? share_context->glrc_ : nullptr, attrib_list); - wglMakeCurrent(nullptr, nullptr); - wglDeleteContext(temp_context); - if (!glrc_) { - FatalGLError("Unable to create real GL context."); - return false; - } - - if (!MakeCurrent()) { - FatalGLError("Could not make real GL context current."); - return false; - } - - XELOGI("Successfully created OpenGL context:"); - XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR)); - XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION)); - XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER)); - XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s", - glGetString(GL_SHADING_LANGUAGE_VERSION)); - - while (glGetError()) { - // Clearing errors. - } - - SetupDebugging(); - - if (!blitter_.Initialize()) { - FatalGLError("Unable to initialize blitter."); - ClearCurrent(); - return false; - } - - immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this); - - ClearCurrent(); - - return true; -} - -std::unique_ptr<WGLContext> WGLContext::CreateOffscreen( - GraphicsProvider* provider, WGLContext* parent_context) { - assert_not_null(parent_context->glrc_); - - HGLRC new_glrc = nullptr; - { - GraphicsContextLock context_lock(parent_context); - - int context_flags = 0; - if (FLAGS_gl_debug) { - context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; - } - - bool robust_access_supported = parent_context->robust_access_supported_; - if (robust_access_supported) { - context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; - } - - int attrib_list[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, - 4, - WGL_CONTEXT_MINOR_VERSION_ARB, - 5, - WGL_CONTEXT_FLAGS_ARB, - context_flags, - WGL_CONTEXT_PROFILE_MASK_ARB, - WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - robust_access_supported ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0, - 0}; - new_glrc = wglCreateContextAttribsARB(parent_context->dc_, - parent_context->glrc_, attrib_list); - if (!new_glrc) { - FatalGLError("Could not create shared context."); - return nullptr; - } - } - - auto new_context = std::unique_ptr<WGLContext>( - new WGLContext(provider, parent_context->target_window_)); - new_context->glrc_ = new_glrc; - new_context->dc_ = - GetDC(HWND(parent_context->target_window_->native_handle())); - new_context->robust_access_supported_ = - parent_context->robust_access_supported_; - if (!new_context->MakeCurrent()) { - FatalGLError("Could not make new GL context current."); - return nullptr; - } - if (!glGetString(GL_EXTENSIONS)) { - new_context->ClearCurrent(); - FatalGLError("New GL context did not have extensions."); - return nullptr; - } - - if (glewInit() != GLEW_OK) { - new_context->ClearCurrent(); - FatalGLError("Unable to initialize GLEW on shared context."); - return nullptr; - } - if (wglewInit() != GLEW_OK) { - new_context->ClearCurrent(); - FatalGLError("Unable to initialize WGLEW on shared context."); - return nullptr; - } - - new_context->SetupDebugging(); - - if (!new_context->blitter_.Initialize()) { - FatalGLError("Unable to initialize blitter on shared context."); - return nullptr; - } - - new_context->ClearCurrent(); - - return new_context; -} - -bool WGLContext::is_current() { - return tls_glew_context_ == glew_context_.get(); -} - -bool WGLContext::MakeCurrent() { - SCOPE_profile_cpu_f("gpu"); - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.lock(); - } - - if (!wglMakeCurrent(dc_, glrc_)) { - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.unlock(); - } - FatalGLError("Unable to make GL context current."); - return false; - } - tls_glew_context_ = glew_context_.get(); - tls_wglew_context_ = wglew_context_.get(); - return true; -} - -void WGLContext::ClearCurrent() { - if (!FLAGS_disable_gl_context_reset) { - wglMakeCurrent(nullptr, nullptr); - } - tls_glew_context_ = nullptr; - tls_wglew_context_ = nullptr; - - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.unlock(); - } -} - -void WGLContext::BeginSwap() { - SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLContext::BeginSwap"); - float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f}; - if (FLAGS_random_clear_color) { - clear_color[0] = - rand() / static_cast<float>(RAND_MAX); // NOLINT(runtime/threadsafe_fn) - clear_color[1] = 1.0f; - clear_color[2] = 0.0f; - clear_color[3] = 1.0f; - } - glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color); -} - -void WGLContext::EndSwap() { - SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLContext::EndSwap"); - SwapBuffers(dc_); -} - -} // namespace gl -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/gl/gl_context_win.h b/src/xenia/ui/gl/gl_context_win.h deleted file mode 100644 index 772de4d3e..000000000 --- a/src/xenia/ui/gl/gl_context_win.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GL_WGL_CONTEXT_H_ -#define XENIA_UI_GL_WGL_CONTEXT_H_ - -#include <gflags/gflags.h> - -#include <memory> - -#include "xenia/ui/gl/blitter.h" -#include "xenia/ui/gl/gl.h" -#include "xenia/ui/gl/gl_context.h" -#include "xenia/ui/graphics_context.h" - -typedef struct HDC__* HDC; -typedef struct HGLRC__* HGLRC; - -namespace xe { -namespace ui { -namespace gl { - -class GLImmediateDrawer; -class GLProvider; - -class WGLContext : public GLContext { - public: - ~WGLContext() override; - - bool is_current() override; - bool MakeCurrent() override; - void ClearCurrent() override; - - void BeginSwap() override; - void EndSwap() override; - - protected: - friend class GLContext; - WGLContext(GraphicsProvider* provider, Window* target_window); - static std::unique_ptr<WGLContext> CreateOffscreen( - GraphicsProvider* provider, WGLContext* parent_context); - - bool Initialize(GLContext* share_context) override; - void* handle() override { return glrc_; } - - private: - HDC dc_ = nullptr; - HGLRC glrc_ = nullptr; - - std::unique_ptr<GLEWContext> glew_context_; - std::unique_ptr<WGLEWContext> wglew_context_; -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_GL_CONTEXT_H_ diff --git a/src/xenia/ui/gl/gl_context_x11.cc b/src/xenia/ui/gl/gl_context_x11.cc deleted file mode 100644 index fc4b3176b..000000000 --- a/src/xenia/ui/gl/gl_context_x11.cc +++ /dev/null @@ -1,323 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/gl/gl_context_x11.h" - -#include <gflags/gflags.h> - -#include <gdk/gdkx.h> -#include <mutex> -#include <string> - -#include "third_party/GL/glxew.h" -#include "xenia/base/assert.h" -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/platform_linux.h" -#include "xenia/base/profiling.h" -#include "xenia/ui/gl/gl_immediate_drawer.h" -#include "xenia/ui/window.h" - -namespace xe { -namespace ui { -namespace gl { - -thread_local GLEWContext* tls_glew_context_ = nullptr; -thread_local GLXEWContext* tls_glxew_context_ = nullptr; -extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; } -extern "C" GLXEWContext* glxewGetContext() { return tls_glxew_context_; } - -std::unique_ptr<GLContext> GLContext::Create(GraphicsProvider* provider, - Window* target_window, - GLContext* share_context) { - auto context = - std::unique_ptr<GLContext>(new GLXContext(provider, target_window)); - if (!context->Initialize(share_context)) { - return nullptr; - } - context->AssertExtensionsPresent(); - return context; -} - -std::unique_ptr<GLContext> GLContext::CreateOffscreen( - GraphicsProvider* provider, GLContext* parent_context) { - return GLXContext::CreateOffscreen(provider, - static_cast<GLXContext*>(parent_context)); -} - -GLXContext::GLXContext(GraphicsProvider* provider, Window* target_window) - : GLContext(provider, target_window) { - glew_context_.reset(new GLEWContext()); - glxew_context_.reset(new GLXEWContext()); -} - -GLXContext::~GLXContext() { - MakeCurrent(); - blitter_.Shutdown(); - immediate_drawer_.reset(); - ClearCurrent(); - if (glx_context_) { - glXDestroyContext(disp_, glx_context_); - } - if (draw_area_) { - gtk_widget_destroy(draw_area_); - } -} - -bool GLXContext::Initialize(GLContext* share_context) { - GtkWidget* window = GTK_WIDGET(target_window_->native_handle()); - GtkWidget* draw_area = gtk_drawing_area_new(); - int32_t width; - int32_t height; - gtk_window_get_size(GTK_WINDOW(window), &width, &height); - gtk_widget_set_size_request(draw_area, width, height); - gtk_container_add(GTK_CONTAINER(window), draw_area); - GdkVisual* visual = gdk_screen_get_system_visual(gdk_screen_get_default()); - - GdkDisplay* gdk_display = gtk_widget_get_display(window); - Display* display = gdk_x11_display_get_xdisplay(gdk_display); - disp_ = display; - ::Window root = gdk_x11_get_default_root_xwindow(); - static int vis_attrib_list[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, - GLX_DOUBLEBUFFER, None}; - XVisualInfo* vi = glXChooseVisual(display, 0, vis_attrib_list); - if (vi == NULL) { - FatalGLError("No matching visuals for X display"); - return false; - } - - cmap_ = XCreateColormap(display, root, vi->visual, AllocNone); - - ::GLXContext temp_context = glXCreateContext(display, vi, NULL, GL_TRUE); - if (!temp_context) { - FatalGLError("Unable to create temporary GLX context"); - return false; - } - xid_ = GDK_WINDOW_XID(gtk_widget_get_window(window)); - glXMakeCurrent(display, xid_, temp_context); - - tls_glew_context_ = glew_context_.get(); - tls_glxew_context_ = glxew_context_.get(); - if (glewInit() != GLEW_OK) { - FatalGLError("Unable to initialize GLEW."); - return false; - } - if (glxewInit() != GLEW_OK) { - FatalGLError("Unable to initialize GLXEW."); - return false; - } - - if (!GLXEW_ARB_create_context) { - FatalGLError("GLX_ARB_create_context not supported by GL ICD."); - return false; - } - - if (GLEW_ARB_robustness) { - robust_access_supported_ = true; - } - - int context_flags = 0; - if (FLAGS_gl_debug) { - context_flags |= GLX_CONTEXT_DEBUG_BIT_ARB; - } - if (robust_access_supported_) { - context_flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; - } - - int attrib_list[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, - 4, - GLX_CONTEXT_MINOR_VERSION_ARB, - 5, - GLX_CONTEXT_FLAGS_ARB, - context_flags, - GLX_CONTEXT_PROFILE_MASK_ARB, - GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - robust_access_supported_ ? GLX_LOSE_CONTEXT_ON_RESET_ARB : 0, - 0}; - GLXContext* share_context_glx = static_cast<GLXContext*>(share_context); - glx_context_ = glXCreateContextAttribsARB( - display, nullptr, - share_context ? share_context_glx->glx_context_ : nullptr, True, - attrib_list); - glXMakeCurrent(display, 0, nullptr); - glXDestroyContext(display, temp_context); - if (!glx_context_) { - FatalGLError("Unable to create real GL context."); - return false; - } - - if (!MakeCurrent()) { - FatalGLError("Could not make real GL context current."); - return false; - } - - XELOGI("Successfully created OpenGL context:"); - XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR)); - XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION)); - XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER)); - XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s", - glGetString(GL_SHADING_LANGUAGE_VERSION)); - - while (glGetError()) { - // Clearing errors. - } - - SetupDebugging(); - - if (!blitter_.Initialize()) { - FatalGLError("Unable to initialize blitter."); - ClearCurrent(); - return false; - } - - immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this); - - ClearCurrent(); - - return true; -} - -std::unique_ptr<GLXContext> GLXContext::CreateOffscreen( - GraphicsProvider* provider, GLXContext* parent_context) { - assert_not_null(parent_context->glx_context_); - - ::GLXContext new_glrc; - { - GraphicsContextLock context_lock(parent_context); - - int context_flags = 0; - if (FLAGS_gl_debug) { - context_flags |= GLX_CONTEXT_DEBUG_BIT_ARB; - } - - bool robust_access_supported = parent_context->robust_access_supported_; - if (robust_access_supported) { - context_flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; - } - - int attrib_list[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, - 4, - GLX_CONTEXT_MINOR_VERSION_ARB, - 5, - GLX_CONTEXT_FLAGS_ARB, - context_flags, - GLX_CONTEXT_PROFILE_MASK_ARB, - GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - robust_access_supported ? GLX_LOSE_CONTEXT_ON_RESET_ARB : 0, - 0}; - new_glrc = glXCreateContextAttribsARB(parent_context->disp_, nullptr, - parent_context->glx_context_, True, - attrib_list); - if (!new_glrc) { - FatalGLError("Could not create shared context."); - return nullptr; - } - } - - auto new_context = std::unique_ptr<GLXContext>( - new GLXContext(provider, parent_context->target_window_)); - new_context->glx_context_ = new_glrc; - new_context->window_ = parent_context->window_; - new_context->draw_area_ = parent_context->draw_area_; - new_context->disp_ = parent_context->disp_; - new_context->xid_ = parent_context->xid_; - new_context->robust_access_supported_ = - parent_context->robust_access_supported_; - if (!new_context->MakeCurrent()) { - FatalGLError("Could not make new GL context current."); - return nullptr; - } - if (!glGetString(GL_EXTENSIONS)) { - new_context->ClearCurrent(); - FatalGLError("New GL context did not have extensions."); - return nullptr; - } - - if (glewInit() != GLEW_OK) { - new_context->ClearCurrent(); - FatalGLError("Unable to initialize GLEW on shared context."); - return nullptr; - } - if (glxewInit() != GLEW_OK) { - new_context->ClearCurrent(); - FatalGLError("Unable to initialize GLXEW on shared context."); - return nullptr; - } - - new_context->SetupDebugging(); - - if (!new_context->blitter_.Initialize()) { - FatalGLError("Unable to initialize blitter on shared context."); - return nullptr; - } - - new_context->ClearCurrent(); - - return new_context; -} - -bool GLXContext::is_current() { - return tls_glew_context_ == glew_context_.get(); -} - -bool GLXContext::MakeCurrent() { - SCOPE_profile_cpu_f("gpu"); - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.lock(); - } - - if (!glXMakeCurrent(disp_, xid_, glx_context_)) { - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.unlock(); - } - FatalGLError("Unable to make GL context current."); - return false; - } - tls_glew_context_ = glew_context_.get(); - tls_glxew_context_ = glxew_context_.get(); - return true; -} - -void GLXContext::ClearCurrent() { - if (!FLAGS_disable_gl_context_reset) { - glXMakeCurrent(disp_, 0, nullptr); - } - tls_glew_context_ = nullptr; - tls_glxew_context_ = nullptr; - - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.unlock(); - } -} - -void GLXContext::BeginSwap() { - SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLXContext::BeginSwap"); - float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f}; - if (FLAGS_random_clear_color) { - clear_color[0] = - rand() / static_cast<float>(RAND_MAX); // NOLINT(runtime/threadsafe_fn) - clear_color[1] = 1.0f; - clear_color[2] = 0.0f; - clear_color[3] = 1.0f; - } - glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color); -} - -void GLXContext::EndSwap() { - SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLXContext::EndSwap"); - glXSwapBuffers(disp_, xid_); -} - -} // namespace gl -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/gl/gl_context_x11.h b/src/xenia/ui/gl/gl_context_x11.h deleted file mode 100644 index 6eea7fdb7..000000000 --- a/src/xenia/ui/gl/gl_context_x11.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GL_GLX_CONTEXT_H_ -#define XENIA_UI_GL_GLX_CONTEXT_H_ - -#include <gflags/gflags.h> - -#include <memory> - -#include "third_party/GL/glxew.h" -#include "xenia/base/platform_linux.h" -#include "xenia/ui/gl/blitter.h" -#include "xenia/ui/gl/gl.h" -#include "xenia/ui/gl/gl_context.h" -#include "xenia/ui/graphics_context.h" - -DECLARE_bool(thread_safe_gl); - -namespace xe { -namespace ui { -namespace gl { - -class GLImmediateDrawer; -class GLProvider; - -class GLXContext : public GLContext { - public: - ~GLXContext() override; - - bool is_current() override; - - bool MakeCurrent() override; - void ClearCurrent() override; - - void BeginSwap() override; - void EndSwap() override; - - protected: - static std::unique_ptr<GLXContext> CreateOffscreen( - GraphicsProvider* provider, GLXContext* parent_context); - - bool Initialize(GLContext* share_context) override; - void* handle() override { return glx_context_; } - - private: - friend class GLContext; - GLXContext(GraphicsProvider* provider, Window* target_window); - std::unique_ptr<GLEWContext> glew_context_; - std::unique_ptr<GLXEWContext> glxew_context_; - ::GLXContext glx_context_; - GtkWidget* window_; - GtkWidget* draw_area_; - Colormap cmap_; - Display* disp_; - int xid_; -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_GL_CONTEXT_H_ diff --git a/src/xenia/ui/gl/gl_immediate_drawer.cc b/src/xenia/ui/gl/gl_immediate_drawer.cc deleted file mode 100644 index 998ff7c50..000000000 --- a/src/xenia/ui/gl/gl_immediate_drawer.cc +++ /dev/null @@ -1,283 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/gl/gl_immediate_drawer.h" - -#include <string> - -#include "xenia/base/assert.h" -#include "xenia/ui/graphics_context.h" - -namespace xe { -namespace ui { -namespace gl { - -constexpr uint32_t kMaxDrawVertices = 64 * 1024; -constexpr uint32_t kMaxDrawIndices = 64 * 1024; - -class GLImmediateTexture : public ImmediateTexture { - public: - GLImmediateTexture(uint32_t width, uint32_t height, - ImmediateTextureFilter filter, bool repeat) - : ImmediateTexture(width, height) { - GLuint gl_handle; - glCreateTextures(GL_TEXTURE_2D, 1, &gl_handle); - - GLenum gl_filter = GL_NEAREST; - switch (filter) { - case ImmediateTextureFilter::kNearest: - gl_filter = GL_NEAREST; - break; - case ImmediateTextureFilter::kLinear: - gl_filter = GL_LINEAR; - break; - } - glTextureParameteri(gl_handle, GL_TEXTURE_MIN_FILTER, gl_filter); - glTextureParameteri(gl_handle, GL_TEXTURE_MAG_FILTER, gl_filter); - - glTextureParameteri(gl_handle, GL_TEXTURE_WRAP_S, - repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); - glTextureParameteri(gl_handle, GL_TEXTURE_WRAP_T, - repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); - - glTextureStorage2D(gl_handle, 1, GL_RGBA8, width, height); - - handle = static_cast<uintptr_t>(gl_handle); - } - - ~GLImmediateTexture() override { - GLuint gl_handle = static_cast<GLuint>(handle); - glDeleteTextures(1, &gl_handle); - } -}; - -GLImmediateDrawer::GLImmediateDrawer(GraphicsContext* graphics_context) - : ImmediateDrawer(graphics_context) { - glCreateBuffers(1, &vertex_buffer_); - glNamedBufferStorage(vertex_buffer_, - kMaxDrawVertices * sizeof(ImmediateVertex), nullptr, - GL_DYNAMIC_STORAGE_BIT); - glCreateBuffers(1, &index_buffer_); - glNamedBufferStorage(index_buffer_, kMaxDrawIndices * sizeof(uint16_t), - nullptr, GL_DYNAMIC_STORAGE_BIT); - - glCreateVertexArrays(1, &vao_); - glEnableVertexArrayAttrib(vao_, 0); - glVertexArrayAttribBinding(vao_, 0, 0); - glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE, - offsetof(ImmediateVertex, x)); - glEnableVertexArrayAttrib(vao_, 1); - glVertexArrayAttribBinding(vao_, 1, 0); - glVertexArrayAttribFormat(vao_, 1, 2, GL_FLOAT, GL_FALSE, - offsetof(ImmediateVertex, u)); - glEnableVertexArrayAttrib(vao_, 2); - glVertexArrayAttribBinding(vao_, 2, 0); - glVertexArrayAttribFormat(vao_, 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, - offsetof(ImmediateVertex, color)); - glVertexArrayVertexBuffer(vao_, 0, vertex_buffer_, 0, - sizeof(ImmediateVertex)); - - InitializeShaders(); -} - -GLImmediateDrawer::~GLImmediateDrawer() { - GraphicsContextLock lock(graphics_context_); - glDeleteBuffers(1, &vertex_buffer_); - glDeleteBuffers(1, &index_buffer_); - glDeleteVertexArrays(1, &vao_); - glDeleteProgram(program_); -} - -void GLImmediateDrawer::InitializeShaders() { - const std::string header = - R"( -#version 450 -#extension GL_ARB_explicit_uniform_location : require -#extension GL_ARB_shading_language_420pack : require -precision highp float; -layout(std140, column_major) uniform; -layout(std430, column_major) buffer; -)"; - const std::string vertex_shader_source = header + - R"( -layout(location = 0) uniform mat4 projection_matrix; -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 = projection_matrix * vec4(in_pos.xy, 0.0, 1.0); - vtx_uv = in_uv; - vtx_color = in_color; -})"; - const std::string fragment_shader_source = header + - R"( -layout(location = 1) uniform sampler2D texture_sampler; -layout(location = 2) uniform int restrict_texture_samples; -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 (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. - } -})"; - - GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); - const char* vertex_shader_source_ptr = vertex_shader_source.c_str(); - GLint vertex_shader_source_length = GLint(vertex_shader_source.size()); - glShaderSource(vertex_shader, 1, &vertex_shader_source_ptr, - &vertex_shader_source_length); - glCompileShader(vertex_shader); - - GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); - const char* fragment_shader_source_ptr = fragment_shader_source.c_str(); - GLint fragment_shader_source_length = GLint(fragment_shader_source.size()); - glShaderSource(fragment_shader, 1, &fragment_shader_source_ptr, - &fragment_shader_source_length); - glCompileShader(fragment_shader); - - program_ = glCreateProgram(); - glAttachShader(program_, vertex_shader); - glAttachShader(program_, fragment_shader); - glLinkProgram(program_); - glDeleteShader(vertex_shader); - glDeleteShader(fragment_shader); -} - -std::unique_ptr<ImmediateTexture> GLImmediateDrawer::CreateTexture( - uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat, - const uint8_t* data) { - GraphicsContextLock lock(graphics_context_); - auto texture = - std::make_unique<GLImmediateTexture>(width, height, filter, repeat); - if (data) { - UpdateTexture(texture.get(), data); - } - return std::unique_ptr<ImmediateTexture>(texture.release()); -} - -void GLImmediateDrawer::UpdateTexture(ImmediateTexture* texture, - const uint8_t* data) { - GraphicsContextLock lock(graphics_context_); - glTextureSubImage2D(static_cast<GLuint>(texture->handle), 0, 0, 0, - texture->width, texture->height, GL_RGBA, - GL_UNSIGNED_BYTE, data); -} - -void GLImmediateDrawer::Begin(int render_target_width, - int render_target_height) { - was_current_ = graphics_context_->is_current(); - if (!was_current_) { - graphics_context_->MakeCurrent(); - } - - // Setup render state. - glEnablei(GL_BLEND, 0); - glBlendEquationi(0, GL_FUNC_ADD); - glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - - // Prepare drawing resources. - glUseProgram(program_); - glBindVertexArray(vao_); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_); - - // Setup orthographic projection matrix and viewport. - const float ortho_projection[4][4] = { - {2.0f / render_target_width, 0.0f, 0.0f, 0.0f}, - {0.0f, 2.0f / -render_target_height, 0.0f, 0.0f}, - {0.0f, 0.0f, -1.0f, 0.0f}, - {-1.0f, 1.0f, 0.0f, 1.0f}, - }; - glProgramUniformMatrix4fv(program_, 0, 1, GL_FALSE, &ortho_projection[0][0]); - glViewport(0, 0, render_target_width, render_target_height); -} - -void GLImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) { - assert_true(batch.vertex_count <= kMaxDrawVertices); - glNamedBufferSubData(vertex_buffer_, 0, - batch.vertex_count * sizeof(ImmediateVertex), - batch.vertices); - if (batch.indices) { - assert_true(batch.index_count <= kMaxDrawIndices); - glNamedBufferSubData(index_buffer_, 0, batch.index_count * sizeof(uint16_t), - batch.indices); - } - - batch_has_index_buffer_ = !!batch.indices; -} - -void GLImmediateDrawer::Draw(const ImmediateDraw& draw) { - if (draw.scissor) { - glEnable(GL_SCISSOR_TEST); - glScissorIndexed(0, draw.scissor_rect[0], draw.scissor_rect[1], - draw.scissor_rect[2], draw.scissor_rect[3]); - } else { - glDisable(GL_SCISSOR_TEST); - } - - if (draw.alpha_blend) { - glEnablei(GL_BLEND, 0); - glBlendEquationi(0, GL_FUNC_ADD); - glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } else { - glDisablei(GL_BLEND, 0); - } - - if (draw.texture_handle) { - glBindTextureUnit(0, static_cast<GLuint>(draw.texture_handle)); - } else { - glBindTextureUnit(0, 0); - } - glProgramUniform1i(program_, 2, draw.restrict_texture_samples ? 1 : 0); - - GLenum mode = GL_TRIANGLES; - switch (draw.primitive_type) { - case ImmediatePrimitiveType::kLines: - mode = GL_LINES; - break; - case ImmediatePrimitiveType::kTriangles: - mode = GL_TRIANGLES; - break; - } - - if (batch_has_index_buffer_) { - glDrawElementsBaseVertex( - mode, draw.count, GL_UNSIGNED_SHORT, - reinterpret_cast<void*>(draw.index_offset * sizeof(uint16_t)), - draw.base_vertex); - } else { - glDrawArrays(mode, draw.base_vertex, draw.count); - } -} - -void GLImmediateDrawer::EndDrawBatch() { glFlush(); } - -void GLImmediateDrawer::End() { - // Restore modified state. - glDisable(GL_SCISSOR_TEST); - glBindTextureUnit(0, 0); - glUseProgram(0); - glBindVertexArray(0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - if (!was_current_) { - graphics_context_->ClearCurrent(); - } -} - -} // namespace gl -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/gl/gl_immediate_drawer.h b/src/xenia/ui/gl/gl_immediate_drawer.h deleted file mode 100644 index e9e02798e..000000000 --- a/src/xenia/ui/gl/gl_immediate_drawer.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_ -#define XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_ - -#include <memory> - -#include "xenia/ui/gl/gl.h" -#include "xenia/ui/immediate_drawer.h" - -namespace xe { -namespace ui { -namespace gl { - -class GLImmediateDrawer : public ImmediateDrawer { - public: - GLImmediateDrawer(GraphicsContext* graphics_context); - ~GLImmediateDrawer() override; - - std::unique_ptr<ImmediateTexture> CreateTexture(uint32_t width, - uint32_t height, - ImmediateTextureFilter filter, - bool repeat, - const uint8_t* data) override; - void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override; - - void Begin(int render_target_width, int render_target_height) override; - void BeginDrawBatch(const ImmediateDrawBatch& batch) override; - void Draw(const ImmediateDraw& draw) override; - void EndDrawBatch() override; - void End() override; - - private: - void InitializeShaders(); - - GLuint program_ = 0; - GLuint vao_ = 0; - GLuint vertex_buffer_ = 0; - GLuint index_buffer_ = 0; - - bool was_current_ = false; - bool batch_has_index_buffer_ = false; -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_ diff --git a/src/xenia/ui/gl/gl_provider.cc b/src/xenia/ui/gl/gl_provider.cc deleted file mode 100644 index b53bc9696..000000000 --- a/src/xenia/ui/gl/gl_provider.cc +++ /dev/null @@ -1,49 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/gl/gl_provider.h" - -#include "xenia/ui/gl/gl_context.h" -#include "xenia/ui/window.h" - -namespace xe { -namespace ui { -namespace gl { - -std::unique_ptr<GraphicsProvider> GLProvider::Create(Window* main_window) { - std::unique_ptr<GLProvider> provider(new GLProvider(main_window)); - - // - - return std::unique_ptr<GraphicsProvider>(provider.release()); -} - -GLProvider::GLProvider(Window* main_window) : GraphicsProvider(main_window) {} - -GLProvider::~GLProvider() = default; - -std::unique_ptr<GraphicsContext> GLProvider::CreateContext( - Window* target_window) { - auto share_context = main_window_->context(); - return std::unique_ptr<GraphicsContext>( - GLContext::Create(this, target_window, - static_cast<GLContext*>(share_context)) - .release()); -} - -std::unique_ptr<GraphicsContext> GLProvider::CreateOffscreenContext() { - auto share_context = main_window_->context(); - return std::unique_ptr<GraphicsContext>( - GLContext::CreateOffscreen(this, static_cast<GLContext*>(share_context)) - .release()); -} - -} // namespace gl -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/gl/gl_provider.h b/src/xenia/ui/gl/gl_provider.h deleted file mode 100644 index 5c3303f32..000000000 --- a/src/xenia/ui/gl/gl_provider.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_GL_GL_PROVIDER_H_ -#define XENIA_UI_GL_GL_PROVIDER_H_ - -#include <memory> - -#include "xenia/ui/graphics_provider.h" - -namespace xe { -namespace ui { -namespace gl { - -class GLProvider : public GraphicsProvider { - public: - ~GLProvider() override; - - static std::unique_ptr<GraphicsProvider> Create(Window* main_window); - - std::unique_ptr<GraphicsContext> CreateContext( - Window* target_window) override; - - std::unique_ptr<GraphicsContext> CreateOffscreenContext() override; - - protected: - explicit GLProvider(Window* main_window); -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_GL_PROVIDER_H_ diff --git a/src/xenia/ui/gl/gl_window_demo.cc b/src/xenia/ui/gl/gl_window_demo.cc deleted file mode 100644 index 6b39ab40f..000000000 --- a/src/xenia/ui/gl/gl_window_demo.cc +++ /dev/null @@ -1,30 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include <string> -#include <vector> - -#include "xenia/base/main.h" -#include "xenia/ui/gl/gl_provider.h" -#include "xenia/ui/window.h" - -namespace xe { -namespace ui { - -int window_demo_main(const std::vector<std::wstring>& args); - -std::unique_ptr<GraphicsProvider> CreateDemoGraphicsProvider(Window* window) { - return xe::ui::gl::GLProvider::Create(window); -} - -} // namespace ui -} // namespace xe - -DEFINE_ENTRY_POINT(L"xenia-ui-window-gl-demo", L"xenia-ui-window-gl-demo", - xe::ui::window_demo_main); diff --git a/src/xenia/ui/gl/premake5.lua b/src/xenia/ui/gl/premake5.lua deleted file mode 100644 index 430640c70..000000000 --- a/src/xenia/ui/gl/premake5.lua +++ /dev/null @@ -1,56 +0,0 @@ -project_root = "../../../.." -include(project_root.."/tools/build") - -group("src") -project("xenia-ui-gl") - uuid("623300e3-0085-4ccc-af46-d60e88cb43aa") - kind("StaticLib") - language("C++") - links({ - "glew", - "xenia-base", - "xenia-ui", - }) - defines({ - "GLEW_STATIC=1", - "GLEW_MX=1", - }) - includedirs({ - project_root.."/third_party/gflags/src", - }) - local_platform_files() - removefiles({"*_demo.cc"}) - -group("demos") -project("xenia-ui-window-gl-demo") - uuid("e0a687e5-d1f4-4c18-b2f7-012c53ec1ee4") - kind("WindowedApp") - language("C++") - links({ - "gflags", - "glew", - "imgui", - "xenia-base", - "xenia-ui", - "xenia-ui-gl", - }) - flags({ - "WinMain", -- Use WinMain instead of main. - }) - defines({ - "GLEW_STATIC=1", - "GLEW_MX=1", - }) - includedirs({ - project_root.."/third_party/gflags/src", - }) - files({ - "../window_demo.cc", - "gl_window_demo.cc", - project_root.."/src/xenia/base/main_"..platform_suffix..".cc", - }) - files({ - }) - resincludedirs({ - project_root, - }) diff --git a/src/xenia/ui/vulkan/premake5.lua b/src/xenia/ui/vulkan/premake5.lua index 71824e5ec..51fd69aa5 100644 --- a/src/xenia/ui/vulkan/premake5.lua +++ b/src/xenia/ui/vulkan/premake5.lua @@ -31,12 +31,22 @@ project("xenia-ui-window-vulkan-demo") links({ "gflags", "imgui", - "vulkan-loader", + "volk", "xenia-base", "xenia-ui", "xenia-ui-spirv", "xenia-ui-vulkan", }) + filter("platforms:Linux") + links({ + "X11", + "xcb", + "X11-xcb", + "GL", + "vulkan", + }) + filter() + flags({ "WinMain", -- Use WinMain instead of main. }) diff --git a/src/xenia/ui/vulkan/vulkan.h b/src/xenia/ui/vulkan/vulkan.h index 38a4400f7..7d4887418 100644 --- a/src/xenia/ui/vulkan/vulkan.h +++ b/src/xenia/ui/vulkan/vulkan.h @@ -22,13 +22,10 @@ #error Platform not yet supported. #endif // XE_PLATFORM_WIN32 -// We are statically linked with the loader, so use function prototypes. -#define VK_PROTOTYPES +// We use a loader with its own function prototypes. +#include "third_party/volk/volk.h" #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); diff --git a/src/xenia/ui/vulkan/vulkan_device.cc b/src/xenia/ui/vulkan/vulkan_device.cc index d31ffc327..437f8568d 100644 --- a/src/xenia/ui/vulkan/vulkan_device.cc +++ b/src/xenia/ui/vulkan/vulkan_device.cc @@ -169,7 +169,7 @@ bool VulkanDevice::Initialize(DeviceInfo device_info) { queue_info.queueFamilyIndex = i; queue_info.queueCount = family_props.queueCount; - queue_priorities[i].resize(queue_count, 0.f); + queue_priorities[i].resize(family_props.queueCount, 0.f); if (i == ideal_queue_family_index) { // Prioritize the first queue on the primary queue family. queue_priorities[i][0] = 1.0f; diff --git a/src/xenia/ui/vulkan/vulkan_instance.cc b/src/xenia/ui/vulkan/vulkan_instance.cc index efadcfa39..a383cf116 100644 --- a/src/xenia/ui/vulkan/vulkan_instance.cc +++ b/src/xenia/ui/vulkan/vulkan_instance.cc @@ -16,6 +16,7 @@ #include <string> #include "third_party/renderdoc/renderdoc_app.h" +#include "third_party/volk/volk.h" #include "xenia/base/assert.h" #include "xenia/base/logging.h" @@ -30,7 +31,7 @@ #include "xenia/ui/window_gtk.h" #endif -#define VK_API_VERSION VK_API_VERSION_1_0 +#define VK_API_VERSION VK_API_VERSION_1_1 namespace xe { namespace ui { @@ -72,6 +73,10 @@ VulkanInstance::~VulkanInstance() { DestroyInstance(); } bool VulkanInstance::Initialize() { auto version = Version::Parse(VK_API_VERSION); XELOGVK("Initializing Vulkan %s...", version.pretty_string.c_str()); + if (volkInitialize() != VK_SUCCESS) { + XELOGE("volkInitialize() failed!"); + return false; + } // Get all of the global layers and extensions provided by the system. if (!QueryGlobals()) { @@ -271,6 +276,9 @@ bool VulkanInstance::CreateInstance() { return false; } + // Load Vulkan entrypoints and extensions. + volkLoadInstance(handle); + // Enable debug validation, if needed. EnableDebugValidation(); diff --git a/src/xenia/ui/vulkan/vulkan_mem_alloc.h b/src/xenia/ui/vulkan/vulkan_mem_alloc.h new file mode 100644 index 000000000..caf89aae6 --- /dev/null +++ b/src/xenia/ui/vulkan/vulkan_mem_alloc.h @@ -0,0 +1,44 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2018 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_VULKAN_VULKAN_MEM_ALLOC_H_ +#define XENIA_UI_VULKAN_VULKAN_MEM_ALLOC_H_ + +#include "third_party/volk/volk.h" + +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +#include "third_party/vulkan/vk_mem_alloc.h" + +namespace xe { +namespace ui { +namespace vulkan { + +inline void FillVMAVulkanFunctions(VmaVulkanFunctions* vma_funcs) { + vma_funcs->vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties; + vma_funcs->vkGetPhysicalDeviceMemoryProperties = + vkGetPhysicalDeviceMemoryProperties; + vma_funcs->vkAllocateMemory = vkAllocateMemory; + vma_funcs->vkFreeMemory = vkFreeMemory; + vma_funcs->vkMapMemory = vkMapMemory; + vma_funcs->vkUnmapMemory = vkUnmapMemory; + vma_funcs->vkBindBufferMemory = vkBindBufferMemory; + vma_funcs->vkBindImageMemory = vkBindImageMemory; + vma_funcs->vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements; + vma_funcs->vkGetImageMemoryRequirements = vkGetImageMemoryRequirements; + vma_funcs->vkCreateBuffer = vkCreateBuffer; + vma_funcs->vkDestroyBuffer = vkDestroyBuffer; + vma_funcs->vkCreateImage = vkCreateImage; + vma_funcs->vkDestroyImage = vkDestroyImage; +} + +} // namespace vulkan +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_VULKAN_VULKAN_MEM_ALLOC_H_
\ No newline at end of file diff --git a/src/xenia/ui/vulkan/vulkan_util.cc b/src/xenia/ui/vulkan/vulkan_util.cc index 9c0a4238a..2e2034f65 100644 --- a/src/xenia/ui/vulkan/vulkan_util.cc +++ b/src/xenia/ui/vulkan/vulkan_util.cc @@ -14,7 +14,7 @@ // Implement AMD's VMA here. #define VMA_IMPLEMENTATION -#include "third_party/vulkan/vk_mem_alloc.h" +#include "xenia/ui/vulkan/vulkan_mem_alloc.h" namespace xe { namespace ui { |
