summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2018-01-10 22:43:17 -0500
committerGravatar bunnei2018-01-10 23:28:59 -0500
commitee4691297f2f030cb112b2467db4ada142e2ba80 (patch)
tree55c36da6343dd4e70641bc11cf4340013406b81c /src
parentrender_base: Add a struct describing framebuffer metadata. (diff)
downloadyuzu-ee4691297f2f030cb112b2467db4ada142e2ba80.tar.gz
yuzu-ee4691297f2f030cb112b2467db4ada142e2ba80.tar.xz
yuzu-ee4691297f2f030cb112b2467db4ada142e2ba80.zip
renderer_opengl: Support rendering Switch framebuffer.
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_base.h7
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp197
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h17
3 files changed, 83 insertions, 138 deletions
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index f18917da3..28015aba9 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -15,7 +15,10 @@ public:
15 /// Used to reference a framebuffer 15 /// Used to reference a framebuffer
16 enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture }; 16 enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture };
17 17
18 /// Struct describing framebuffer metadata 18 /**
19 * Struct describing framebuffer metadata
20 * TODO(bunnei): This struct belongs in the GPU code, but we don't have a good place for it yet.
21 */
19 struct FramebufferInfo { 22 struct FramebufferInfo {
20 enum class PixelFormat : u32 { 23 enum class PixelFormat : u32 {
21 ABGR8 = 1, 24 ABGR8 = 1,
@@ -44,7 +47,7 @@ public:
44 virtual ~RendererBase() {} 47 virtual ~RendererBase() {}
45 48
46 /// Swap buffers (render frame) 49 /// Swap buffers (render frame)
47 virtual void SwapBuffers() = 0; 50 virtual void SwapBuffers(const FramebufferInfo& framebuffer_info) = 0;
48 51
49 /** 52 /**
50 * Set the emulator window to use for renderer 53 * Set the emulator window to use for renderer
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index c481d1d76..de61987a8 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -56,7 +56,9 @@ out vec4 color;
56uniform sampler2D color_texture; 56uniform sampler2D color_texture;
57 57
58void main() { 58void main() {
59 color = texture(color_texture, frag_tex_coord); 59 // Swap RGBA -> ABGR so we don't have to do this on the CPU. This needs to change if we have to
60 // support more framebuffer pixel formats.
61 color = texture(color_texture, frag_tex_coord).abgr;
60} 62}
61)"; 63)";
62 64
@@ -98,44 +100,20 @@ RendererOpenGL::RendererOpenGL() = default;
98RendererOpenGL::~RendererOpenGL() = default; 100RendererOpenGL::~RendererOpenGL() = default;
99 101
100/// Swap buffers (render frame) 102/// Swap buffers (render frame)
101void RendererOpenGL::SwapBuffers() { 103void RendererOpenGL::SwapBuffers(const FramebufferInfo& framebuffer_info) {
102 // Maintain the rasterizer's state as a priority 104 // Maintain the rasterizer's state as a priority
103 OpenGLState prev_state = OpenGLState::GetCurState(); 105 OpenGLState prev_state = OpenGLState::GetCurState();
104 state.Apply(); 106 state.Apply();
105 107
106 for (int i : {0, 1}) { 108 if (screen_info.texture.width != (GLsizei)framebuffer_info.width ||
107 const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; 109 screen_info.texture.height != (GLsizei)framebuffer_info.height ||
108 110 screen_info.texture.pixel_format != framebuffer_info.pixel_format) {
109 // Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04 111 // Reallocate texture if the framebuffer size has changed.
110 u32 lcd_color_addr = 112 // This is expected to not happen very often and hence should not be a
111 (i == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom); 113 // performance problem.
112 lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr; 114 ConfigureFramebufferTexture(screen_info.texture, framebuffer_info);
113 LCD::Regs::ColorFill color_fill = {0};
114 LCD::Read(color_fill.raw, lcd_color_addr);
115
116 if (color_fill.is_enabled) {
117 LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b,
118 screen_infos[i].texture);
119
120 // Resize the texture in case the framebuffer size has changed
121 screen_infos[i].texture.width = 1;
122 screen_infos[i].texture.height = 1;
123 } else {
124 if (screen_infos[i].texture.width != (GLsizei)framebuffer.width ||
125 screen_infos[i].texture.height != (GLsizei)framebuffer.height ||
126 screen_infos[i].texture.format != framebuffer.color_format) {
127 // Reallocate texture if the framebuffer size has changed.
128 // This is expected to not happen very often and hence should not be a
129 // performance problem.
130 ConfigureFramebufferTexture(screen_infos[i].texture, framebuffer);
131 }
132 LoadFBToScreenInfo(framebuffer, screen_infos[i]);
133
134 // Resize the texture in case the framebuffer size has changed
135 screen_infos[i].texture.width = framebuffer.width;
136 screen_infos[i].texture.height = framebuffer.height;
137 }
138 } 115 }
116 LoadFBToScreenInfo(framebuffer_info, screen_info);
139 117
140 DrawScreens(); 118 DrawScreens();
141 119
@@ -270,56 +248,48 @@ static void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32
270/** 248/**
271 * Loads framebuffer from emulated memory into the active OpenGL texture. 249 * Loads framebuffer from emulated memory into the active OpenGL texture.
272 */ 250 */
273void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer, 251void RendererOpenGL::LoadFBToScreenInfo(const FramebufferInfo& framebuffer_info,
274 ScreenInfo& screen_info) { 252 ScreenInfo& screen_info) {
253 const u32 bpp{FramebufferInfo::BytesPerPixel(framebuffer_info.pixel_format)};
254 const u32 size_in_bytes{framebuffer_info.stride * framebuffer_info.height * bpp};
275 255
276 const PAddr framebuffer_addr = 256 MortonCopyPixels128(framebuffer_info.width, framebuffer_info.height, bpp, 4,
277 framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2; 257 Memory::GetPointer(framebuffer_info.address), gl_framebuffer_data.data(),
278 258 true);
279 LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
280 framebuffer.stride * framebuffer.height, framebuffer_addr, (int)framebuffer.width,
281 (int)framebuffer.height, (int)framebuffer.format);
282 259
283 int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); 260 LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%llx(%dx%d), fmt %x", size_in_bytes,
284 size_t pixel_stride = framebuffer.stride / bpp; 261 framebuffer_info.address, framebuffer_info.width, framebuffer_info.height,
285 262 (int)framebuffer_info.format);
286 // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately
287 ASSERT(pixel_stride * bpp == framebuffer.stride);
288 263
289 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default 264 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
290 // only allows rows to have a memory alignement of 4. 265 // only allows rows to have a memory alignement of 4.
291 ASSERT(pixel_stride % 4 == 0); 266 ASSERT(framebuffer_info.stride % 4 == 0);
292
293 if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr,
294 static_cast<u32>(pixel_stride), screen_info)) {
295 // Reset the screen info's display texture to its own permanent texture
296 screen_info.display_texture = screen_info.texture.resource.handle;
297 screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
298 267
299 Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); 268 // Reset the screen info's display texture to its own permanent texture
269 screen_info.display_texture = screen_info.texture.resource.handle;
270 screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
300 271
301 const u8* framebuffer_data = Memory::GetPhysicalPointer(framebuffer_addr); 272 Memory::RasterizerFlushRegion(framebuffer_info.address, size_in_bytes);
302 273
303 state.texture_units[0].texture_2d = screen_info.texture.resource.handle; 274 state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
304 state.Apply(); 275 state.Apply();
305 276
306 glActiveTexture(GL_TEXTURE0); 277 glActiveTexture(GL_TEXTURE0);
307 glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); 278 glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)framebuffer_info.stride);
308 279
309 // Update existing texture 280 // Update existing texture
310 // TODO: Test what happens on hardware when you change the framebuffer dimensions so that 281 // TODO: Test what happens on hardware when you change the framebuffer dimensions so that
311 // they differ from the LCD resolution. 282 // they differ from the LCD resolution.
312 // TODO: Applications could theoretically crash Citra here by specifying too large 283 // TODO: Applications could theoretically crash Citra here by specifying too large
313 // framebuffer sizes. We should make sure that this cannot happen. 284 // framebuffer sizes. We should make sure that this cannot happen.
314 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, 285 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer_info.width, framebuffer_info.height,
315 screen_info.texture.gl_format, screen_info.texture.gl_type, 286 screen_info.texture.gl_format, screen_info.texture.gl_type,
316 framebuffer_data); 287 gl_framebuffer_data.data());
317 288
318 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 289 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
319 290
320 state.texture_units[0].texture_2d = 0; 291 state.texture_units[0].texture_2d = 0;
321 state.Apply(); 292 state.Apply();
322 }
323} 293}
324 294
325/** 295/**
@@ -377,74 +347,43 @@ void RendererOpenGL::InitOpenGLObjects() {
377 glEnableVertexAttribArray(attrib_position); 347 glEnableVertexAttribArray(attrib_position);
378 glEnableVertexAttribArray(attrib_tex_coord); 348 glEnableVertexAttribArray(attrib_tex_coord);
379 349
380 // Allocate textures for each screen 350 // Allocate textures for the screen
381 for (auto& screen_info : screen_infos) { 351 screen_info.texture.resource.Create();
382 screen_info.texture.resource.Create();
383 352
384 // Allocation of storage is deferred until the first frame, when we 353 // Allocation of storage is deferred until the first frame, when we
385 // know the framebuffer size. 354 // know the framebuffer size.
386 355
387 state.texture_units[0].texture_2d = screen_info.texture.resource.handle; 356 state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
388 state.Apply(); 357 state.Apply();
389 358
390 glActiveTexture(GL_TEXTURE0); 359 glActiveTexture(GL_TEXTURE0);
391 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); 360 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
392 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 361 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
393 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 362 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
394 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 363 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
395 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 364 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
396 365
397 screen_info.display_texture = screen_info.texture.resource.handle; 366 screen_info.display_texture = screen_info.texture.resource.handle;
398 }
399 367
400 state.texture_units[0].texture_2d = 0; 368 state.texture_units[0].texture_2d = 0;
401 state.Apply(); 369 state.Apply();
402} 370}
403 371
404void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, 372void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
405 const GPU::Regs::FramebufferConfig& framebuffer) { 373 const FramebufferInfo& framebuffer_info) {
406 GPU::Regs::PixelFormat format = framebuffer.color_format;
407 GLint internal_format;
408 374
409 texture.format = format; 375 texture.width = framebuffer_info.width;
410 texture.width = framebuffer.width; 376 texture.height = framebuffer_info.height;
411 texture.height = framebuffer.height;
412 377
413 switch (format) { 378 GLint internal_format;
414 case GPU::Regs::PixelFormat::RGBA8: 379 switch (framebuffer_info.pixel_format) {
380 case FramebufferInfo::PixelFormat::ABGR8:
381 // Use RGBA8 and swap in the fragment shader
415 internal_format = GL_RGBA; 382 internal_format = GL_RGBA;
416 texture.gl_format = GL_RGBA; 383 texture.gl_format = GL_RGBA;
417 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8; 384 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8;
385 gl_framebuffer_data.resize(texture.width * texture.height * 4);
418 break; 386 break;
419
420 case GPU::Regs::PixelFormat::RGB8:
421 // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every
422 // specific OpenGL type used in this function using native-endian (that is, little-endian
423 // mostly everywhere) for words or half-words.
424 // TODO: check how those behave on big-endian processors.
425 internal_format = GL_RGB;
426 texture.gl_format = GL_BGR;
427 texture.gl_type = GL_UNSIGNED_BYTE;
428 break;
429
430 case GPU::Regs::PixelFormat::RGB565:
431 internal_format = GL_RGB;
432 texture.gl_format = GL_RGB;
433 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
434 break;
435
436 case GPU::Regs::PixelFormat::RGB5A1:
437 internal_format = GL_RGBA;
438 texture.gl_format = GL_RGBA;
439 texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1;
440 break;
441
442 case GPU::Regs::PixelFormat::RGBA4:
443 internal_format = GL_RGBA;
444 texture.gl_format = GL_RGBA;
445 texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4;
446 break;
447
448 default: 387 default:
449 UNIMPLEMENTED(); 388 UNIMPLEMENTED();
450 } 389 }
@@ -465,10 +404,10 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl
465 auto& texcoords = screen_info.display_texcoords; 404 auto& texcoords = screen_info.display_texcoords;
466 405
467 std::array<ScreenRectVertex, 4> vertices = {{ 406 std::array<ScreenRectVertex, 4> vertices = {{
468 ScreenRectVertex(x, y, texcoords.top, texcoords.left), 407 ScreenRectVertex(x, y, texcoords.top, texcoords.right),
469 ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.left), 408 ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.right),
470 ScreenRectVertex(x, y + h, texcoords.top, texcoords.right), 409 ScreenRectVertex(x, y + h, texcoords.top, texcoords.left),
471 ScreenRectVertex(x + w, y + h, texcoords.bottom, texcoords.right), 410 ScreenRectVertex(x + w, y + h, texcoords.bottom, texcoords.left),
472 }}; 411 }};
473 412
474 state.texture_units[0].texture_2d = screen_info.display_texture; 413 state.texture_units[0].texture_2d = screen_info.display_texture;
@@ -500,8 +439,8 @@ void RendererOpenGL::DrawScreens() {
500 glActiveTexture(GL_TEXTURE0); 439 glActiveTexture(GL_TEXTURE0);
501 glUniform1i(uniform_color_texture, 0); 440 glUniform1i(uniform_color_texture, 0);
502 441
503 DrawSingleScreen(screen_infos[0], (float)screen.left, (float)screen.top, 442 DrawSingleScreen(screen_info, (float)screen.left, (float)screen.top, (float)screen.GetWidth(),
504 (float)screen.GetWidth(), (float)screen.GetHeight()); 443 (float)screen.GetHeight());
505 444
506 m_current_frame++; 445 m_current_frame++;
507} 446}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 111b78466..9d2bb8423 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -4,7 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <vector>
8#include <glad/glad.h> 8#include <glad/glad.h>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/math_util.h" 10#include "common/math_util.h"
@@ -20,9 +20,9 @@ struct TextureInfo {
20 OGLTexture resource; 20 OGLTexture resource;
21 GLsizei width; 21 GLsizei width;
22 GLsizei height; 22 GLsizei height;
23 GPU::Regs::PixelFormat format;
24 GLenum gl_format; 23 GLenum gl_format;
25 GLenum gl_type; 24 GLenum gl_type;
25 RendererBase::FramebufferInfo::PixelFormat pixel_format;
26}; 26};
27 27
28/// Structure used for storing information about the display target for each 3DS screen 28/// Structure used for storing information about the display target for each 3DS screen
@@ -38,7 +38,7 @@ public:
38 ~RendererOpenGL() override; 38 ~RendererOpenGL() override;
39 39
40 /// Swap buffers (render frame) 40 /// Swap buffers (render frame)
41 void SwapBuffers() override; 41 void SwapBuffers(const FramebufferInfo& framebuffer_info) override;
42 42
43 /** 43 /**
44 * Set the emulator window to use for renderer 44 * Set the emulator window to use for renderer
@@ -55,13 +55,13 @@ public:
55private: 55private:
56 void InitOpenGLObjects(); 56 void InitOpenGLObjects();
57 void ConfigureFramebufferTexture(TextureInfo& texture, 57 void ConfigureFramebufferTexture(TextureInfo& texture,
58 const GPU::Regs::FramebufferConfig& framebuffer); 58 const FramebufferInfo& framebuffer_info);
59 void DrawScreens(); 59 void DrawScreens();
60 void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h); 60 void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h);
61 void UpdateFramerate(); 61 void UpdateFramerate();
62 62
63 // Loads framebuffer from emulated memory into the display information structure 63 // Loads framebuffer from emulated memory into the display information structure
64 void LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer, 64 void LoadFBToScreenInfo(const FramebufferInfo& framebuffer_info,
65 ScreenInfo& screen_info); 65 ScreenInfo& screen_info);
66 // Fills active OpenGL texture with the given RGB color. 66 // Fills active OpenGL texture with the given RGB color.
67 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture); 67 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture);
@@ -75,8 +75,11 @@ private:
75 OGLBuffer vertex_buffer; 75 OGLBuffer vertex_buffer;
76 OGLShader shader; 76 OGLShader shader;
77 77
78 /// Display information for top and bottom screens respectively 78 /// Display information for Switch screen
79 std::array<ScreenInfo, 2> screen_infos; 79 ScreenInfo screen_info;
80
81 /// OpenGL framebuffer data
82 std::vector<u8> gl_framebuffer_data;
80 83
81 // Shader uniform location indices 84 // Shader uniform location indices
82 GLuint uniform_modelview_matrix; 85 GLuint uniform_modelview_matrix;