Files
InstanceBrick--nightcap/tests/platform_graphics_foundation_test.cc
T
glook9001andglook9001 703af11d17 perf(runtime, asset, jni): lock-free JNI resolution, in-memory ETC1 transcode caching, and spin guard optimization (#89)
* perf(runtime, asset, jni): lock-free JNI object resolution, in-memory ETC1 transcode caching, one-time spin guard classification, and host driver environment tuning

* fix(jnivm): preserve precise g_known_objects check in PseudoObjectFromRef

* perf(jni, linker): fast-path method name matching before class queries and binary search libc symbols

* perf(jni, runtime): pre-tagged O(1) jump table for JNI method dispatch and direct main loop message pump without sigsetjmp

* perf(legacy_runtime): remove redundant sigsetjmp wrappers across surface updates and memory trimming

* Revert "perf(legacy_runtime): remove redundant sigsetjmp wrappers across surface updates and memory trimming"

This reverts commit 82e4019f68aa0d2968716a2dc066bc26961d7755.

* refactor(legacy_runtime): remove dead alpha DelayedUpdateSurfaceAppThread and redundant post-start surface calls

* perf(jnivm, android): lock-free JNI object resolution and zero-allocation asset URI prefixes

* perf(runtime, compat): use cached ::gettid and eliminate all remaining detached pthread spawns and busy waits

* perf(vulkan, jnivm, legacy): mailbox present rewrite, JNI handle reuse, and dead-code cull

Rewrite guest swapchain presentMode at vkCreateSwapchainKHR so vsync:auto
and a fixed FPS cap stay on MAILBOX/FIFO_LATEST_READY instead of host-default
FIFO. Prefer FIFO_LATEST_READY then MAILBOX, bump vsync minImageCount, treat
SUBOPTIMAL as OUT_OF_DATE, and drop the 1s acquire watchdog.

Fix JNI local-frame handle leaks (unsigned 32-bit handles, freelist reuse,
no intern pool wrap-stomp). Default native_set_roblox_channel on so injected
production channel is applied. Remove TM2 .* deny-list, unused OpenGL trim,
and leftover legacy_runtime dead code.

Leave untracked docs, vulkaninfo dumps, rbx_bin assets, and bpftrace scripts
out of this commit.

---------

Co-authored-by: glook9001 <310372091+glook9001@users.noreply.github.com>
2026-08-30 13:46:47 +03:00

288 lines
11 KiB
C++

#include <SDL3/SDL.h>
#include <gtest/gtest.h>
#include <cstdlib>
#include <vector>
#include "mocktail/graphics/android_vulkan_wsi_adapter.h"
#include "mocktail/graphics/angle_probe.h"
#include "mocktail/graphics/graphics_backend.h"
#include "mocktail/graphics/sdl_vulkan_wsi.h"
#include "mocktail/platform/sdl_application_metadata.h"
#include "mocktail/platform/sdl_event_converter.h"
#include "mocktail/platform/sdl_platform_runtime.h"
#include "mocktail/platform/sdl_window_icon.h"
namespace mocktail {
namespace {
using graphics::BackendCapability;
using graphics::BackendSelectionPolicy;
using graphics::CapabilityState;
using graphics::GraphicsBackendKind;
using graphics::HardwareAcceleration;
TEST(SdlApplicationMetadataTest, MatchesInstalledDesktopIdentity) {
Status status = platform::ConfigureSdlApplicationMetadata();
ASSERT_TRUE(status.ok()) << status.message();
EXPECT_STREQ(SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING),
platform::kMocktailApplicationName);
EXPECT_STREQ(
SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING),
platform::kMocktailApplicationIdentifier);
EXPECT_STREQ(SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING),
"game");
}
TEST(GraphicsBackendSelectionTest, FailsClosedWithoutRequestedBackend) {
BackendSelectionPolicy policy;
policy.requested = GraphicsBackendKind::kAngleVulkan;
const std::vector<BackendCapability> capabilities = {
{GraphicsBackendKind::kDirectVulkan, CapabilityState::kReady,
HardwareAcceleration::kHardware, "direct Vulkan ready"}};
auto result = graphics::SelectGraphicsBackend(policy, capabilities);
EXPECT_FALSE(result.status.ok());
EXPECT_EQ(result.backend, GraphicsBackendKind::kAngleVulkan);
}
TEST(GraphicsBackendSelectionTest, UsesFallbackOnlyWhenExplicitlyAllowed) {
BackendSelectionPolicy policy;
policy.requested = GraphicsBackendKind::kAngleVulkan;
policy.allow_fallback = true;
const std::vector<BackendCapability> capabilities = {
{GraphicsBackendKind::kDirectVulkan, CapabilityState::kReady,
HardwareAcceleration::kHardware, "direct Vulkan ready"}};
auto result = graphics::SelectGraphicsBackend(policy, capabilities);
ASSERT_TRUE(result.status.ok()) << result.status.message();
EXPECT_EQ(result.backend, GraphicsBackendKind::kDirectVulkan);
}
TEST(GraphicsBackendSelectionTest, RejectsUnvalidatedLoaderByDefault) {
const std::vector<BackendCapability> capabilities = {
{GraphicsBackendKind::kAngleVulkan, CapabilityState::kLoadable,
HardwareAcceleration::kHardware, "symbols exist"}};
auto result = graphics::SelectGraphicsBackend({}, capabilities);
EXPECT_FALSE(result.status.ok());
}
TEST(AngleProbeTest, RequiresExplicitPinnedLibraryPaths) {
auto capability = graphics::ProbeAngleVulkan({});
EXPECT_EQ(capability.state, CapabilityState::kUnavailable);
EXPECT_NE(capability.detail.find("required"), std::string::npos);
}
TEST(AngleProbeTest, ValidatesConfiguredAngleVulkanDistribution) {
const char* egl = std::getenv("MOCKTAIL_TEST_ANGLE_EGL_LIBRARY");
const char* gles = std::getenv("MOCKTAIL_TEST_ANGLE_GLES_LIBRARY");
if (egl == nullptr || gles == nullptr) {
GTEST_SKIP() << "pinned ANGLE test paths were not provided";
}
graphics::AngleProbeOptions options;
options.egl_library_path = egl;
options.gles_library_path = gles;
auto capability = graphics::ProbeAngleVulkan(options);
EXPECT_EQ(capability.state, CapabilityState::kReady) << capability.detail;
EXPECT_EQ(capability.acceleration, HardwareAcceleration::kHardware);
}
TEST(SdlPlatformRuntimeTest, RejectsInvalidWindowDimensionsBeforeSdlInit) {
auto runtime = platform::CreateSdlPlatformRuntime();
platform::WindowOptions options;
options.width = 0;
Status status = runtime->Initialize(options);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), StatusCode::kInvalidArgument);
EXPECT_FALSE(runtime->IsInitialized());
}
TEST(SdlWindowIconTest, RejectsNullWindow) {
EXPECT_EQ(platform::ApplySdlWindowIcon(nullptr).code(),
StatusCode::kInvalidArgument);
}
TEST(SdlWindowIconTest, AppliesEmbeddedPngToRealWindow) {
const char* enabled = std::getenv("MOCKTAIL_TEST_SDL_WINDOW");
if (enabled == nullptr || std::string(enabled) != "1") {
GTEST_SKIP() << "real SDL window test was not explicitly enabled";
}
ASSERT_TRUE(SDL_Init(SDL_INIT_VIDEO)) << SDL_GetError();
SDL_Window* window =
SDL_CreateWindow("Mocktail icon test", 64, 64, SDL_WINDOW_HIDDEN);
ASSERT_NE(window, nullptr) << SDL_GetError();
const Status status = platform::ApplySdlWindowIcon(window);
EXPECT_TRUE(status.ok()) << status.message();
SDL_DestroyWindow(window);
SDL_Quit();
}
TEST(SdlEventConverterTest, ConvertsFocusWithoutOwningTheSdlQueue) {
SDL_Event source{};
source.type = SDL_EVENT_WINDOW_FOCUS_LOST;
source.window.timestamp = 123;
platform::PlatformEvent event;
auto* non_owning_window = reinterpret_cast<SDL_Window*>(0x1);
ASSERT_TRUE(platform::ConvertSdlEvent(non_owning_window, source, &event));
EXPECT_EQ(event.timestamp_ns, 123U);
const auto* focus = std::get_if<platform::WindowFocusEvent>(&event.payload);
ASSERT_NE(focus, nullptr);
EXPECT_FALSE(focus->focused);
source.type = SDL_EVENT_WINDOW_FOCUS_GAINED;
ASSERT_TRUE(platform::ConvertSdlEvent(non_owning_window, source, &event));
focus = std::get_if<platform::WindowFocusEvent>(&event.payload);
ASSERT_NE(focus, nullptr);
EXPECT_TRUE(focus->focused);
}
TEST(SdlEventConverterTest, RejectsInvalidArgumentsAndUnsupportedEvents) {
SDL_Event source{};
source.type = SDL_EVENT_USER;
platform::PlatformEvent event;
auto* non_owning_window = reinterpret_cast<SDL_Window*>(0x1);
EXPECT_FALSE(platform::ConvertSdlEvent(nullptr, source, &event));
EXPECT_FALSE(platform::ConvertSdlEvent(non_owning_window, source, nullptr));
EXPECT_FALSE(platform::ConvertSdlEvent(non_owning_window, source, &event));
}
TEST(SdlPlatformRuntimeTest, CreatesConfiguredRealNativeWindow) {
const char* enabled = std::getenv("MOCKTAIL_TEST_SDL_WINDOW");
if (enabled == nullptr || std::string(enabled) != "1") {
GTEST_SKIP() << "real SDL window test was not explicitly enabled";
}
auto runtime = platform::CreateSdlPlatformRuntime();
platform::WindowOptions options;
options.title = "Mocktail foundation test";
options.width = 320;
options.height = 180;
options.initially_hidden = true;
options.require_native_window = true;
Status status = runtime->Initialize(options);
ASSERT_TRUE(status.ok()) << status.message();
EXPECT_TRUE(runtime->IsInitialized());
EXPECT_NE(runtime->GetNativeWindow().backend_window, nullptr);
EXPECT_TRUE(runtime->GetNativeWindow().HasPlatformHandle());
status = runtime->ShowWindow();
ASSERT_TRUE(status.ok()) << status.message();
if (std::getenv("MOCKTAIL_TEST_SDL_WINDOW_HOLD") != nullptr) {
SDL_Delay(3000);
}
SDL_Event source{};
source.type = SDL_EVENT_KEY_DOWN;
source.key.timestamp = 42;
source.key.down = true;
source.key.scancode = SDL_SCANCODE_A;
source.key.key = SDLK_A;
ASSERT_TRUE(SDL_PushEvent(&source));
bool received_key = false;
for (int i = 0; i < 32 && !received_key; ++i) {
platform::PlatformEvent event;
bool has_event = false;
status = runtime->PollEvent(&event, &has_event);
ASSERT_TRUE(status.ok()) << status.message();
if (!has_event) {
break;
}
const auto* key = std::get_if<platform::KeyEvent>(&event.payload);
if (key != nullptr && key->pressed &&
key->scancode == static_cast<std::uint32_t>(SDL_SCANCODE_A)) {
received_key = true;
EXPECT_EQ(event.timestamp_ns, 42u);
}
}
EXPECT_TRUE(received_key);
runtime->Shutdown();
EXPECT_FALSE(runtime->IsInitialized());
}
TEST(SdlVulkanWsiTest, DiscoversHostWsiWithoutCreatingRenderer) {
const char* enabled = std::getenv("MOCKTAIL_TEST_SDL_WINDOW");
if (enabled == nullptr || std::string(enabled) != "1") {
GTEST_SKIP() << "real SDL window test was not explicitly enabled";
}
auto runtime = platform::CreateSdlPlatformRuntime();
platform::WindowOptions options;
options.title = "Mocktail Vulkan WSI test";
options.width = 320;
options.height = 180;
options.surface_api = platform::WindowSurfaceApi::kDirectVulkan;
Status status = runtime->Initialize(options);
ASSERT_TRUE(status.ok()) << status.message();
graphics::SdlVulkanWsi wsi;
status = wsi.Initialize(runtime->GetNativeWindow());
ASSERT_TRUE(status.ok()) << status.message();
EXPECT_FALSE(wsi.required_instance_extensions().empty());
EXPECT_NE(wsi.GetInstanceProcAddress(), nullptr);
EXPECT_EQ(wsi.DescribeCapability().state, CapabilityState::kLoadable);
wsi.Shutdown();
runtime->Shutdown();
}
TEST(AndroidVulkanWsiAdapterTest, RewritesAndroidSurfaceToHostWsiExtensions) {
const std::vector<std::string> android_extensions = {
"VK_KHR_surface", graphics::kAndroidSurfaceExtension,
"VK_EXT_debug_utils",
"VK_KHR_surface"};
const std::vector<std::string> host_wsi_extensions = {
"VK_KHR_surface", "VK_KHR_xcb_surface"};
std::vector<std::string> translated;
Status status = graphics::TranslateAndroidVulkanInstanceExtensions(
android_extensions, host_wsi_extensions, &translated);
ASSERT_TRUE(status.ok()) << status.message();
EXPECT_EQ(translated,
(std::vector<std::string>{"VK_KHR_surface",
"VK_EXT_debug_utils",
"VK_KHR_xcb_surface"}));
}
TEST(AndroidVulkanWsiAdapterTest, RejectsSurfaceWithoutHostWsiExtensions) {
std::vector<std::string> translated;
Status status = graphics::TranslateAndroidVulkanInstanceExtensions(
{graphics::kAndroidSurfaceExtension}, {}, &translated);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), StatusCode::kFailedPrecondition);
}
TEST(AndroidVulkanWsiAdapterTest, LeavesNonSurfaceExtensionsUnchanged) {
std::vector<std::string> translated;
Status status = graphics::TranslateAndroidVulkanInstanceExtensions(
{"VK_EXT_debug_utils"}, {"VK_KHR_surface", "VK_KHR_wayland_surface"},
&translated);
ASSERT_TRUE(status.ok()) << status.message();
EXPECT_EQ(translated,
(std::vector<std::string>{"VK_EXT_debug_utils"}));
}
TEST(AndroidVulkanWsiAdapterTest, MapsSuboptimalToOutOfDateForSwapchainRebuild) {
constexpr VkResult kOutOfDate = static_cast<VkResult>(-1000001004);
EXPECT_EQ(graphics::NormalizeAndroidSwapchainResult(VK_SUBOPTIMAL_KHR),
kOutOfDate);
EXPECT_EQ(graphics::NormalizeAndroidSwapchainResult(VK_SUCCESS),
VK_SUCCESS);
EXPECT_EQ(graphics::NormalizeAndroidSwapchainResult(kOutOfDate), kOutOfDate);
}
} // namespace
} // namespace mocktail