1 // Copyright 2014 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <algorithm>
6 #include <array>
7 #include <condition_variable>
8 #include <cstddef>
9 #include <cstdlib>
10 #include <deque>
11 #include <memory>
12 #include <mutex>
13 #include <glad/glad.h>
14 #include <queue>
15 #include "common/assert.h"
16 #include "common/bit_field.h"
17 #include "common/logging/log.h"
18 #include "common/microprofile.h"
19 #include "core/core.h"
20 #include "core/core_timing.h"
21 #include "core/dumping/backend.h"
22 #include "core/frontend/emu_window.h"
23 #include "core/frontend/framebuffer_layout.h"
24 #include "core/hw/gpu.h"
25 #include "core/hw/hw.h"
26 #include "core/hw/lcd.h"
27 #include "core/memory.h"
28 #include "core/settings.h"
29 #include "core/tracer/recorder.h"
30 #include "video_core/debug_utils/debug_utils.h"
31 #include "video_core/rasterizer_interface.h"
32 #include "video_core/renderer_opengl/gl_state.h"
33 #include "video_core/renderer_opengl/gl_vars.h"
34 #include "video_core/renderer_opengl/post_processing_opengl.h"
35 #include "video_core/renderer_opengl/renderer_opengl.h"
36 #include "video_core/video_core.h"
37
38 namespace OpenGL {
39
40 // If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
41 // to wait on available presentation frames. There doesn't seem to be much of a downside to a larger
42 // number but 9 swap textures at 60FPS presentation allows for 800% speed so thats probably fine
43 #ifdef ANDROID
44 // Reduce the size of swap_chain, since the UI only allows upto 200% speed.
45 constexpr std::size_t SWAP_CHAIN_SIZE = 6;
46 #else
47 constexpr std::size_t SWAP_CHAIN_SIZE = 9;
48 #endif
49
50 class OGLTextureMailboxException : public std::runtime_error {
51 public:
52 using std::runtime_error::runtime_error;
53 };
54
55 class OGLTextureMailbox : public Frontend::TextureMailbox {
56 public:
57 std::mutex swap_chain_lock;
58 std::condition_variable free_cv;
59 std::condition_variable present_cv;
60 std::array<Frontend::Frame, SWAP_CHAIN_SIZE> swap_chain{};
61 std::queue<Frontend::Frame*> free_queue{};
62 std::deque<Frontend::Frame*> present_queue{};
63 Frontend::Frame* previous_frame = nullptr;
64
OGLTextureMailbox()65 OGLTextureMailbox() {
66 for (auto& frame : swap_chain) {
67 free_queue.push(&frame);
68 }
69 }
70
~OGLTextureMailbox()71 ~OGLTextureMailbox() override {
72 // lock the mutex and clear out the present and free_queues and notify any people who are
73 // blocked to prevent deadlock on shutdown
74 std::scoped_lock lock(swap_chain_lock);
75 std::queue<Frontend::Frame*>().swap(free_queue);
76 present_queue.clear();
77 present_cv.notify_all();
78 free_cv.notify_all();
79 }
80
ReloadPresentFrame(Frontend::Frame * frame,u32 height,u32 width)81 void ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) override {
82 frame->present.Release();
83 frame->present.Create();
84 GLint previous_draw_fbo{};
85 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
86 glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
87 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
88 frame->color.handle);
89 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
90 LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
91 }
92 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
93 frame->color_reloaded = false;
94 }
95
ReloadRenderFrame(Frontend::Frame * frame,u32 width,u32 height)96 void ReloadRenderFrame(Frontend::Frame* frame, u32 width, u32 height) override {
97 OpenGLState prev_state = OpenGLState::GetCurState();
98 OpenGLState state = OpenGLState::GetCurState();
99
100 // Recreate the color texture attachment
101 frame->color.Release();
102 frame->color.Create();
103 state.renderbuffer = frame->color.handle;
104 state.Apply();
105 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
106
107 // Recreate the FBO for the render target
108 frame->render.Release();
109 frame->render.Create();
110 state.draw.read_framebuffer = frame->render.handle;
111 state.draw.draw_framebuffer = frame->render.handle;
112 state.Apply();
113 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
114 frame->color.handle);
115 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
116 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
117 }
118 prev_state.Apply();
119 frame->width = width;
120 frame->height = height;
121 frame->color_reloaded = true;
122 }
123
GetRenderFrame()124 Frontend::Frame* GetRenderFrame() override {
125 std::unique_lock<std::mutex> lock(swap_chain_lock);
126
127 // If theres no free frames, we will reuse the oldest render frame
128 if (free_queue.empty()) {
129 auto frame = present_queue.back();
130 present_queue.pop_back();
131 return frame;
132 }
133
134 Frontend::Frame* frame = free_queue.front();
135 free_queue.pop();
136 return frame;
137 }
138
ReleaseRenderFrame(Frontend::Frame * frame)139 void ReleaseRenderFrame(Frontend::Frame* frame) override {
140 std::unique_lock<std::mutex> lock(swap_chain_lock);
141 present_queue.push_front(frame);
142 present_cv.notify_one();
143 }
144
145 // This is virtual as it is to be overriden in OGLVideoDumpingMailbox below.
LoadPresentFrame()146 virtual void LoadPresentFrame() {
147 // free the previous frame and add it back to the free queue
148 if (previous_frame) {
149 free_queue.push(previous_frame);
150 free_cv.notify_one();
151 }
152
153 // the newest entries are pushed to the front of the queue
154 Frontend::Frame* frame = present_queue.front();
155 present_queue.pop_front();
156 // remove all old entries from the present queue and move them back to the free_queue
157 for (auto f : present_queue) {
158 free_queue.push(f);
159 }
160 present_queue.clear();
161 previous_frame = frame;
162 }
163
TryGetPresentFrame(int timeout_ms)164 Frontend::Frame* TryGetPresentFrame(int timeout_ms) override {
165 std::unique_lock<std::mutex> lock(swap_chain_lock);
166 // wait for new entries in the present_queue
167 present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
168 [&] { return !present_queue.empty(); });
169 if (present_queue.empty()) {
170 // timed out waiting for a frame to draw so return the previous frame
171 return previous_frame;
172 }
173
174 LoadPresentFrame();
175 return previous_frame;
176 }
177 };
178
179 /// This mailbox is different in that it will never discard rendered frames
180 class OGLVideoDumpingMailbox : public OGLTextureMailbox {
181 public:
182 bool quit = false;
183
GetRenderFrame()184 Frontend::Frame* GetRenderFrame() override {
185 std::unique_lock<std::mutex> lock(swap_chain_lock);
186
187 // If theres no free frames, we will wait until one shows up
188 if (free_queue.empty()) {
189 free_cv.wait(lock, [&] { return (!free_queue.empty() || quit); });
190 if (quit) {
191 throw OGLTextureMailboxException("VideoDumpingMailbox quitting");
192 }
193
194 if (free_queue.empty()) {
195 LOG_CRITICAL(Render_OpenGL, "Could not get free frame");
196 return nullptr;
197 }
198 }
199
200 Frontend::Frame* frame = free_queue.front();
201 free_queue.pop();
202 return frame;
203 }
204
LoadPresentFrame()205 void LoadPresentFrame() override {
206 // free the previous frame and add it back to the free queue
207 if (previous_frame) {
208 free_queue.push(previous_frame);
209 free_cv.notify_one();
210 }
211
212 Frontend::Frame* frame = present_queue.back();
213 present_queue.pop_back();
214 previous_frame = frame;
215
216 // Do not remove entries from the present_queue, as video dumping would require
217 // that we preserve all frames
218 }
219
TryGetPresentFrame(int timeout_ms)220 Frontend::Frame* TryGetPresentFrame(int timeout_ms) override {
221 std::unique_lock<std::mutex> lock(swap_chain_lock);
222 // wait for new entries in the present_queue
223 present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
224 [&] { return !present_queue.empty(); });
225 if (present_queue.empty()) {
226 // timed out waiting for a frame
227 return nullptr;
228 }
229
230 LoadPresentFrame();
231 return previous_frame;
232 }
233 };
234
235 static const char vertex_shader[] = R"(
236 in vec2 vert_position;
237 in vec2 vert_tex_coord;
238 out vec2 frag_tex_coord;
239
240 // This is a truncated 3x3 matrix for 2D transformations:
241 // The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
242 // The third column performs translation.
243 // The third row could be used for projection, which we don't need in 2D. It hence is assumed to
244 // implicitly be [0, 0, 1]
245 uniform mat3x2 modelview_matrix;
246
247 void main() {
248 // Multiply input position by the rotscale part of the matrix and then manually translate by
249 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
250 // to `vec3(vert_position.xy, 1.0)`
251 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
252 frag_tex_coord = vert_tex_coord;
253 }
254 )";
255
256 static const char fragment_shader[] = R"(
257 in vec2 frag_tex_coord;
258 layout(location = 0) out vec4 color;
259
260 uniform vec4 i_resolution;
261 uniform vec4 o_resolution;
262 uniform int layer;
263
264 uniform sampler2D color_texture;
265
266 void main() {
267 color = texture(color_texture, frag_tex_coord);
268 }
269 )";
270
271 static const char fragment_shader_anaglyph[] = R"(
272
273 // Anaglyph Red-Cyan shader based on Dubois algorithm
274 // Constants taken from the paper:
275 // "Conversion of a Stereo Pair to Anaglyph with
276 // the Least-Squares Projection Method"
277 // Eric Dubois, March 2009
278 const mat3 l = mat3( 0.437, 0.449, 0.164,
279 -0.062,-0.062,-0.024,
280 -0.048,-0.050,-0.017);
281 const mat3 r = mat3(-0.011,-0.032,-0.007,
282 0.377, 0.761, 0.009,
283 -0.026,-0.093, 1.234);
284
285 in vec2 frag_tex_coord;
286 out vec4 color;
287
288 uniform vec4 resolution;
289 uniform int layer;
290
291 uniform sampler2D color_texture;
292 uniform sampler2D color_texture_r;
293
294 void main() {
295 vec4 color_tex_l = texture(color_texture, frag_tex_coord);
296 vec4 color_tex_r = texture(color_texture_r, frag_tex_coord);
297 color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a);
298 }
299 )";
300
301 static const char fragment_shader_interlaced[] = R"(
302
303 in vec2 frag_tex_coord;
304 out vec4 color;
305
306 uniform vec4 o_resolution;
307
308 uniform sampler2D color_texture;
309 uniform sampler2D color_texture_r;
310
311 uniform int reverse_interlaced;
312
313 void main() {
314 float screen_row = o_resolution.x * frag_tex_coord.x;
315 if (int(screen_row) % 2 == reverse_interlaced)
316 color = texture(color_texture, frag_tex_coord);
317 else
318 color = texture(color_texture_r, frag_tex_coord);
319 }
320 )";
321
322 /**
323 * Vertex structure that the drawn screen rectangles are composed of.
324 */
325 struct ScreenRectVertex {
ScreenRectVertexOpenGL::ScreenRectVertex326 ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v) {
327 position[0] = x;
328 position[1] = y;
329 tex_coord[0] = u;
330 tex_coord[1] = v;
331 }
332
333 GLfloat position[2];
334 GLfloat tex_coord[2];
335 };
336
337 /**
338 * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
339 * corner and (width, height) on the lower-bottom.
340 *
341 * The projection part of the matrix is trivial, hence these operations are represented
342 * by a 3x2 matrix.
343 *
344 * @param flipped Whether the frame should be flipped upside down.
345 */
346 static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, const float height,
347 bool flipped) {
348
349 std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order
350
351 // Last matrix row is implicitly assumed to be [0, 0, 1].
352 if (flipped) {
353 // clang-format off
354 matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
355 matrix[1] = 0.f; matrix[3] = 2.f / height; matrix[5] = -1.f;
356 // clang-format on
357 } else {
358 // clang-format off
359 matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
360 matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
361 // clang-format on
362 }
363
364 return matrix;
365 }
366
367 RendererOpenGL::RendererOpenGL(Frontend::EmuWindow& window)
368 : RendererBase{window}, frame_dumper(Core::System::GetInstance().VideoDumper(), window) {
369
370 window.mailbox = std::make_unique<OGLTextureMailbox>();
371 frame_dumper.mailbox = std::make_unique<OGLVideoDumpingMailbox>();
372 }
373
374 RendererOpenGL::~RendererOpenGL() = default;
375
376 MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
377 MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
378
379 /// Swap buffers (render frame)
380 void RendererOpenGL::SwapBuffers() {
381 // Maintain the rasterizer's state as a priority
382 OpenGLState prev_state = OpenGLState::GetCurState();
383 state.Apply();
384
385 PrepareRendertarget();
386
387 RenderScreenshot();
388
389 const auto& layout = render_window.GetFramebufferLayout();
390 RenderToMailbox(layout, render_window.mailbox, false);
391
392 if (frame_dumper.IsDumping()) {
393 try {
394 RenderToMailbox(frame_dumper.GetLayout(), frame_dumper.mailbox, true);
395 } catch (const OGLTextureMailboxException& exception) {
396 LOG_DEBUG(Render_OpenGL, "Frame dumper exception caught: {}", exception.what());
397 }
398 }
399
400 m_current_frame++;
401
402 Core::System::GetInstance().perf_stats->EndSystemFrame();
403
404 render_window.PollEvents();
405
406 Core::System::GetInstance().frame_limiter.DoFrameLimiting(
407 Core::System::GetInstance().CoreTiming().GetGlobalTimeUs());
408 Core::System::GetInstance().perf_stats->BeginSystemFrame();
409
410 prev_state.Apply();
411 RefreshRasterizerSetting();
412
413 if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
414 Pica::g_debug_context->recorder->FrameFinished();
415 }
416 }
417
RenderScreenshot()418 void RendererOpenGL::RenderScreenshot() {
419 if (VideoCore::g_renderer_screenshot_requested) {
420 // Draw this frame to the screenshot framebuffer
421 screenshot_framebuffer.Create();
422 GLuint old_read_fb = state.draw.read_framebuffer;
423 GLuint old_draw_fb = state.draw.draw_framebuffer;
424 state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
425 state.Apply();
426
427 Layout::FramebufferLayout layout{VideoCore::g_screenshot_framebuffer_layout};
428
429 GLuint renderbuffer;
430 glGenRenderbuffers(1, &renderbuffer);
431 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
432 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
433 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
434 renderbuffer);
435
436 DrawScreens(layout, false);
437
438 glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
439 VideoCore::g_screenshot_bits);
440
441 screenshot_framebuffer.Release();
442 state.draw.read_framebuffer = old_read_fb;
443 state.draw.draw_framebuffer = old_draw_fb;
444 state.Apply();
445 glDeleteRenderbuffers(1, &renderbuffer);
446
447 VideoCore::g_screenshot_complete_callback();
448 VideoCore::g_renderer_screenshot_requested = false;
449 }
450 }
451
452 void RendererOpenGL::PrepareRendertarget() {
453 for (int i : {0, 1, 2}) {
454 int fb_id = i == 2 ? 1 : 0;
455 const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id];
456
457 // Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04
458 u32 lcd_color_addr =
459 (fb_id == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom);
460 lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr;
461 LCD::Regs::ColorFill color_fill = {0};
462 LCD::Read(color_fill.raw, lcd_color_addr);
463
464 if (color_fill.is_enabled) {
465 LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b,
466 screen_infos[i].texture);
467
468 // Resize the texture in case the framebuffer size has changed
469 screen_infos[i].texture.width = 1;
470 screen_infos[i].texture.height = 1;
471 } else {
472 if (screen_infos[i].texture.width != (GLsizei)framebuffer.width ||
473 screen_infos[i].texture.height != (GLsizei)framebuffer.height ||
474 screen_infos[i].texture.format != framebuffer.color_format) {
475 // Reallocate texture if the framebuffer size has changed.
476 // This is expected to not happen very often and hence should not be a
477 // performance problem.
478 ConfigureFramebufferTexture(screen_infos[i].texture, framebuffer);
479 }
480 LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1);
481
482 // Resize the texture in case the framebuffer size has changed
483 screen_infos[i].texture.width = framebuffer.width;
484 screen_infos[i].texture.height = framebuffer.height;
485 }
486 }
487 }
488
489 void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout,
490 std::unique_ptr<Frontend::TextureMailbox>& mailbox,
491 bool flipped) {
492
493 Frontend::Frame* frame;
494 {
495 MICROPROFILE_SCOPE(OpenGL_WaitPresent);
496
497 frame = mailbox->GetRenderFrame();
498
499 // Clean up sync objects before drawing
500
501 // INTEL driver workaround. We can't delete the previous render sync object until we are
502 // sure that the presentation is done
503 if (frame->present_fence) {
504 glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
505 }
506
507 // delete the draw fence if the frame wasn't presented
508 if (frame->render_fence) {
509 glDeleteSync(frame->render_fence);
510 frame->render_fence = nullptr;
511 }
512
513 // wait for the presentation to be done
514 if (frame->present_fence) {
515 glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
516 glDeleteSync(frame->present_fence);
517 frame->present_fence = nullptr;
518 }
519 }
520
521 {
522 MICROPROFILE_SCOPE(OpenGL_RenderFrame);
523 // Recreate the frame if the size of the window has changed
524 if (layout.width != frame->width || layout.height != frame->height) {
525 LOG_DEBUG(Render_OpenGL, "Reloading render frame");
526 mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
527 }
528
529 GLuint render_texture = frame->color.handle;
530 state.draw.draw_framebuffer = frame->render.handle;
531 state.Apply();
532 DrawScreens(layout, flipped);
533 // Create a fence for the frontend to wait on and swap this frame to OffTex
534 frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
535 glFlush();
536 mailbox->ReleaseRenderFrame(frame);
537 }
538 }
539
540 /**
541 * Loads framebuffer from emulated memory into the active OpenGL texture.
542 */
543 void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
544 ScreenInfo& screen_info, bool right_eye) {
545
546 if (framebuffer.address_right1 == 0 || framebuffer.address_right2 == 0)
547 right_eye = false;
548
549 const PAddr framebuffer_addr =
550 framebuffer.active_fb == 0
551 ? (!right_eye ? framebuffer.address_left1 : framebuffer.address_right1)
552 : (!right_eye ? framebuffer.address_left2 : framebuffer.address_right2);
553
554 LOG_TRACE(Render_OpenGL, "0x{:08x} bytes from 0x{:08x}({}x{}), fmt {:x}",
555 framebuffer.stride * framebuffer.height, framebuffer_addr, framebuffer.width.Value(),
556 framebuffer.height.Value(), framebuffer.format);
557
558 int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format);
559 std::size_t pixel_stride = framebuffer.stride / bpp;
560
561 // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately
562 ASSERT(pixel_stride * bpp == framebuffer.stride);
563
564 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
565 // only allows rows to have a memory alignement of 4.
566 ASSERT(pixel_stride % 4 == 0);
567
568 if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr,
569 static_cast<u32>(pixel_stride), screen_info)) {
570 // Reset the screen info's display texture to its own permanent texture
571 screen_info.display_texture = screen_info.texture.resource.handle;
572 screen_info.display_texcoords = Common::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
573
574 Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height);
575
576 const u8* framebuffer_data = VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr);
577
578 state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
579 state.Apply();
580
581 glActiveTexture(GL_TEXTURE0);
582 glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride);
583
584 // Update existing texture
585 // TODO: Test what happens on hardware when you change the framebuffer dimensions so that
586 // they differ from the LCD resolution.
587 // TODO: Applications could theoretically crash Citra here by specifying too large
588 // framebuffer sizes. We should make sure that this cannot happen.
589 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height,
590 screen_info.texture.gl_format, screen_info.texture.gl_type,
591 framebuffer_data);
592
593 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
594
595 state.texture_units[0].texture_2d = 0;
596 state.Apply();
597 }
598 }
599
600 /**
601 * Fills active OpenGL texture with the given RGB color. Since the color is solid, the texture can
602 * be 1x1 but will stretch across whatever it's rendered on.
603 */
LoadColorToActiveGLTexture(u8 color_r,u8 color_g,u8 color_b,const TextureInfo & texture)604 void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
605 const TextureInfo& texture) {
606 state.texture_units[0].texture_2d = texture.resource.handle;
607 state.Apply();
608
609 glActiveTexture(GL_TEXTURE0);
610 u8 framebuffer_data[3] = {color_r, color_g, color_b};
611
612 // Update existing texture
613 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data);
614
615 state.texture_units[0].texture_2d = 0;
616 state.Apply();
617 }
618
619 /**
620 * Initializes the OpenGL state and creates persistent objects.
621 */
622 void RendererOpenGL::InitOpenGLObjects() {
623 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
624 0.0f);
625
626 filter_sampler.Create();
627 ReloadSampler();
628
629 ReloadShader();
630
631 // Generate VBO handle for drawing
632 vertex_buffer.Create();
633
634 // Generate VAO
635 vertex_array.Create();
636
637 state.draw.vertex_array = vertex_array.handle;
638 state.draw.vertex_buffer = vertex_buffer.handle;
639 state.draw.uniform_buffer = 0;
640 state.Apply();
641
642 // Attach vertex data to VAO
643 glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
644 glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex),
645 (GLvoid*)offsetof(ScreenRectVertex, position));
646 glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex),
647 (GLvoid*)offsetof(ScreenRectVertex, tex_coord));
648 glEnableVertexAttribArray(attrib_position);
649 glEnableVertexAttribArray(attrib_tex_coord);
650
651 // Allocate textures for each screen
652 for (auto& screen_info : screen_infos) {
653 screen_info.texture.resource.Create();
654
655 // Allocation of storage is deferred until the first frame, when we
656 // know the framebuffer size.
657
658 state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
659 state.Apply();
660
661 glActiveTexture(GL_TEXTURE0);
662 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
663 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
664 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
665 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
666 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
667
668 screen_info.display_texture = screen_info.texture.resource.handle;
669 }
670
671 state.texture_units[0].texture_2d = 0;
672 state.Apply();
673 }
674
675 void RendererOpenGL::ReloadSampler() {
676 glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MIN_FILTER,
677 Settings::values.filter_mode ? GL_LINEAR : GL_NEAREST);
678 glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MAG_FILTER,
679 Settings::values.filter_mode ? GL_LINEAR : GL_NEAREST);
680 glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
681 glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
682 }
683
684 void RendererOpenGL::ReloadShader() {
685 // Link shaders and get variable locations
686 std::string shader_data;
687 if (GLES) {
688 shader_data += fragment_shader_precision_OES;
689 }
690 if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph) {
691 if (Settings::values.pp_shader_name == "dubois (builtin)") {
692 shader_data += fragment_shader_anaglyph;
693 } else {
694 std::string shader_text =
695 OpenGL::GetPostProcessingShaderCode(true, Settings::values.pp_shader_name);
696 if (shader_text.empty()) {
697 // Should probably provide some information that the shader couldn't load
698 shader_data += fragment_shader_anaglyph;
699 } else {
700 shader_data += shader_text;
701 }
702 }
703 } else if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
704 Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) {
705 if (Settings::values.pp_shader_name == "horizontal (builtin)") {
706 shader_data += fragment_shader_interlaced;
707 } else {
708 std::string shader_text =
709 OpenGL::GetPostProcessingShaderCode(true, Settings::values.pp_shader_name);
710 if (shader_text.empty()) {
711 // Should probably provide some information that the shader couldn't load
712 shader_data += fragment_shader_interlaced;
713 } else {
714 shader_data += shader_text;
715 }
716 }
717 } else {
718 if (Settings::values.pp_shader_name == "none (builtin)") {
719 shader_data += fragment_shader;
720 } else {
721 std::string shader_text =
722 OpenGL::GetPostProcessingShaderCode(false, Settings::values.pp_shader_name);
723 if (shader_text.empty()) {
724 // Should probably provide some information that the shader couldn't load
725 shader_data += fragment_shader;
726 } else {
727 shader_data += shader_text;
728 }
729 }
730 }
731 shader.Create(vertex_shader, shader_data.c_str());
732 state.draw.shader_program = shader.handle;
733 state.Apply();
734 uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
735 uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture");
736 if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
737 Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
738 Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) {
739 uniform_color_texture_r = glGetUniformLocation(shader.handle, "color_texture_r");
740 }
741 if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
742 Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) {
743 GLuint uniform_reverse_interlaced =
744 glGetUniformLocation(shader.handle, "reverse_interlaced");
745 if (Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced)
746 glUniform1i(uniform_reverse_interlaced, 1);
747 else
748 glUniform1i(uniform_reverse_interlaced, 0);
749 }
750 uniform_i_resolution = glGetUniformLocation(shader.handle, "i_resolution");
751 uniform_o_resolution = glGetUniformLocation(shader.handle, "o_resolution");
752 uniform_layer = glGetUniformLocation(shader.handle, "layer");
753 attrib_position = glGetAttribLocation(shader.handle, "vert_position");
754 attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord");
755 }
756
757 void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
758 const GPU::Regs::FramebufferConfig& framebuffer) {
759 GPU::Regs::PixelFormat format = framebuffer.color_format;
760 GLint internal_format;
761
762 texture.format = format;
763 texture.width = framebuffer.width;
764 texture.height = framebuffer.height;
765
766 switch (format) {
767 case GPU::Regs::PixelFormat::RGBA8:
768 internal_format = GL_RGBA;
769 texture.gl_format = GL_RGBA;
770 texture.gl_type = GLES ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_8_8_8_8;
771 break;
772
773 case GPU::Regs::PixelFormat::RGB8:
774 // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every
775 // specific OpenGL type used in this function using native-endian (that is, little-endian
776 // mostly everywhere) for words or half-words.
777 // TODO: check how those behave on big-endian processors.
778 internal_format = GL_RGB;
779
780 // GLES Dosen't support BGR , Use RGB instead
781 texture.gl_format = GLES ? GL_RGB : GL_BGR;
782 texture.gl_type = GL_UNSIGNED_BYTE;
783 break;
784
785 case GPU::Regs::PixelFormat::RGB565:
786 internal_format = GL_RGB;
787 texture.gl_format = GL_RGB;
788 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
789 break;
790
791 case GPU::Regs::PixelFormat::RGB5A1:
792 internal_format = GL_RGBA;
793 texture.gl_format = GL_RGBA;
794 texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1;
795 break;
796
797 case GPU::Regs::PixelFormat::RGBA4:
798 internal_format = GL_RGBA;
799 texture.gl_format = GL_RGBA;
800 texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4;
801 break;
802
803 default:
804 UNIMPLEMENTED();
805 }
806
807 state.texture_units[0].texture_2d = texture.resource.handle;
808 state.Apply();
809
810 glActiveTexture(GL_TEXTURE0);
811 glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
812 texture.gl_format, texture.gl_type, nullptr);
813
814 state.texture_units[0].texture_2d = 0;
815 state.Apply();
816 }
817
818 /**
819 * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
820 * rotation.
821 */
822 void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y,
823 float w, float h) {
824 const auto& texcoords = screen_info.display_texcoords;
825
826 const std::array<ScreenRectVertex, 4> vertices = {{
827 ScreenRectVertex(x, y, texcoords.bottom, texcoords.left),
828 ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.right),
829 ScreenRectVertex(x, y + h, texcoords.top, texcoords.left),
830 ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.right),
831 }};
832
833 // As this is the "DrawSingleScreenRotated" function, the output resolution dimensions have been
834 // swapped. If a non-rotated draw-screen function were to be added for book-mode games, those
835 // should probably be set to the standard (w, h, 1.0 / w, 1.0 / h) ordering.
836 const u16 scale_factor = VideoCore::GetResolutionScaleFactor();
837 glUniform4f(uniform_i_resolution, static_cast<float>(screen_info.texture.width * scale_factor),
838 static_cast<float>(screen_info.texture.height * scale_factor),
839 1.0f / static_cast<float>(screen_info.texture.width * scale_factor),
840 1.0f / static_cast<float>(screen_info.texture.height * scale_factor));
841 glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w);
842 state.texture_units[0].texture_2d = screen_info.display_texture;
843 state.texture_units[0].sampler = filter_sampler.handle;
844 state.Apply();
845
846 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
847 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
848
849 state.texture_units[0].texture_2d = 0;
850 state.texture_units[0].sampler = 0;
851 state.Apply();
852 }
853
854 void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w,
855 float h) {
856 const auto& texcoords = screen_info.display_texcoords;
857
858 const std::array<ScreenRectVertex, 4> vertices = {{
859 ScreenRectVertex(x, y, texcoords.bottom, texcoords.right),
860 ScreenRectVertex(x + w, y, texcoords.top, texcoords.right),
861 ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.left),
862 ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.left),
863 }};
864
865 const u16 scale_factor = VideoCore::GetResolutionScaleFactor();
866 glUniform4f(uniform_i_resolution, static_cast<float>(screen_info.texture.width * scale_factor),
867 static_cast<float>(screen_info.texture.height * scale_factor),
868 1.0f / static_cast<float>(screen_info.texture.width * scale_factor),
869 1.0f / static_cast<float>(screen_info.texture.height * scale_factor));
870 glUniform4f(uniform_o_resolution, w, h, 1.0f / w, 1.0f / h);
871 state.texture_units[0].texture_2d = screen_info.display_texture;
872 state.texture_units[0].sampler = filter_sampler.handle;
873 state.Apply();
874
875 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
876 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
877
878 state.texture_units[0].texture_2d = 0;
879 state.texture_units[0].sampler = 0;
880 state.Apply();
881 }
882
883 /**
884 * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
885 * rotation.
886 */
887 void RendererOpenGL::DrawSingleScreenStereoRotated(const ScreenInfo& screen_info_l,
888 const ScreenInfo& screen_info_r, float x,
889 float y, float w, float h) {
890 const auto& texcoords = screen_info_l.display_texcoords;
891
892 const std::array<ScreenRectVertex, 4> vertices = {{
893 ScreenRectVertex(x, y, texcoords.bottom, texcoords.left),
894 ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.right),
895 ScreenRectVertex(x, y + h, texcoords.top, texcoords.left),
896 ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.right),
897 }};
898
899 const u16 scale_factor = VideoCore::GetResolutionScaleFactor();
900 glUniform4f(uniform_i_resolution,
901 static_cast<float>(screen_info_l.texture.width * scale_factor),
902 static_cast<float>(screen_info_l.texture.height * scale_factor),
903 1.0f / static_cast<float>(screen_info_l.texture.width * scale_factor),
904 1.0f / static_cast<float>(screen_info_l.texture.height * scale_factor));
905 glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w);
906 state.texture_units[0].texture_2d = screen_info_l.display_texture;
907 state.texture_units[1].texture_2d = screen_info_r.display_texture;
908 state.texture_units[0].sampler = filter_sampler.handle;
909 state.texture_units[1].sampler = filter_sampler.handle;
910 state.Apply();
911
912 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
913 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
914
915 state.texture_units[0].texture_2d = 0;
916 state.texture_units[1].texture_2d = 0;
917 state.texture_units[0].sampler = 0;
918 state.texture_units[1].sampler = 0;
919 state.Apply();
920 }
921
922 void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l,
923 const ScreenInfo& screen_info_r, float x, float y,
924 float w, float h) {
925 const auto& texcoords = screen_info_l.display_texcoords;
926
927 const std::array<ScreenRectVertex, 4> vertices = {{
928 ScreenRectVertex(x, y, texcoords.bottom, texcoords.right),
929 ScreenRectVertex(x + w, y, texcoords.top, texcoords.right),
930 ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.left),
931 ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.left),
932 }};
933
934 const u16 scale_factor = VideoCore::GetResolutionScaleFactor();
935 glUniform4f(uniform_i_resolution,
936 static_cast<float>(screen_info_l.texture.width * scale_factor),
937 static_cast<float>(screen_info_l.texture.height * scale_factor),
938 1.0f / static_cast<float>(screen_info_l.texture.width * scale_factor),
939 1.0f / static_cast<float>(screen_info_l.texture.height * scale_factor));
940 glUniform4f(uniform_o_resolution, w, h, 1.0f / w, 1.0f / h);
941 state.texture_units[0].texture_2d = screen_info_l.display_texture;
942 state.texture_units[1].texture_2d = screen_info_r.display_texture;
943 state.texture_units[0].sampler = filter_sampler.handle;
944 state.texture_units[1].sampler = filter_sampler.handle;
945 state.Apply();
946
947 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
948 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
949
950 state.texture_units[0].texture_2d = 0;
951 state.texture_units[1].texture_2d = 0;
952 state.texture_units[0].sampler = 0;
953 state.texture_units[1].sampler = 0;
954 state.Apply();
955 }
956
957 /**
958 * Draws the emulated screens to the emulator window.
959 */
960 void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool flipped) {
961 if (VideoCore::g_renderer_bg_color_update_requested.exchange(false)) {
962 // Update background color before drawing
963 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
964 0.0f);
965 }
966
967 if (VideoCore::g_renderer_sampler_update_requested.exchange(false)) {
968 // Set the new filtering mode for the sampler
969 ReloadSampler();
970 }
971
972 if (VideoCore::g_renderer_shader_update_requested.exchange(false)) {
973 // Update fragment shader before drawing
974 shader.Release();
975 // Link shaders and get variable locations
976 ReloadShader();
977 }
978
979 const auto& top_screen = layout.top_screen;
980 const auto& bottom_screen = layout.bottom_screen;
981
982 glViewport(0, 0, layout.width, layout.height);
983 glClear(GL_COLOR_BUFFER_BIT);
984
985 // Set projection matrix
986 std::array<GLfloat, 3 * 2> ortho_matrix =
987 MakeOrthographicMatrix((float)layout.width, (float)layout.height, flipped);
988 glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
989
990 // Bind texture in Texture Unit 0
991 glUniform1i(uniform_color_texture, 0);
992
993 const bool stereo_single_screen =
994 Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph ||
995 Settings::values.render_3d == Settings::StereoRenderOption::Interlaced ||
996 Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced;
997
998 // Bind a second texture for the right eye if in Anaglyph mode
999 if (stereo_single_screen) {
1000 glUniform1i(uniform_color_texture_r, 1);
1001 }
1002
1003 glUniform1i(uniform_layer, 0);
1004 if (layout.top_screen_enabled) {
1005 if (layout.is_rotated) {
1006 if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
1007 DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left,
1008 (float)top_screen.top, (float)top_screen.GetWidth(),
1009 (float)top_screen.GetHeight());
1010 } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
1011 DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2,
1012 (float)top_screen.top, (float)top_screen.GetWidth() / 2,
1013 (float)top_screen.GetHeight());
1014 glUniform1i(uniform_layer, 1);
1015 DrawSingleScreenRotated(screen_infos[1],
1016 ((float)top_screen.left / 2) + ((float)layout.width / 2),
1017 (float)top_screen.top, (float)top_screen.GetWidth() / 2,
1018 (float)top_screen.GetHeight());
1019 } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
1020 DrawSingleScreenRotated(screen_infos[0], layout.top_screen.left,
1021 layout.top_screen.top, layout.top_screen.GetWidth(),
1022 layout.top_screen.GetHeight());
1023 glUniform1i(uniform_layer, 1);
1024 DrawSingleScreenRotated(screen_infos[1],
1025 layout.cardboard.top_screen_right_eye +
1026 ((float)layout.width / 2),
1027 layout.top_screen.top, layout.top_screen.GetWidth(),
1028 layout.top_screen.GetHeight());
1029 } else if (stereo_single_screen) {
1030 DrawSingleScreenStereoRotated(
1031 screen_infos[0], screen_infos[1], (float)top_screen.left, (float)top_screen.top,
1032 (float)top_screen.GetWidth(), (float)top_screen.GetHeight());
1033 }
1034 } else {
1035 if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
1036 DrawSingleScreen(screen_infos[0], (float)top_screen.left, (float)top_screen.top,
1037 (float)top_screen.GetWidth(), (float)top_screen.GetHeight());
1038 } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
1039 DrawSingleScreen(screen_infos[0], (float)top_screen.left / 2, (float)top_screen.top,
1040 (float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight());
1041 glUniform1i(uniform_layer, 1);
1042 DrawSingleScreen(screen_infos[1],
1043 ((float)top_screen.left / 2) + ((float)layout.width / 2),
1044 (float)top_screen.top, (float)top_screen.GetWidth() / 2,
1045 (float)top_screen.GetHeight());
1046 } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
1047 DrawSingleScreen(screen_infos[0], layout.top_screen.left, layout.top_screen.top,
1048 layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
1049 glUniform1i(uniform_layer, 1);
1050 DrawSingleScreen(screen_infos[1],
1051 layout.cardboard.top_screen_right_eye + ((float)layout.width / 2),
1052 layout.top_screen.top, layout.top_screen.GetWidth(),
1053 layout.top_screen.GetHeight());
1054 } else if (stereo_single_screen) {
1055 DrawSingleScreenStereo(screen_infos[0], screen_infos[1], (float)top_screen.left,
1056 (float)top_screen.top, (float)top_screen.GetWidth(),
1057 (float)top_screen.GetHeight());
1058 }
1059 }
1060 }
1061 glUniform1i(uniform_layer, 0);
1062 if (layout.bottom_screen_enabled) {
1063 if (layout.is_rotated) {
1064 if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
1065 DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left,
1066 (float)bottom_screen.top, (float)bottom_screen.GetWidth(),
1067 (float)bottom_screen.GetHeight());
1068 } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
1069 DrawSingleScreenRotated(
1070 screen_infos[2], (float)bottom_screen.left / 2, (float)bottom_screen.top,
1071 (float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight());
1072 glUniform1i(uniform_layer, 1);
1073 DrawSingleScreenRotated(
1074 screen_infos[2], ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
1075 (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
1076 (float)bottom_screen.GetHeight());
1077 } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
1078 DrawSingleScreenRotated(screen_infos[2], layout.bottom_screen.left,
1079 layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
1080 layout.bottom_screen.GetHeight());
1081 glUniform1i(uniform_layer, 1);
1082 DrawSingleScreenRotated(screen_infos[2],
1083 layout.cardboard.bottom_screen_right_eye +
1084 ((float)layout.width / 2),
1085 layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
1086 layout.bottom_screen.GetHeight());
1087 } else if (stereo_single_screen) {
1088 DrawSingleScreenStereoRotated(screen_infos[2], screen_infos[2],
1089 (float)bottom_screen.left, (float)bottom_screen.top,
1090 (float)bottom_screen.GetWidth(),
1091 (float)bottom_screen.GetHeight());
1092 }
1093 } else {
1094 if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
1095 DrawSingleScreen(screen_infos[2], (float)bottom_screen.left,
1096 (float)bottom_screen.top, (float)bottom_screen.GetWidth(),
1097 (float)bottom_screen.GetHeight());
1098 } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
1099 DrawSingleScreen(screen_infos[2], (float)bottom_screen.left / 2,
1100 (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
1101 (float)bottom_screen.GetHeight());
1102 glUniform1i(uniform_layer, 1);
1103 DrawSingleScreen(screen_infos[2],
1104 ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
1105 (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
1106 (float)bottom_screen.GetHeight());
1107 } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
1108 DrawSingleScreen(screen_infos[2], layout.bottom_screen.left,
1109 layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
1110 layout.bottom_screen.GetHeight());
1111 glUniform1i(uniform_layer, 1);
1112 DrawSingleScreen(screen_infos[2],
1113 layout.cardboard.bottom_screen_right_eye +
1114 ((float)layout.width / 2),
1115 layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
1116 layout.bottom_screen.GetHeight());
1117 } else if (stereo_single_screen) {
1118 DrawSingleScreenStereo(screen_infos[2], screen_infos[2], (float)bottom_screen.left,
1119 (float)bottom_screen.top, (float)bottom_screen.GetWidth(),
1120 (float)bottom_screen.GetHeight());
1121 }
1122 }
1123 }
1124 }
1125
1126 void RendererOpenGL::TryPresent(int timeout_ms) {
1127 const auto& layout = render_window.GetFramebufferLayout();
1128 auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms);
1129 if (!frame) {
1130 LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
1131 return;
1132 }
1133
1134 // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
1135 // readback since we won't be doing any blending
1136 glClear(GL_COLOR_BUFFER_BIT);
1137
1138 // Recreate the presentation FBO if the color attachment was changed
1139 if (frame->color_reloaded) {
1140 LOG_DEBUG(Render_OpenGL, "Reloading present frame");
1141 render_window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
1142 }
1143 glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
1144 // INTEL workaround.
1145 // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
1146 // it on the emulation thread without too much penalty
1147 // glDeleteSync(frame.render_sync);
1148 // frame.render_sync = 0;
1149
1150 glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
1151 glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
1152 GL_COLOR_BUFFER_BIT, GL_LINEAR);
1153
1154 // Delete the fence if we're re-presenting to avoid leaking fences
1155 if (frame->present_fence) {
1156 glDeleteSync(frame->present_fence);
1157 }
1158
1159 /* insert fence for the main thread to block on */
1160 frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1161 glFlush();
1162
1163 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
1164 }
1165
1166 /// Updates the framerate
1167 void RendererOpenGL::UpdateFramerate() {}
1168
1169 void RendererOpenGL::PrepareVideoDumping() {
1170 auto* mailbox = static_cast<OGLVideoDumpingMailbox*>(frame_dumper.mailbox.get());
1171 {
1172 std::unique_lock lock(mailbox->swap_chain_lock);
1173 mailbox->quit = false;
1174 }
1175 frame_dumper.StartDumping();
1176 }
1177
1178 void RendererOpenGL::CleanupVideoDumping() {
1179 frame_dumper.StopDumping();
1180 auto* mailbox = static_cast<OGLVideoDumpingMailbox*>(frame_dumper.mailbox.get());
1181 {
1182 std::unique_lock lock(mailbox->swap_chain_lock);
1183 mailbox->quit = true;
1184 }
1185 mailbox->free_cv.notify_one();
1186 }
1187
1188 static const char* GetSource(GLenum source) {
1189 #define RET(s) \
1190 case GL_DEBUG_SOURCE_##s: \
1191 return #s
1192 switch (source) {
1193 RET(API);
1194 RET(WINDOW_SYSTEM);
1195 RET(SHADER_COMPILER);
1196 RET(THIRD_PARTY);
1197 RET(APPLICATION);
1198 RET(OTHER);
1199 default:
1200 UNREACHABLE();
1201 }
1202 #undef RET
1203 }
1204
1205 static const char* GetType(GLenum type) {
1206 #define RET(t) \
1207 case GL_DEBUG_TYPE_##t: \
1208 return #t
1209 switch (type) {
1210 RET(ERROR);
1211 RET(DEPRECATED_BEHAVIOR);
1212 RET(UNDEFINED_BEHAVIOR);
1213 RET(PORTABILITY);
1214 RET(PERFORMANCE);
1215 RET(OTHER);
1216 RET(MARKER);
1217 default:
1218 UNREACHABLE();
1219 }
1220 #undef RET
1221 }
1222
1223 static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity,
1224 GLsizei length, const GLchar* message, const void* user_param) {
1225 Log::Level level;
1226 switch (severity) {
1227 case GL_DEBUG_SEVERITY_HIGH:
1228 level = Log::Level::Critical;
1229 break;
1230 case GL_DEBUG_SEVERITY_MEDIUM:
1231 level = Log::Level::Warning;
1232 break;
1233 case GL_DEBUG_SEVERITY_NOTIFICATION:
1234 case GL_DEBUG_SEVERITY_LOW:
1235 level = Log::Level::Debug;
1236 break;
1237 }
1238 LOG_GENERIC(Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), GetType(type),
1239 id, message);
1240 }
1241
1242 /// Initialize the renderer
Init()1243 VideoCore::ResultStatus RendererOpenGL::Init() {
1244 #ifndef ANDROID
1245 if (!gladLoadGL()) {
1246 return VideoCore::ResultStatus::ErrorBelowGL33;
1247 }
1248
1249 // Qualcomm has some spammy info messages that are marked as errors but not important
1250 // https://developer.qualcomm.com/comment/11845
1251 if (GLAD_GL_KHR_debug) {
1252 glEnable(GL_DEBUG_OUTPUT);
1253 glDebugMessageCallback(DebugHandler, nullptr);
1254 }
1255 #endif
1256
1257 const char* gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
1258 const char* gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
1259 const char* gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))};
1260
1261 LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version);
1262 LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
1263 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
1264
1265 auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
1266 constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
1267 telemetry_session.AddField(user_system, "GPU_Vendor", std::string(gpu_vendor));
1268 telemetry_session.AddField(user_system, "GPU_Model", std::string(gpu_model));
1269 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
1270
1271 if (!strcmp(gpu_vendor, "GDI Generic")) {
1272 return VideoCore::ResultStatus::ErrorGenericDrivers;
1273 }
1274
1275 if (!(GLAD_GL_VERSION_3_3 || GLAD_GL_ES_VERSION_3_1)) {
1276 return VideoCore::ResultStatus::ErrorBelowGL33;
1277 }
1278
1279 InitOpenGLObjects();
1280
1281 RefreshRasterizerSetting();
1282
1283 return VideoCore::ResultStatus::Success;
1284 }
1285
1286 /// Shutdown the renderer
ShutDown()1287 void RendererOpenGL::ShutDown() {}
1288
1289 } // namespace OpenGL
1290