Files
EVEngine/test/main.cpp
T
西风逍遥游 454c4b5504 gpu-driven: fix indirect instance indexing and bind per-mesh buffers
Stage 1 (CPU indirect draw) produced no geometry for the second draw:
- mesh3d_gpudriven.vert added pc.firstInstance on top of gl_InstanceIndex,
  which already includes VkDrawIndexedIndirectCommand.firstInstance per the
  Vulkan spec; the second command read instance 2 instead of 1 and sampled
  garbage matrices. Drop the push-constant indirection entirely.
- gpuDrivenSubmitOpaque never bound vertex/index buffers; each GpuMesh owns
  its own buffer pair (vertexBase/firstIndex are 0), so draws ran against
  whatever the legacy path left bound. Track meshRecordOwners_ parallel to
  the GPU mesh table and bind per-mesh at bucket boundaries.

Test: GpuDriven.opaqueForwardParityMultiTexture now uses a distinct bindless
slot-1 albedo on the second sphere; all 5 GpuDriven tests + IndirectBuilder
tests pass (integrated GPU). Also add the shared Windows crash-backtrace
handler (CrashHandler.h) to unit_test and document the repo debugging
playbook (incl. stale-object/layout pitfalls with unscanned Ninja deps) in
AGENTS.md.
2026-08-21 13:35:57 +08:00

97 lines
3.2 KiB
C++

#include "zeroerr/unittest.h"
#include "common/CrashHandler.h"
#if defined(EVENGINE_ANDROID)
#include <SDL2/SDL.h>
#include <SDL2/SDL_main.h>
#include <android/log.h>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <string>
#include <streambuf>
#include <unistd.h>
namespace {
// Route C++ iostream output to Android logcat so `adb logcat` can show the
// zeroerr console reporter (stdout/stderr are not attached to anything on Android).
class LogcatBuf : public std::streambuf {
public:
LogcatBuf() { setp(buffer_, buffer_ + sizeof(buffer_) - 1); }
~LogcatBuf() override { sync(); }
protected:
int overflow(int ch) override {
if (ch != traits_type::eof()) {
*pptr() = static_cast<char>(ch);
pbump(1);
}
return sync() == 0 ? traits_type::not_eof(ch) : traits_type::eof();
}
int sync() override {
if (pptr() == pbase())
return 0;
*pptr() = '\0';
__android_log_print(ANDROID_LOG_INFO, "EVEngineTest", "%s", pbase());
setp(buffer_, buffer_ + sizeof(buffer_) - 1);
return 0;
}
private:
char buffer_[1024];
};
// Root of the unpacked test tree: <internal storage>/evengine_test. The Java
// activity unpacks the APK's assets/test + assets/examples here before SDL_main runs.
std::string testRoot() {
const char *internal = SDL_AndroidGetInternalStoragePath();
if (!internal)
return {};
return std::string(internal) + "/evengine_test";
}
} // namespace
#endif // EVENGINE_ANDROID
// Signature must match the `extern "C" int SDL_main(int, char*[])` declared by
// SDL_main.h so the symbol stays unmangled ("SDL_main" for the SDLActivity JNI
// dlsym). A `const char**` parameter would become a C++ overload and be mangled.
int main(int argc, char **argv) {
#if defined(EVENGINE_WINDOWS) || defined(_WIN32)
// Same symbolized crash backtrace as the engine binary (main.cpp), so a
// crashing test prints a stack trace instead of a bare access-violation
// exit code.
eve::installCrashHandler();
#endif
#if defined(EVENGINE_ANDROID)
static LogcatBuf logcatBuf;
std::cout.rdbuf(&logcatBuf);
std::cerr.rdbuf(&logcatBuf);
std::clog.rdbuf(&logcatBuf);
std::cout.setf(std::ios::unitbuf);
std::cerr.setf(std::ios::unitbuf);
const std::string root = testRoot();
if (root.empty()) {
__android_log_print(ANDROID_LOG_ERROR, "EVEngineTest", "no internal storage path");
} else if (chdir(root.c_str()) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "EVEngineTest", "chdir(%s) failed: %s",
root.c_str(), strerror(errno));
} else {
__android_log_print(ANDROID_LOG_INFO, "EVEngineTest", "test root: %s", root.c_str());
}
// SDLActivity already ran SDL_SetMainReady(); init the subsystems the tests use.
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER | SDL_INIT_AUDIO) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "EVEngineTest", "SDL_Init failed: %s",
SDL_GetError());
}
__android_log_print(ANDROID_LOG_INFO, "EVEngineTest", "unit_test android starting");
#endif // EVENGINE_ANDROID
return zeroerr::UnitTest().parseArgs(argc, const_cast<const char **>(argv)).run();
}