mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-15 07:20:31 +02:00
EmuThread_Join drains the render queue on the calling thread. On Android that is either the UI thread with no GL context (NativeApp.shutdown), or the GL thread with a brand new context after the old one was lost (displayInit the second time). The skip flag was only set once the emu thread reached NativeShutdownGraphics, so everything queued before that ran for real, against nothing or against the wrong context. Add GraphicsContext::NotifyContextLost() and call it before the join in both places. skipGLCalls_ is now atomic, since it's set from one thread and read from another. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
78 lines
2.9 KiB
C++
78 lines
2.9 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
|
|
#include "Common/Log.h"
|
|
#include "Common/GPU/thin3d.h"
|
|
#include "Common/TimeUtil.h"
|
|
|
|
struct WindowDesc {
|
|
WindowSystem winsys{};
|
|
void *data1 = nullptr;
|
|
void *data2 = nullptr;
|
|
|
|
bool Valid() const {
|
|
return winsys != WindowSystem::WINDOWSYSTEM_UNINITIALIZED && data2; // Not all platforms use data1, so don't check it.
|
|
}
|
|
};
|
|
|
|
// Init is done differently on each platform, and done close to the creation, so it's
|
|
// expected to be implemented by subclasses.
|
|
class GraphicsContext {
|
|
public:
|
|
virtual ~GraphicsContext() = default;
|
|
|
|
// Some implementations will separate init of the API and of the rendering surface.
|
|
// wnd here is only to be used for messageboxes and similar, not for the surface.
|
|
virtual bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { return true; }
|
|
virtual void ShutdownAPI() {}
|
|
|
|
// These should be called on the render thread if NeedsRenderThread.
|
|
virtual bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { return true; }
|
|
virtual void ShutdownSurface() {}
|
|
|
|
// Used during window resize on desktop ONLY. Must be called from the window thread,
|
|
// not the rendering thread or CPU thread.
|
|
virtual void Pause() {}
|
|
virtual void Resume() {}
|
|
|
|
virtual void Resize() = 0;
|
|
virtual void NotifyWindowRestored() {}
|
|
|
|
// Needs casting to the appropriate type, unfortunately. Should find a better solution..
|
|
virtual void *GetAPIContext() { return nullptr; }
|
|
|
|
// OpenGL returns true here. Later we will need to do the same with Vulkan.
|
|
virtual bool NeedsSeparateEmuThread() const { return false; }
|
|
|
|
// Called from the render thread from threaded backends.
|
|
virtual void ThreadStart() {}
|
|
virtual bool ThreadFrame() { return true; } // Returns false when it's time to exit the loop.
|
|
virtual void ThreadEnd() {}
|
|
|
|
// When this is called, the emulation thread has stopped producing frames completely.
|
|
// Called from the emu thread, so be ready for that.
|
|
virtual void NotifyEmuThreadExit() {}
|
|
|
|
// The API context that queued-up commands were recorded against is gone (or isn't current on the
|
|
// thread about to drain them), so they must not be executed for real. Only meaningful on Android GL.
|
|
virtual void NotifyContextLost() {}
|
|
|
|
// Useful for checks that need to be performed every frame.
|
|
// Should strive to get rid of these.
|
|
virtual void Poll() {}
|
|
|
|
// TODO: Store in a protected variable?
|
|
virtual Draw::DrawContext *GetDrawContext() = 0;
|
|
};
|
|
|
|
// This is used by the headless build, for the case of software rendering where we really don't need any context,
|
|
// unlike the standalone build where we do need a context anyway for UI and stuff.
|
|
class NullGraphicsContext : public GraphicsContext {
|
|
public:
|
|
NullGraphicsContext() {}
|
|
Draw::DrawContext *GetDrawContext() override { return nullptr; }
|
|
void Resize() override {}
|
|
bool NeedsSeparateEmuThread() const override { return false; }
|
|
};
|