Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/headless/SDLHeadlessHost.cpp
5656 views
1
// Copyright (c) 2017- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#ifdef SDL
19
20
#include <cstdio>
21
22
#include "ppsspp_config.h"
23
#if PPSSPP_PLATFORM(MAC)
24
#include "SDL2/SDL.h"
25
#else
26
#include "SDL.h"
27
#endif
28
29
#include "headless/SDLHeadlessHost.h"
30
#include "Common/GPU/OpenGL/GLCommon.h"
31
#include "Common/GPU/OpenGL/GLFeatures.h"
32
#include "Common/GPU/thin3d_create.h"
33
#include "Common/GPU/OpenGL/GLRenderManager.h"
34
#include "Common/File/VFS/VFS.h"
35
#include "Common/File/VFS/DirectoryReader.h"
36
#include "Common/GraphicsContext.h"
37
#include "Common/TimeUtil.h"
38
#include "Common/Thread/ThreadUtil.h"
39
#include "Core/Config.h"
40
#include "Core/System.h"
41
#include "GPU/GPUState.h"
42
43
const bool WINDOW_VISIBLE = false;
44
const int WINDOW_WIDTH = 480;
45
const int WINDOW_HEIGHT = 272;
46
47
SDL_Window *CreateHiddenWindow() {
48
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS;
49
if (!WINDOW_VISIBLE) {
50
flags |= SDL_WINDOW_HIDDEN;
51
}
52
return SDL_CreateWindow("PPSSPPHeadless", 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, flags);
53
}
54
55
class GLDummyGraphicsContext : public GraphicsContext {
56
public:
57
GLDummyGraphicsContext() {
58
}
59
~GLDummyGraphicsContext() { delete draw_; }
60
61
bool InitFromRenderThread(std::string *errorMessage) override;
62
63
void ShutdownFromRenderThread() override {
64
delete draw_;
65
draw_ = nullptr;
66
67
SDL_GL_DeleteContext(glContext_);
68
glContext_ = nullptr;
69
SDL_DestroyWindow(screen_);
70
screen_ = nullptr;
71
72
SDL_Quit();
73
}
74
75
Draw::DrawContext *GetDrawContext() override {
76
return draw_;
77
}
78
79
void ThreadStart() override {
80
renderManager_->ThreadStart(draw_);
81
}
82
83
bool ThreadFrame(bool waitIfEmpty) override {
84
return renderManager_->ThreadFrame(waitIfEmpty);
85
}
86
87
void ThreadEnd() override {
88
renderManager_->ThreadEnd();
89
}
90
91
void Shutdown() override {}
92
void Resize() override {}
93
94
private:
95
Draw::DrawContext *draw_ = nullptr;
96
GLRenderManager *renderManager_ = nullptr;
97
SDL_Window *screen_;
98
SDL_GLContext glContext_;
99
};
100
101
bool GLDummyGraphicsContext::InitFromRenderThread(std::string *errorMessage) {
102
SDL_Init(SDL_INIT_VIDEO);
103
104
// TODO
105
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
106
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
107
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
108
109
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
110
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
111
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
112
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
113
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
114
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
115
116
screen_ = CreateHiddenWindow();
117
if (!screen_) {
118
const char *err = SDL_GetError();
119
printf("Failed to create offscreen window: %s\n", err ? err : "(unknown error)");
120
return false;
121
}
122
glContext_ = SDL_GL_CreateContext(screen_);
123
if (!glContext_) {
124
const char *err = SDL_GetError();
125
printf("Failed to create GL context: %s\n", err ? err : "(unknown error)");
126
return false;
127
}
128
129
// Ensure that the swap interval is set after context creation (needed for kmsdrm)
130
SDL_GL_SetSwapInterval(0);
131
132
#ifndef USING_GLES2
133
// Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc.
134
// glewExperimental allows us to force GLEW to search for the pointers anyway.
135
if (gl_extensions.IsCoreContext)
136
glewExperimental = true;
137
if (GLEW_OK != glewInit()) {
138
printf("Failed to initialize glew!\n");
139
return false;
140
}
141
// Unfortunately, glew will generate an invalid enum error, ignore.
142
if (gl_extensions.IsCoreContext)
143
glGetError();
144
145
if (GLEW_VERSION_2_0) {
146
printf("OpenGL 2.0 or higher.\n");
147
} else {
148
printf("Sorry, this program requires OpenGL 2.0.\n");
149
return false;
150
}
151
#endif
152
153
CheckGLExtensions();
154
draw_ = Draw::T3DCreateGLContext(false);
155
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
156
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
157
SetGPUBackend(GPUBackend::OPENGL);
158
bool success = draw_->CreatePresets();
159
_assert_(success);
160
renderManager_->SetSwapFunction([&]() {
161
SDL_GL_SwapWindow(screen_);
162
});
163
return success;
164
}
165
166
bool SDLHeadlessHost::InitGraphics(std::string *error_message, GraphicsContext **ctx, GPUCore core) {
167
GraphicsContext *graphicsContext = new GLDummyGraphicsContext();
168
*ctx = graphicsContext;
169
gfx_ = graphicsContext;
170
171
std::thread th([&]{
172
// This is the "EmuThread".
173
SetCurrentThreadName("SDL-EmuThread");
174
175
while (threadState_ == RenderThreadState::IDLE)
176
sleep_ms(1, "sdl-idle-poll");
177
threadState_ = RenderThreadState::STARTING;
178
179
std::string err;
180
if (!gfx_->InitFromRenderThread(&err)) {
181
threadState_ = RenderThreadState::START_FAILED;
182
return;
183
}
184
gfx_->ThreadStart();
185
threadState_ = RenderThreadState::STARTED;
186
187
while (threadState_ != RenderThreadState::STOP_REQUESTED) {
188
if (!gfx_->ThreadFrame(true)) {
189
break;
190
}
191
}
192
193
threadState_ = RenderThreadState::STOPPING;
194
gfx_->ThreadEnd();
195
gfx_->ShutdownFromRenderThread();
196
threadState_ = RenderThreadState::STOPPED;
197
});
198
th.detach();
199
200
threadState_ = RenderThreadState::START_REQUESTED;
201
while (threadState_ == RenderThreadState::START_REQUESTED || threadState_ == RenderThreadState::STARTING)
202
sleep_ms(1, "sdl-start-poll");
203
204
return threadState_ == RenderThreadState::STARTED;
205
}
206
207
void SDLHeadlessHost::ShutdownGraphics() {
208
while (threadState_ != RenderThreadState::STOPPED && threadState_ != RenderThreadState::START_FAILED)
209
sleep_ms(1, "sdl-stop-poll");
210
211
gfx_->Shutdown();
212
delete gfx_;
213
gfx_ = nullptr;
214
}
215
216
void SDLHeadlessHost::SwapBuffers() {
217
}
218
219
#endif
220
221