OGL renderer
This commit is contained in:
		| @@ -15,6 +15,7 @@ | |||||||
| #include "core/hw/lcd.h" | #include "core/hw/lcd.h" | ||||||
|  |  | ||||||
| #include "video_core/gpu_debugger.h" | #include "video_core/gpu_debugger.h" | ||||||
|  | #include "video_core/video_core.h" | ||||||
|  |  | ||||||
| // Main graphics debugger object - TODO: Here is probably not the best place for this | // Main graphics debugger object - TODO: Here is probably not the best place for this | ||||||
| GraphicsDebugger g_debugger; | GraphicsDebugger g_debugger; | ||||||
| @@ -264,6 +265,8 @@ static void FlushDataCache(Service::Interface* self) { | |||||||
|     u32 size    = cmd_buff[2]; |     u32 size    = cmd_buff[2]; | ||||||
|     u32 process = cmd_buff[4]; |     u32 process = cmd_buff[4]; | ||||||
|  |  | ||||||
|  |     VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(address), size); | ||||||
|  |  | ||||||
|     // TODO(purpasmart96): Verify return header on HW |     // TODO(purpasmart96): Verify return header on HW | ||||||
|  |  | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error |     cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||||||
| @@ -352,10 +355,16 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||||||
|  |  | ||||||
|     // GX request DMA - typically used for copying memory from GSP heap to VRAM |     // GX request DMA - typically used for copying memory from GSP heap to VRAM | ||||||
|     case CommandId::REQUEST_DMA: |     case CommandId::REQUEST_DMA: | ||||||
|  |         VideoCore::g_renderer->hw_rasterizer->NotifyPreCopy(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), | ||||||
|  |                                                            command.dma_request.size); | ||||||
|  |  | ||||||
|         memcpy(Memory::GetPointer(command.dma_request.dest_address), |         memcpy(Memory::GetPointer(command.dma_request.dest_address), | ||||||
|                Memory::GetPointer(command.dma_request.source_address), |                Memory::GetPointer(command.dma_request.source_address), | ||||||
|                command.dma_request.size); |                command.dma_request.size); | ||||||
|         SignalInterrupt(InterruptId::DMA); |         SignalInterrupt(InterruptId::DMA); | ||||||
|  |  | ||||||
|  |         VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), command.dma_request.size); | ||||||
|  |  | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|     // ctrulib homebrew sends all relevant command list data with this command, |     // ctrulib homebrew sends all relevant command list data with this command, | ||||||
|   | |||||||
| @@ -106,6 +106,8 @@ inline void Write(u32 addr, const T data) { | |||||||
|             } else { |             } else { | ||||||
|                 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); |                 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| @@ -129,6 +131,11 @@ inline void Write(u32 addr, const T data) { | |||||||
|             u32 output_width = config.output_width / horizontal_scale; |             u32 output_width = config.output_width / horizontal_scale; | ||||||
|             u32 output_height = config.output_height / vertical_scale; |             u32 output_height = config.output_height / vertical_scale; | ||||||
|  |  | ||||||
|  |             u32 input_size = config.input_height.Value() * config.input_width.Value() * GPU::Regs::BytesPerPixel(config.input_format.Value()); | ||||||
|  |             u32 output_size = output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format.Value()); | ||||||
|  |  | ||||||
|  |             VideoCore::g_renderer->hw_rasterizer->NotifyPreCopy(config.GetPhysicalInputAddress(), input_size); | ||||||
|  |  | ||||||
|             if (config.raw_copy) { |             if (config.raw_copy) { | ||||||
|                 // Raw copies do not perform color conversion nor tiled->linear / linear->tiled conversions |                 // Raw copies do not perform color conversion nor tiled->linear / linear->tiled conversions | ||||||
|                 // TODO(Subv): Verify if raw copies perform scaling |                 // TODO(Subv): Verify if raw copies perform scaling | ||||||
| @@ -142,6 +149,9 @@ inline void Write(u32 addr, const T data) { | |||||||
|                     config.output_format.Value(), config.flags); |                     config.output_format.Value(), config.flags); | ||||||
|  |  | ||||||
|                 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); |                 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); | ||||||
|  |  | ||||||
|  |                 VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size); | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -247,6 +257,8 @@ inline void Write(u32 addr, const T data) { | |||||||
|                       config.output_format.Value(), config.flags); |                       config.output_format.Value(), config.flags); | ||||||
|  |  | ||||||
|             GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); |             GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); | ||||||
|  |  | ||||||
|  |             VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,7 +1,11 @@ | |||||||
| set(SRCS | set(SRCS | ||||||
|             renderer_opengl/generated/gl_3_2_core.c |             renderer_opengl/generated/gl_3_2_core.c | ||||||
|  |             renderer_opengl/gl_rasterizer.cpp | ||||||
|  |             renderer_opengl/gl_rasterizer_cache.cpp | ||||||
|             renderer_opengl/renderer_opengl.cpp |             renderer_opengl/renderer_opengl.cpp | ||||||
|  |             renderer_opengl/gl_resource_manager.cpp | ||||||
|             renderer_opengl/gl_shader_util.cpp |             renderer_opengl/gl_shader_util.cpp | ||||||
|  |             renderer_opengl/gl_state.cpp | ||||||
|             debug_utils/debug_utils.cpp |             debug_utils/debug_utils.cpp | ||||||
|             clipper.cpp |             clipper.cpp | ||||||
|             command_processor.cpp |             command_processor.cpp | ||||||
| @@ -15,13 +19,19 @@ set(SRCS | |||||||
| set(HEADERS | set(HEADERS | ||||||
|             debug_utils/debug_utils.h |             debug_utils/debug_utils.h | ||||||
|             renderer_opengl/generated/gl_3_2_core.h |             renderer_opengl/generated/gl_3_2_core.h | ||||||
|  |             renderer_opengl/gl_pica_to_gl.h | ||||||
|  |             renderer_opengl/gl_rasterizer.h | ||||||
|  |             renderer_opengl/gl_rasterizer_cache.h | ||||||
|  |             renderer_opengl/gl_resource_manager.h | ||||||
|             renderer_opengl/gl_shader_util.h |             renderer_opengl/gl_shader_util.h | ||||||
|             renderer_opengl/gl_shaders.h |             renderer_opengl/gl_shaders.h | ||||||
|  |             renderer_opengl/gl_state.h | ||||||
|             renderer_opengl/renderer_opengl.h |             renderer_opengl/renderer_opengl.h | ||||||
|             clipper.h |             clipper.h | ||||||
|             color.h |             color.h | ||||||
|             command_processor.h |             command_processor.h | ||||||
|             gpu_debugger.h |             gpu_debugger.h | ||||||
|  |             hwrasterizer_base.h | ||||||
|             math.h |             math.h | ||||||
|             pica.h |             pica.h | ||||||
|             primitive_assembly.h |             primitive_assembly.h | ||||||
|   | |||||||
| @@ -12,8 +12,10 @@ | |||||||
| #include "pica.h" | #include "pica.h" | ||||||
| #include "primitive_assembly.h" | #include "primitive_assembly.h" | ||||||
| #include "vertex_shader.h" | #include "vertex_shader.h" | ||||||
|  | #include "video_core.h" | ||||||
| #include "core/hle/service/gsp_gpu.h" | #include "core/hle/service/gsp_gpu.h" | ||||||
| #include "core/hw/gpu.h" | #include "core/hw/gpu.h" | ||||||
|  | #include "core/settings.h" | ||||||
|  |  | ||||||
| #include "debug_utils/debug_utils.h" | #include "debug_utils/debug_utils.h" | ||||||
|  |  | ||||||
| @@ -107,7 +109,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||||||
|             bool index_u16 = index_info.format != 0; |             bool index_u16 = index_info.format != 0; | ||||||
|  |  | ||||||
|             DebugUtils::GeometryDumper geometry_dumper; |             DebugUtils::GeometryDumper geometry_dumper; | ||||||
|             PrimitiveAssembler<VertexShader::OutputVertex> clipper_primitive_assembler(registers.triangle_topology.Value()); |             PrimitiveAssembler<VertexShader::OutputVertex> primitive_assembler(registers.triangle_topology.Value()); | ||||||
|             PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(registers.triangle_topology.Value()); |             PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(registers.triangle_topology.Value()); | ||||||
|  |  | ||||||
|             for (unsigned int index = 0; index < registers.num_vertices; ++index) |             for (unsigned int index = 0; index < registers.num_vertices; ++index) | ||||||
| @@ -185,9 +187,25 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||||||
|                     // TODO: Add processed vertex to vertex cache! |                     // TODO: Add processed vertex to vertex cache! | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 if (Settings::values.use_hw_renderer) { | ||||||
|  |                     // Send to hardware renderer | ||||||
|  |                     static auto AddHWTriangle = [](const Pica::VertexShader::OutputVertex& v0, | ||||||
|  |                                                    const Pica::VertexShader::OutputVertex& v1, | ||||||
|  |                                                    const Pica::VertexShader::OutputVertex& v2) { | ||||||
|  |                         VideoCore::g_renderer->hw_rasterizer->AddTriangle(v0, v1, v2); | ||||||
|  |                     }; | ||||||
|  |                      | ||||||
|  |                     primitive_assembler.SubmitVertex(output, AddHWTriangle); | ||||||
|  |                 } else { | ||||||
|                     // Send to triangle clipper |                     // Send to triangle clipper | ||||||
|                 clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle); |                     primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle); | ||||||
|                 } |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (Settings::values.use_hw_renderer) { | ||||||
|  |                 VideoCore::g_renderer->hw_rasterizer->DrawTriangles(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             geometry_dumper.Dump(); |             geometry_dumper.Dump(); | ||||||
|  |  | ||||||
|             if (g_debug_context) |             if (g_debug_context) | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								src/video_core/hwrasterizer_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/video_core/hwrasterizer_base.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/emu_window.h" | ||||||
|  | #include "video_core/vertex_shader.h" | ||||||
|  |  | ||||||
|  | class HWRasterizer { | ||||||
|  | public: | ||||||
|  |     virtual ~HWRasterizer() { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Initialize API-specific GPU objects | ||||||
|  |     virtual void InitObjects() = 0; | ||||||
|  |  | ||||||
|  |     /// Set the window (context) to draw with | ||||||
|  |     virtual void SetWindow(EmuWindow* window) = 0; | ||||||
|  |  | ||||||
|  |     /// Converts the triangle verts to hardware data format and adds them to the current batch | ||||||
|  |     virtual void AddTriangle(const Pica::VertexShader::OutputVertex& v0, | ||||||
|  |                              const Pica::VertexShader::OutputVertex& v1, | ||||||
|  |                              const Pica::VertexShader::OutputVertex& v2) = 0; | ||||||
|  |  | ||||||
|  |     /// Draw the current batch of triangles | ||||||
|  |     virtual void DrawTriangles() = 0; | ||||||
|  |  | ||||||
|  |     /// Notify rasterizer that a copy within 3ds memory will occur after this notification | ||||||
|  |     virtual void NotifyPreCopy(u32 src_paddr, u32 size) = 0; | ||||||
|  |  | ||||||
|  |     /// Notify rasterizer that a 3ds memory region has been changed | ||||||
|  |     virtual void NotifyFlush(u32 paddr, u32 size) = 0; | ||||||
|  | }; | ||||||
| @@ -6,6 +6,8 @@ | |||||||
|  |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | #include "video_core/hwrasterizer_base.h" | ||||||
|  |  | ||||||
| class RendererBase : NonCopyable { | class RendererBase : NonCopyable { | ||||||
| public: | public: | ||||||
|  |  | ||||||
| @@ -48,6 +50,8 @@ public: | |||||||
|         return m_current_frame; |         return m_current_frame; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     HWRasterizer *hw_rasterizer; | ||||||
|  |  | ||||||
| protected: | protected: | ||||||
|     f32 m_current_fps;              ///< Current framerate, should be set by the renderer |     f32 m_current_fps;              ///< Current framerate, should be set by the renderer | ||||||
|     int m_current_frame;            ///< Current frame, should be set by the renderer |     int m_current_frame;            ///< Current frame, should be set by the renderer | ||||||
|   | |||||||
							
								
								
									
										93
									
								
								src/video_core/renderer_opengl/gl_pica_to_gl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/video_core/renderer_opengl/gl_pica_to_gl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | #include "video_core/pica.h" | ||||||
|  |  | ||||||
|  | #include "generated/gl_3_2_core.h" | ||||||
|  |  | ||||||
|  | namespace PicaToGL { | ||||||
|  |  | ||||||
|  | static GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) { | ||||||
|  |     switch (mode) { | ||||||
|  |     case Pica::Regs::TextureConfig::WrapMode::ClampToEdge: | ||||||
|  |         return GL_CLAMP_TO_EDGE; | ||||||
|  |     case Pica::Regs::TextureConfig::WrapMode::Repeat: | ||||||
|  |         return GL_REPEAT; | ||||||
|  |     case Pica::Regs::TextureConfig::WrapMode::MirroredRepeat: | ||||||
|  |         return GL_MIRRORED_REPEAT; | ||||||
|  |     default: | ||||||
|  |         LOG_ERROR(Render_OpenGL, "Unknown texture wrap mode %d", mode); | ||||||
|  |         return GL_CLAMP_TO_EDGE; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static GLenum BlendFunc(u32 factor) | ||||||
|  | { | ||||||
|  |     switch (factor) { | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.Zero: | ||||||
|  |         return GL_ZERO; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.One: | ||||||
|  |         return GL_ONE; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.SourceColor: | ||||||
|  |         return GL_SRC_COLOR; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.OneMinusSourceColor: | ||||||
|  |         return GL_ONE_MINUS_SRC_COLOR; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.DestColor: | ||||||
|  |         return GL_DST_COLOR; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.OneMinusDestColor: | ||||||
|  |         return GL_ONE_MINUS_DST_COLOR; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.SourceAlpha: | ||||||
|  |         return GL_SRC_ALPHA; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.OneMinusSourceAlpha: | ||||||
|  |         return GL_ONE_MINUS_SRC_ALPHA; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.DestAlpha: | ||||||
|  |         return GL_DST_ALPHA; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.OneMinusDestAlpha: | ||||||
|  |         return GL_ONE_MINUS_DST_ALPHA; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.ConstantColor: | ||||||
|  |         return GL_CONSTANT_COLOR; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.OneMinusConstantColor: | ||||||
|  |         return GL_ONE_MINUS_CONSTANT_COLOR; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.ConstantAlpha: | ||||||
|  |         return GL_CONSTANT_ALPHA; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.OneMinusConstantAlpha: | ||||||
|  |         return GL_ONE_MINUS_CONSTANT_ALPHA; | ||||||
|  |     case Pica::registers.output_merger.alpha_blending.SourceAlphaSaturate: | ||||||
|  |         return GL_SRC_ALPHA_SATURATE; | ||||||
|  |     default: | ||||||
|  |         LOG_ERROR(Render_OpenGL, "Unknown blend factor %d", factor); | ||||||
|  |         return GL_ONE; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static GLenum CompareFunc(u32 func) | ||||||
|  | { | ||||||
|  |     switch (func) { | ||||||
|  |     case Pica::registers.output_merger.Never: | ||||||
|  |         return GL_NEVER; | ||||||
|  |     case Pica::registers.output_merger.Always: | ||||||
|  |         return GL_ALWAYS; | ||||||
|  |     case Pica::registers.output_merger.Equal: | ||||||
|  |         return GL_EQUAL; | ||||||
|  |     case Pica::registers.output_merger.NotEqual: | ||||||
|  |         return GL_NOTEQUAL; | ||||||
|  |     case Pica::registers.output_merger.LessThan: | ||||||
|  |         return GL_LESS; | ||||||
|  |     case Pica::registers.output_merger.LessThanOrEqual: | ||||||
|  |         return GL_LEQUAL; | ||||||
|  |     case Pica::registers.output_merger.GreaterThan: | ||||||
|  |         return GL_GREATER; | ||||||
|  |     case Pica::registers.output_merger.GreaterThanOrEqual: | ||||||
|  |         return GL_GEQUAL; | ||||||
|  |     default: | ||||||
|  |         LOG_ERROR(Render_OpenGL, "Unknown compare function %d", func); | ||||||
|  |         return GL_ALWAYS; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace | ||||||
							
								
								
									
										730
									
								
								src/video_core/renderer_opengl/gl_rasterizer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										730
									
								
								src/video_core/renderer_opengl/gl_rasterizer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,730 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "core/settings.h" | ||||||
|  | #include "core/hw/gpu.h" | ||||||
|  |  | ||||||
|  | #include "video_core/color.h" | ||||||
|  | #include "video_core/pica.h" | ||||||
|  | #include "video_core/utils.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_pica_to_gl.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_shaders.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_shader_util.h" | ||||||
|  |  | ||||||
|  | #include "generated/gl_3_2_core.h" | ||||||
|  |  | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
|  | u32 ColorFormatBytesPerPixel(u32 format) { | ||||||
|  |     switch (format) { | ||||||
|  |     case Pica::registers.framebuffer.RGBA8: | ||||||
|  |         return 4; | ||||||
|  |     case Pica::registers.framebuffer.RGB8: | ||||||
|  |         return 3; | ||||||
|  |     case Pica::registers.framebuffer.RGB5A1: | ||||||
|  |     case Pica::registers.framebuffer.RGB565: | ||||||
|  |     case Pica::registers.framebuffer.RGBA4: | ||||||
|  |         return 2; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(-1), last_fb_depth_addr(-1) { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | RasterizerOpenGL::~RasterizerOpenGL() { | ||||||
|  |     // Set context for automatic resource destruction | ||||||
|  |     render_window->MakeCurrent(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Initialize API-specific GPU objects | ||||||
|  | void RasterizerOpenGL::InitObjects() { | ||||||
|  |     // Create the hardware shader program and get attrib/uniform locations | ||||||
|  |     shader.Create(GLShaders::g_vertex_shader_hw, GLShaders::g_fragment_shader_hw); | ||||||
|  |     attrib_position = glGetAttribLocation(shader.GetHandle(), "vert_position"); | ||||||
|  |     attrib_color = glGetAttribLocation(shader.GetHandle(), "vert_color"); | ||||||
|  |     attrib_texcoords = glGetAttribLocation(shader.GetHandle(), "vert_texcoords"); | ||||||
|  |  | ||||||
|  |     uniform_alphatest_func = glGetUniformLocation(shader.GetHandle(), "alphatest_func"); | ||||||
|  |     uniform_alphatest_ref = glGetUniformLocation(shader.GetHandle(), "alphatest_ref"); | ||||||
|  |  | ||||||
|  |     uniform_tex = glGetUniformLocation(shader.GetHandle(), "tex"); | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 6; i++) { | ||||||
|  |         auto& uniform_tev = uniform_tev_cfgs[i]; | ||||||
|  |  | ||||||
|  |         std::string tev_ref_str = "tev_cfgs[" + std::to_string(i) + "]"; | ||||||
|  |         uniform_tev.color_sources = glGetUniformLocation(shader.GetHandle(), (tev_ref_str + ".color_sources").c_str()); | ||||||
|  |         uniform_tev.alpha_sources = glGetUniformLocation(shader.GetHandle(), (tev_ref_str + ".alpha_sources").c_str()); | ||||||
|  |         uniform_tev.color_modifiers = glGetUniformLocation(shader.GetHandle(), (tev_ref_str + ".color_modifiers").c_str()); | ||||||
|  |         uniform_tev.alpha_modifiers = glGetUniformLocation(shader.GetHandle(), (tev_ref_str + ".alpha_modifiers").c_str()); | ||||||
|  |         uniform_tev.color_alpha_op = glGetUniformLocation(shader.GetHandle(), (tev_ref_str + ".color_alpha_op").c_str()); | ||||||
|  |         uniform_tev.color_alpha_multiplier = glGetUniformLocation(shader.GetHandle(), (tev_ref_str + ".color_alpha_multiplier").c_str()); | ||||||
|  |         uniform_tev.const_color = glGetUniformLocation(shader.GetHandle(), (tev_ref_str + ".const_color").c_str()); | ||||||
|  |         uniform_tev.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.GetHandle(), (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     uniform_out_maps = glGetUniformLocation(shader.GetHandle(), "out_maps"); | ||||||
|  |  | ||||||
|  |     // Generate VBO and VAO | ||||||
|  |     vertex_buffer.Create(); | ||||||
|  |     vertex_array.Create(); | ||||||
|  |  | ||||||
|  |     // Update OpenGL state | ||||||
|  |     state.draw.vertex_array = vertex_array.GetHandle(); | ||||||
|  |     state.draw.vertex_buffer = vertex_buffer.GetHandle(); | ||||||
|  |     state.draw.shader_program = shader.GetHandle(); | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 3; i++) { | ||||||
|  |         state.texture_unit[i].enabled_2d = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     // Set the texture samplers to correspond to different texture units | ||||||
|  |     glUniform1i(uniform_tex, 0); | ||||||
|  |     glUniform1i(uniform_tex + 1, 1); | ||||||
|  |     glUniform1i(uniform_tex + 2, 2); | ||||||
|  |  | ||||||
|  |     // Set vertex attributes | ||||||
|  |     glVertexAttribPointer(attrib_position, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); | ||||||
|  |     glVertexAttribPointer(attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); | ||||||
|  |     glVertexAttribPointer(attrib_texcoords, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); | ||||||
|  |     glVertexAttribPointer(attrib_texcoords + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); | ||||||
|  |     glVertexAttribPointer(attrib_texcoords + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); | ||||||
|  |     glEnableVertexAttribArray(attrib_position); | ||||||
|  |     glEnableVertexAttribArray(attrib_color); | ||||||
|  |     glEnableVertexAttribArray(attrib_texcoords); | ||||||
|  |     glEnableVertexAttribArray(attrib_texcoords + 1); | ||||||
|  |     glEnableVertexAttribArray(attrib_texcoords + 2); | ||||||
|  |  | ||||||
|  |     // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation | ||||||
|  |     fb_color_texture.texture.Create(); | ||||||
|  |     ReconfigColorTexture(fb_color_texture, Pica::registers.framebuffer.RGBA8, 1, 1); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||||
|  |  | ||||||
|  |     fb_depth_texture.texture.Create(); | ||||||
|  |     ReconfigDepthTexture(fb_depth_texture, Pica::Regs::DepthFormat::D16, 1, 1); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); | ||||||
|  |  | ||||||
|  |     // Configure OpenGL framebuffer | ||||||
|  |     framebuffer.Create(); | ||||||
|  |  | ||||||
|  |     state.draw.framebuffer = framebuffer.GetHandle(); | ||||||
|  |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |     state.texture_unit[0].texture_2d = 0; | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|  |     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.GetHandle(), 0); | ||||||
|  |     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.GetHandle(), 0); | ||||||
|  |  | ||||||
|  |     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | ||||||
|  |         LOG_ERROR(Render_OpenGL, "Framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Set the window (context) to draw with | ||||||
|  | void RasterizerOpenGL::SetWindow(EmuWindow* window) { | ||||||
|  |     render_window = window; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Converts the triangle verts to hardware data format and adds them to the current batch | ||||||
|  | void RasterizerOpenGL::AddTriangle(const Pica::VertexShader::OutputVertex& v0, | ||||||
|  |                                    const Pica::VertexShader::OutputVertex& v1, | ||||||
|  |                                    const Pica::VertexShader::OutputVertex& v2) { | ||||||
|  |     vertex_batch.push_back(HardwareVertex(v0)); | ||||||
|  |     vertex_batch.push_back(HardwareVertex(v1)); | ||||||
|  |     vertex_batch.push_back(HardwareVertex(v2)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Draw the current batch of triangles | ||||||
|  | void RasterizerOpenGL::DrawTriangles() { | ||||||
|  |     render_window->MakeCurrent(); | ||||||
|  |  | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     SyncFramebuffer(); | ||||||
|  |     SyncDrawState(); | ||||||
|  |  | ||||||
|  |     glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW); | ||||||
|  |     glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); | ||||||
|  |  | ||||||
|  |     vertex_batch.clear(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Notify rasterizer that a copy within 3ds memory will occur after this notification | ||||||
|  | void RasterizerOpenGL::NotifyPreCopy(u32 src_paddr, u32 size) { | ||||||
|  |     render_window->MakeCurrent(); | ||||||
|  |  | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     u32 cur_fb_color_addr = Pica::registers.framebuffer.GetColorBufferPhysicalAddress(); | ||||||
|  |     u32 cur_fb_color_size = ColorFormatBytesPerPixel(Pica::registers.framebuffer.color_format.Value()) | ||||||
|  |                             * Pica::registers.framebuffer.GetWidth() * Pica::registers.framebuffer.GetHeight(); | ||||||
|  |  | ||||||
|  |     u32 cur_fb_depth_addr = Pica::registers.framebuffer.GetDepthBufferPhysicalAddress(); | ||||||
|  |     u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(Pica::registers.framebuffer.depth_format) | ||||||
|  |                             * Pica::registers.framebuffer.GetWidth() * Pica::registers.framebuffer.GetHeight(); | ||||||
|  |  | ||||||
|  |     // If source memory region overlaps 3ds framebuffers, commit them before the copy happens | ||||||
|  |     u32 max_lower = std::max(src_paddr, cur_fb_color_addr); | ||||||
|  |     u32 min_upper = std::min(src_paddr + size, cur_fb_color_addr + cur_fb_color_size); | ||||||
|  |  | ||||||
|  |     if (max_lower <= min_upper) { | ||||||
|  |         CommitFramebuffer(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     max_lower = std::max(src_paddr, cur_fb_depth_addr); | ||||||
|  |     min_upper = std::min(src_paddr + size, cur_fb_depth_addr + cur_fb_depth_size); | ||||||
|  |  | ||||||
|  |     if (max_lower <= min_upper) { | ||||||
|  |         CommitFramebuffer(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Notify rasterizer that a 3ds memory region has been changed | ||||||
|  | void RasterizerOpenGL::NotifyFlush(u32 paddr, u32 size) { | ||||||
|  |     render_window->MakeCurrent(); | ||||||
|  |  | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     u32 cur_fb_color_addr = Pica::registers.framebuffer.GetColorBufferPhysicalAddress(); | ||||||
|  |     u32 cur_fb_color_size = ColorFormatBytesPerPixel(Pica::registers.framebuffer.color_format.Value()) | ||||||
|  |                             * Pica::registers.framebuffer.GetWidth() * Pica::registers.framebuffer.GetHeight(); | ||||||
|  |  | ||||||
|  |     u32 cur_fb_depth_addr = Pica::registers.framebuffer.GetDepthBufferPhysicalAddress(); | ||||||
|  |     u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(Pica::registers.framebuffer.depth_format) | ||||||
|  |                             * Pica::registers.framebuffer.GetWidth() * Pica::registers.framebuffer.GetHeight(); | ||||||
|  |  | ||||||
|  |     // If modified memory region overlaps 3ds framebuffers, reload their contents into OpenGL | ||||||
|  |     u32 max_lower = std::max(paddr, cur_fb_color_addr); | ||||||
|  |     u32 min_upper = std::min(paddr + size, cur_fb_color_addr + cur_fb_color_size); | ||||||
|  |  | ||||||
|  |     if (max_lower <= min_upper) { | ||||||
|  |         ReloadColorBuffer(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     max_lower = std::max(paddr, cur_fb_depth_addr); | ||||||
|  |     min_upper = std::min(paddr + size, cur_fb_depth_addr + cur_fb_depth_size); | ||||||
|  |  | ||||||
|  |     if (max_lower <= min_upper) { | ||||||
|  |         ReloadDepthBuffer(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Notify cache of flush in case the region touches a cached resource | ||||||
|  |     res_cache.NotifyFlush(paddr, size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Reconfigure the OpenGL color texture to use the given format and dimensions | ||||||
|  | void RasterizerOpenGL::ReconfigColorTexture(TextureInfo& texture, u32 format, u32 width, u32 height) { | ||||||
|  |     GLint internal_format; | ||||||
|  |  | ||||||
|  |     texture.format = format; | ||||||
|  |     texture.width = width; | ||||||
|  |     texture.height = height; | ||||||
|  |  | ||||||
|  |     switch (format) { | ||||||
|  |     case Pica::registers.framebuffer.RGBA8: | ||||||
|  |         internal_format = GL_RGBA; | ||||||
|  |         texture.gl_format = GL_RGBA; | ||||||
|  |         texture.gl_type = GL_UNSIGNED_INT_8_8_8_8; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Pica::registers.framebuffer.RGB8: | ||||||
|  |         // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every | ||||||
|  |         // specific OpenGL type used in this function using native-endian (that is, little-endian | ||||||
|  |         // mostly everywhere) for words or half-words. | ||||||
|  |         // TODO: check how those behave on big-endian processors. | ||||||
|  |         internal_format = GL_RGB; | ||||||
|  |         texture.gl_format = GL_BGR; | ||||||
|  |         texture.gl_type = GL_UNSIGNED_BYTE; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Pica::registers.framebuffer.RGB5A1: | ||||||
|  |         internal_format = GL_RGBA; | ||||||
|  |         texture.gl_format = GL_RGBA; | ||||||
|  |         texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Pica::registers.framebuffer.RGB565: | ||||||
|  |         internal_format = GL_RGB; | ||||||
|  |         texture.gl_format = GL_RGB; | ||||||
|  |         texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Pica::registers.framebuffer.RGBA4: | ||||||
|  |         internal_format = GL_RGBA; | ||||||
|  |         texture.gl_format = GL_RGBA; | ||||||
|  |         texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |     state.texture_unit[0].texture_2d = texture.texture.GetHandle(); | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|  |     glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, | ||||||
|  |         texture.gl_format, texture.gl_type, nullptr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Reconfigure the OpenGL depth texture to use the given format and dimensions | ||||||
|  | void RasterizerOpenGL::ReconfigDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height) { | ||||||
|  |     GLint internal_format; | ||||||
|  |  | ||||||
|  |     texture.format = format; | ||||||
|  |     texture.width = width; | ||||||
|  |     texture.height = height; | ||||||
|  |  | ||||||
|  |     switch (format) { | ||||||
|  |     case Pica::Regs::DepthFormat::D16: | ||||||
|  |         internal_format = GL_DEPTH_COMPONENT16; | ||||||
|  |         texture.gl_format = GL_DEPTH_COMPONENT; | ||||||
|  |         texture.gl_type = GL_UNSIGNED_SHORT; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Pica::Regs::DepthFormat::D24: | ||||||
|  |         internal_format = GL_DEPTH_COMPONENT24; | ||||||
|  |         texture.gl_format = GL_DEPTH_COMPONENT; | ||||||
|  |         texture.gl_type = GL_UNSIGNED_INT_24_8; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Pica::Regs::DepthFormat::D24S8: | ||||||
|  |         internal_format = GL_DEPTH24_STENCIL8; | ||||||
|  |         texture.gl_format = GL_DEPTH_STENCIL; | ||||||
|  |         texture.gl_type = GL_UNSIGNED_INT_24_8; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |     state.texture_unit[0].texture_2d = texture.texture.GetHandle(); | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|  |     glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, | ||||||
|  |         texture.gl_format, texture.gl_type, nullptr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer | ||||||
|  | void RasterizerOpenGL::SyncFramebuffer() { | ||||||
|  |     u32 cur_fb_color_addr = Pica::registers.framebuffer.GetColorBufferPhysicalAddress(); | ||||||
|  |     u32 new_fb_color_format = Pica::registers.framebuffer.color_format.Value(); | ||||||
|  |  | ||||||
|  |     u32 cur_fb_depth_addr = Pica::registers.framebuffer.GetDepthBufferPhysicalAddress(); | ||||||
|  |     Pica::Regs::DepthFormat new_fb_depth_format = Pica::registers.framebuffer.depth_format; | ||||||
|  |  | ||||||
|  |     bool fb_prop_changed = (fb_color_texture.format != new_fb_color_format || | ||||||
|  |                             fb_depth_texture.format != new_fb_depth_format || | ||||||
|  |                             fb_color_texture.width != Pica::registers.framebuffer.GetWidth() || | ||||||
|  |                             fb_color_texture.height != Pica::registers.framebuffer.GetHeight()); | ||||||
|  |     bool fb_modified = (last_fb_color_addr != cur_fb_color_addr || | ||||||
|  |                         last_fb_depth_addr != cur_fb_depth_addr || | ||||||
|  |                         fb_prop_changed); | ||||||
|  |  | ||||||
|  |     // Commit if fb modified in any way | ||||||
|  |     if (fb_modified) { | ||||||
|  |         CommitFramebuffer(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Reconfigure framebuffer textures if any property has changed | ||||||
|  |     if (fb_prop_changed) { | ||||||
|  |         ReconfigColorTexture(fb_color_texture, new_fb_color_format, | ||||||
|  |                                 Pica::registers.framebuffer.GetWidth(), Pica::registers.framebuffer.GetHeight()); | ||||||
|  |  | ||||||
|  |         ReconfigDepthTexture(fb_depth_texture, new_fb_depth_format, | ||||||
|  |                                 Pica::registers.framebuffer.GetWidth(), Pica::registers.framebuffer.GetHeight()); | ||||||
|  |  | ||||||
|  |         // Only attach depth buffer as stencil if it supports stencil | ||||||
|  |         switch (new_fb_depth_format) { | ||||||
|  |         case Pica::Regs::DepthFormat::D16: | ||||||
|  |         case Pica::Regs::DepthFormat::D24: | ||||||
|  |             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case Pica::Regs::DepthFormat::D24S8: | ||||||
|  |             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.GetHandle(), 0); | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         default: | ||||||
|  |             UNIMPLEMENTED(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Load buffer data again if fb modified in any way | ||||||
|  |     if (fb_modified) { | ||||||
|  |         last_fb_color_addr = cur_fb_color_addr; | ||||||
|  |         last_fb_depth_addr = cur_fb_depth_addr; | ||||||
|  |  | ||||||
|  |         // Currently not needed b/c of reloading buffers below, but will be needed for high-res rendering | ||||||
|  |         //glDepthMask(GL_TRUE); | ||||||
|  |         //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||||||
|  |  | ||||||
|  |         ReloadColorBuffer(); | ||||||
|  |         ReloadDepthBuffer(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Syncs the OpenGL drawing state to match the current PICA state | ||||||
|  | void RasterizerOpenGL::SyncDrawState() { | ||||||
|  |     // Sync the viewport | ||||||
|  |     GLsizei viewportWidth = (GLsizei)Pica::float24::FromRawFloat24(Pica::registers.viewport_size_x.Value()).ToFloat32() * 2; | ||||||
|  |     GLsizei viewportHeight = (GLsizei)Pica::float24::FromRawFloat24(Pica::registers.viewport_size_y.Value()).ToFloat32() * 2; | ||||||
|  |  | ||||||
|  |     // OpenGL uses different y coordinates, so negate corner offset and flip origin | ||||||
|  |     // TODO: Ensure viewport_corner.x should not be negated or origin flipped | ||||||
|  |     glViewport((GLsizei)static_cast<float>(Pica::registers.viewport_corner.x.Value()), | ||||||
|  |                 -(GLsizei)static_cast<float>(Pica::registers.viewport_corner.y.Value()) | ||||||
|  |                     + Pica::registers.framebuffer.GetHeight() - viewportHeight, | ||||||
|  |                 viewportWidth, viewportHeight); | ||||||
|  |  | ||||||
|  |     // Sync the cull mode | ||||||
|  |     switch (Pica::registers.cull_mode.Value()) { | ||||||
|  |     case Pica::Regs::CullMode::KeepAll: | ||||||
|  |         state.cull.enabled = false; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Pica::Regs::CullMode::KeepClockWise: | ||||||
|  |         state.cull.enabled = true; | ||||||
|  |         state.cull.mode = GL_BACK; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case Pica::Regs::CullMode::KeepCounterClockWise: | ||||||
|  |         state.cull.enabled = true; | ||||||
|  |         state.cull.mode = GL_FRONT; | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         LOG_ERROR(Render_OpenGL, "Unknown cull mode %d", Pica::registers.cull_mode.Value()); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Sync depth test | ||||||
|  |     if (Pica::registers.output_merger.depth_test_enable.Value()) { | ||||||
|  |         state.depth.test_enabled = true; | ||||||
|  |         state.depth.test_func = PicaToGL::CompareFunc(Pica::registers.output_merger.depth_test_func.Value()); | ||||||
|  |     } else { | ||||||
|  |         state.depth.test_enabled = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Sync depth writing | ||||||
|  |     if (Pica::registers.output_merger.depth_write_enable.Value()) { | ||||||
|  |         state.depth.write_mask = GL_TRUE; | ||||||
|  |     } else { | ||||||
|  |         state.depth.write_mask = GL_FALSE; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Sync stencil test | ||||||
|  |     // TODO: Untested, make sure stencil_reference_value refers to this mask | ||||||
|  |     if (Pica::registers.output_merger.stencil_test.stencil_test_enable.Value()) { | ||||||
|  |         state.stencil.test_enabled = true; | ||||||
|  |         state.stencil.test_func = PicaToGL::CompareFunc(Pica::registers.output_merger.stencil_test.stencil_test_func.Value()); | ||||||
|  |         state.stencil.test_ref = Pica::registers.output_merger.stencil_test.stencil_reference_value.Value(); | ||||||
|  |         state.stencil.test_mask = Pica::registers.output_merger.stencil_test.stencil_replacement_value.Value(); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         state.stencil.test_enabled = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Sync stencil writing | ||||||
|  |     // TODO: Untested, make sure stencil_mask refers to this mask | ||||||
|  |     state.stencil.write_mask = Pica::registers.output_merger.stencil_test.stencil_mask.Value(); | ||||||
|  |  | ||||||
|  |     // TODO: Need to sync glStencilOp() once corresponding PICA registers are discovered | ||||||
|  |  | ||||||
|  |     // Sync blend state | ||||||
|  |     if (Pica::registers.output_merger.alphablend_enable.Value()) { | ||||||
|  |         state.blend.enabled = true; | ||||||
|  |  | ||||||
|  |         state.blend.color.red = (GLclampf)Pica::registers.output_merger.blend_const.r.Value() / 255.0f; | ||||||
|  |         state.blend.color.green = (GLclampf)Pica::registers.output_merger.blend_const.g.Value() / 255.0f; | ||||||
|  |         state.blend.color.blue = (GLclampf)Pica::registers.output_merger.blend_const.b.Value() / 255.0f; | ||||||
|  |         state.blend.color.alpha = (GLclampf)Pica::registers.output_merger.blend_const.a.Value() / 255.0f; | ||||||
|  |  | ||||||
|  |         state.blend.src_rgb_func = PicaToGL::BlendFunc(Pica::registers.output_merger.alpha_blending.factor_source_rgb.Value()); | ||||||
|  |         state.blend.dst_rgb_func = PicaToGL::BlendFunc(Pica::registers.output_merger.alpha_blending.factor_dest_rgb.Value()); | ||||||
|  |         state.blend.src_a_func = PicaToGL::BlendFunc(Pica::registers.output_merger.alpha_blending.factor_source_a.Value()); | ||||||
|  |         state.blend.dst_a_func = PicaToGL::BlendFunc(Pica::registers.output_merger.alpha_blending.factor_dest_a.Value()); | ||||||
|  |     } else { | ||||||
|  |         state.blend.enabled = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Sync bound texture(s), upload if uncached | ||||||
|  |     auto pica_textures = Pica::registers.GetTextures(); | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 3; ++i) { | ||||||
|  |         const auto& texture = pica_textures[i]; | ||||||
|  |  | ||||||
|  |         if (texture.enabled) { | ||||||
|  |             state.texture_unit[i].enabled_2d = true; | ||||||
|  |             res_cache.LoadAndBindTexture(state, i, texture); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             state.texture_unit[i].enabled_2d = false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     // Sync shader output register mapping to hw shader | ||||||
|  |     for (int i = 0; i < 7 * 4; ++i) { | ||||||
|  |         glUniform1i(uniform_out_maps + i, 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 6; ++i) { | ||||||
|  |         const auto& output_register_map = Pica::registers.vs_output_attributes[i]; | ||||||
|  |  | ||||||
|  |         u32 semantics[4] = { | ||||||
|  |             output_register_map.map_x.Value(), output_register_map.map_y.Value(), | ||||||
|  |             output_register_map.map_z.Value(), output_register_map.map_w.Value() | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         // TODO: Might only need to do this once per shader? Not sure when/if out maps are modified. | ||||||
|  |         for (int comp = 0; comp < 4; ++comp) { | ||||||
|  |             if (semantics[comp] != Pica::Regs::VSOutputAttributes::INVALID) { | ||||||
|  |                 glUniform1i(uniform_out_maps + semantics[comp], 4 * i + comp); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Sync texture environment configurations to hw shader | ||||||
|  |     auto tev_stages = Pica::registers.GetTevStages(); | ||||||
|  |     for (int i = 0; i < 6; i++) { | ||||||
|  |         const auto& stage = tev_stages[i]; | ||||||
|  |         const auto& uniform_tev_cfg = uniform_tev_cfgs[i]; | ||||||
|  |  | ||||||
|  |         GLint color_srcs[3] = { (GLint)stage.color_source1.Value(), (GLint)stage.color_source2.Value(), (GLint)stage.color_source3.Value() }; | ||||||
|  |         GLint alpha_srcs[3] = { (GLint)stage.alpha_source1.Value(), (GLint)stage.alpha_source2.Value(), (GLint)stage.alpha_source3.Value() }; | ||||||
|  |         GLint color_mods[3] = { (GLint)stage.color_modifier1.Value(), (GLint)stage.color_modifier2.Value(), (GLint)stage.color_modifier3.Value() }; | ||||||
|  |         GLint alpha_mods[3] = { (GLint)stage.alpha_modifier1.Value(), (GLint)stage.alpha_modifier2.Value(), (GLint)stage.alpha_modifier3.Value() }; | ||||||
|  |         GLfloat const_color[4] = { stage.const_r.Value() / 255.0f, | ||||||
|  |                                     stage.const_g.Value() / 255.0f, | ||||||
|  |                                     stage.const_b.Value() / 255.0f, | ||||||
|  |                                     stage.const_a.Value() / 255.0f }; | ||||||
|  |  | ||||||
|  |         glUniform3iv(uniform_tev_cfg.color_sources, 1, color_srcs); | ||||||
|  |         glUniform3iv(uniform_tev_cfg.alpha_sources, 1, alpha_srcs); | ||||||
|  |         glUniform3iv(uniform_tev_cfg.color_modifiers, 1, color_mods); | ||||||
|  |         glUniform3iv(uniform_tev_cfg.alpha_modifiers, 1, alpha_mods); | ||||||
|  |         glUniform2i(uniform_tev_cfg.color_alpha_op, (GLint)stage.color_op.Value(), (GLint)stage.alpha_op.Value()); | ||||||
|  |         glUniform2f(uniform_tev_cfg.color_alpha_multiplier, (GLfloat)stage.GetColorMultiplier(), (GLfloat)stage.GetAlphaMultiplier()); | ||||||
|  |         glUniform4fv(uniform_tev_cfg.const_color, 1, const_color); | ||||||
|  |         glUniform2i(uniform_tev_cfg.updates_combiner_buffer_color_alpha, | ||||||
|  |                     Pica::registers.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(i), | ||||||
|  |                     Pica::registers.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(i)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Sync alpha testing to hw shader | ||||||
|  |     if (Pica::registers.output_merger.alpha_test.enable.Value()) { | ||||||
|  |         glUniform1i(uniform_alphatest_func, Pica::registers.output_merger.alpha_test.func.Value()); | ||||||
|  |         glUniform1f(uniform_alphatest_ref, Pica::registers.output_merger.alpha_test.ref.Value() / 255.0f); | ||||||
|  |     } else { | ||||||
|  |         glUniform1i(uniform_alphatest_func, 1); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Copies the 3ds color framebuffer into the OpenGL color framebuffer texture | ||||||
|  | void RasterizerOpenGL::ReloadColorBuffer() { | ||||||
|  |     u8* color_buffer = Memory::GetPhysicalPointer(last_fb_color_addr); | ||||||
|  |  | ||||||
|  |     if (color_buffer == nullptr) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     u32 bytes_per_pixel = ColorFormatBytesPerPixel(fb_color_texture.format); | ||||||
|  |  | ||||||
|  |     u8* ogl_img = new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]; | ||||||
|  |  | ||||||
|  |     // TODO: Evaluate whether u16/memcpy/u32 is faster for 2/3/4 bpp versus memcpy for all | ||||||
|  |     for (int x = 0; x < fb_color_texture.width; ++x) | ||||||
|  |     { | ||||||
|  |         for (int y = 0; y < fb_color_texture.height; ++y) | ||||||
|  |         { | ||||||
|  |             const u32 coarse_y = y & ~7; | ||||||
|  |             u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; | ||||||
|  |             u32 ogl_px_idx = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel; | ||||||
|  |  | ||||||
|  |             u8* pixel = color_buffer + dst_offset; | ||||||
|  |             memcpy(&ogl_img[ogl_px_idx], pixel, bytes_per_pixel); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |     state.texture_unit[0].texture_2d = fb_color_texture.texture.GetHandle(); | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|  |     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_color_texture.width, fb_color_texture.height, fb_color_texture.gl_format, fb_color_texture.gl_type, ogl_img); | ||||||
|  |  | ||||||
|  |     delete[] ogl_img; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Copies the 3ds depth framebuffer into the OpenGL depth framebuffer texture | ||||||
|  | void RasterizerOpenGL::ReloadDepthBuffer() { | ||||||
|  |     // TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil | ||||||
|  |     u8* depth_buffer = Memory::GetPhysicalPointer(last_fb_depth_addr); | ||||||
|  |  | ||||||
|  |     if (depth_buffer == nullptr) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format); | ||||||
|  |  | ||||||
|  |     // OpenGL needs 4 bpp alignment for D24 | ||||||
|  |     u32 ogl_bpp = bytes_per_pixel == 3 ? 4 : bytes_per_pixel; | ||||||
|  |  | ||||||
|  |     u8* ogl_img = new u8[fb_depth_texture.width * fb_depth_texture.height * ogl_bpp]; | ||||||
|  |  | ||||||
|  |     for (int x = 0; x < fb_depth_texture.width; ++x) | ||||||
|  |     { | ||||||
|  |         for (int y = 0; y < fb_depth_texture.height; ++y) | ||||||
|  |         { | ||||||
|  |             const u32 coarse_y = y & ~7; | ||||||
|  |             u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; | ||||||
|  |             u32 ogl_px_idx = x + y * fb_depth_texture.width; | ||||||
|  |  | ||||||
|  |             switch (fb_depth_texture.format) { | ||||||
|  |             case Pica::Regs::DepthFormat::D16: | ||||||
|  |                 ((u16*)ogl_img)[ogl_px_idx] = Color::DecodeD16(depth_buffer + dst_offset); | ||||||
|  |                 break; | ||||||
|  |             case Pica::Regs::DepthFormat::D24: | ||||||
|  |                 ((u32*)ogl_img)[ogl_px_idx] = Color::DecodeD24(depth_buffer + dst_offset); | ||||||
|  |                 break; | ||||||
|  |             case Pica::Regs::DepthFormat::D24S8: | ||||||
|  |             { | ||||||
|  |                 Math::Vec2<u32> depth_stencil = Color::DecodeD24S8(depth_buffer + dst_offset); | ||||||
|  |                 ((u32*)ogl_img)[ogl_px_idx] = depth_stencil.x << 8 | depth_stencil.y; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             default: | ||||||
|  |                 LOG_CRITICAL(Render_OpenGL, "Unknown memory framebuffer depth format %x", fb_depth_texture.format); | ||||||
|  |                 UNIMPLEMENTED(); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |     state.texture_unit[0].texture_2d = fb_depth_texture.texture.GetHandle(); | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|  |     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_depth_texture.width, fb_depth_texture.height, fb_depth_texture.gl_format, fb_depth_texture.gl_type, ogl_img); | ||||||
|  |  | ||||||
|  |     delete[] ogl_img; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  | * Save the current OpenGL framebuffer to the current PICA framebuffer in 3ds memory | ||||||
|  | * Loads the OpenGL framebuffer textures into temporary buffers | ||||||
|  | * Then copies into the 3ds framebuffer using proper Morton order | ||||||
|  | */ | ||||||
|  | void RasterizerOpenGL::CommitFramebuffer() { | ||||||
|  |     if (last_fb_color_addr != -1) | ||||||
|  |     { | ||||||
|  |         u8* color_buffer = Memory::GetPhysicalPointer(last_fb_color_addr); | ||||||
|  |  | ||||||
|  |         if (color_buffer != nullptr) { | ||||||
|  |             u32 bytes_per_pixel = ColorFormatBytesPerPixel(fb_color_texture.format); | ||||||
|  |  | ||||||
|  |             std::unique_ptr<u8> ogl_img(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]); | ||||||
|  |  | ||||||
|  |             state.texture_unit[0].enabled_2d = true; | ||||||
|  |             state.texture_unit[0].texture_2d = fb_color_texture.texture.GetHandle(); | ||||||
|  |             state.Apply(); | ||||||
|  |  | ||||||
|  |             glActiveTexture(GL_TEXTURE0); | ||||||
|  |             glGetTexImage(GL_TEXTURE_2D, 0, fb_color_texture.gl_format, fb_color_texture.gl_type, ogl_img.get()); | ||||||
|  |  | ||||||
|  |             for (int x = 0; x < fb_color_texture.width; ++x) | ||||||
|  |             { | ||||||
|  |                 for (int y = 0; y < fb_color_texture.height; ++y) | ||||||
|  |                 { | ||||||
|  |                     const u32 coarse_y = y & ~7; | ||||||
|  |                     u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; | ||||||
|  |                     u32 ogl_px_idx = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel; | ||||||
|  |  | ||||||
|  |                     u8* pixel = color_buffer + dst_offset; | ||||||
|  |                     memcpy(pixel, &ogl_img.get()[ogl_px_idx], bytes_per_pixel); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (last_fb_depth_addr != -1) | ||||||
|  |     { | ||||||
|  |         // TODO: Output seems correct visually, but doesn't quite match sw renderer output. One of them is wrong. | ||||||
|  |         u8* depth_buffer = Memory::GetPhysicalPointer(last_fb_depth_addr); | ||||||
|  |  | ||||||
|  |         if (depth_buffer != nullptr) { | ||||||
|  |             u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format); | ||||||
|  |  | ||||||
|  |             // OpenGL needs 4 bpp alignment for D24 | ||||||
|  |             u32 ogl_bpp = bytes_per_pixel == 3 ? 4 : bytes_per_pixel; | ||||||
|  |  | ||||||
|  |             std::unique_ptr<u8> ogl_img(new u8[fb_depth_texture.width * fb_depth_texture.height * ogl_bpp]); | ||||||
|  |  | ||||||
|  |             state.texture_unit[0].enabled_2d = true; | ||||||
|  |             state.texture_unit[0].texture_2d = fb_depth_texture.texture.GetHandle(); | ||||||
|  |             state.Apply(); | ||||||
|  |  | ||||||
|  |             glActiveTexture(GL_TEXTURE0); | ||||||
|  |             glGetTexImage(GL_TEXTURE_2D, 0, fb_depth_texture.gl_format, fb_depth_texture.gl_type, ogl_img.get()); | ||||||
|  |  | ||||||
|  |             for (int x = 0; x < fb_depth_texture.width; ++x) | ||||||
|  |             { | ||||||
|  |                 for (int y = 0; y < fb_depth_texture.height; ++y) | ||||||
|  |                 { | ||||||
|  |                     const u32 coarse_y = y & ~7; | ||||||
|  |                     u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; | ||||||
|  |                     u32 ogl_px_idx = x + y * fb_depth_texture.width; | ||||||
|  |  | ||||||
|  |                     switch (fb_depth_texture.format) { | ||||||
|  |                     case Pica::Regs::DepthFormat::D16: | ||||||
|  |                         Color::EncodeD16(((u16*)ogl_img.get())[ogl_px_idx], depth_buffer + dst_offset); | ||||||
|  |                         break; | ||||||
|  |                     case Pica::Regs::DepthFormat::D24: | ||||||
|  |                         Color::EncodeD24(((u32*)ogl_img.get())[ogl_px_idx], depth_buffer + dst_offset); | ||||||
|  |                         break; | ||||||
|  |                     case Pica::Regs::DepthFormat::D24S8: | ||||||
|  |                     { | ||||||
|  |                         u32 depth_stencil = ((u32*)ogl_img.get())[ogl_px_idx]; | ||||||
|  |                         Color::EncodeD24S8(depth_stencil >> 8, depth_stencil & 0xFF, depth_buffer + dst_offset); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                     default: | ||||||
|  |                         LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer depth format %x", fb_depth_texture.format); | ||||||
|  |                         UNIMPLEMENTED(); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										152
									
								
								src/video_core/renderer_opengl/gl_rasterizer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/video_core/renderer_opengl/gl_rasterizer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "video_core/hwrasterizer_base.h" | ||||||
|  |  | ||||||
|  | #include "gl_state.h" | ||||||
|  | #include "gl_rasterizer_cache.h" | ||||||
|  |  | ||||||
|  | class RasterizerOpenGL : public HWRasterizer { | ||||||
|  | public: | ||||||
|  |  | ||||||
|  |     RasterizerOpenGL(); | ||||||
|  |     ~RasterizerOpenGL() override; | ||||||
|  |  | ||||||
|  |     /// Initialize API-specific GPU objects | ||||||
|  |     void InitObjects() override; | ||||||
|  |  | ||||||
|  |     /// Set the window (context) to draw with | ||||||
|  |     void SetWindow(EmuWindow* window) override; | ||||||
|  |  | ||||||
|  |     /// Converts the triangle verts to hardware data format and adds them to the current batch | ||||||
|  |     void AddTriangle(const Pica::VertexShader::OutputVertex& v0, | ||||||
|  |                      const Pica::VertexShader::OutputVertex& v1, | ||||||
|  |                      const Pica::VertexShader::OutputVertex& v2) override; | ||||||
|  |  | ||||||
|  |     /// Draw the current batch of triangles | ||||||
|  |     void DrawTriangles() override; | ||||||
|  |  | ||||||
|  |     /// Notify rasterizer that a copy within 3ds memory will occur after this notification | ||||||
|  |     void NotifyPreCopy(u32 src_paddr, u32 size) override; | ||||||
|  |  | ||||||
|  |     /// Notify rasterizer that a 3ds memory region has been changed | ||||||
|  |     void NotifyFlush(u32 paddr, u32 size) override; | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     /// Structure used for managing texture environment states | ||||||
|  |     struct TEVConfigUniforms { | ||||||
|  |         GLuint color_sources; | ||||||
|  |         GLuint alpha_sources; | ||||||
|  |         GLuint color_modifiers; | ||||||
|  |         GLuint alpha_modifiers; | ||||||
|  |         GLuint color_alpha_op; | ||||||
|  |         GLuint color_alpha_multiplier; | ||||||
|  |         GLuint const_color; | ||||||
|  |         GLuint updates_combiner_buffer_color_alpha; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /// Structure used for storing information about color textures | ||||||
|  |     struct TextureInfo { | ||||||
|  |         OGLTexture texture; | ||||||
|  |         GLsizei width; | ||||||
|  |         GLsizei height; | ||||||
|  |         u32 format; | ||||||
|  |         GLenum gl_format; | ||||||
|  |         GLenum gl_type; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /// Structure used for storing information about depth textures | ||||||
|  |     struct DepthTextureInfo { | ||||||
|  |         OGLTexture texture; | ||||||
|  |         GLsizei width; | ||||||
|  |         GLsizei height; | ||||||
|  |         Pica::Regs::DepthFormat format; | ||||||
|  |         GLenum gl_format; | ||||||
|  |         GLenum gl_type; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     ///Structure that the hardware rendered vertices are composed of | ||||||
|  |     struct HardwareVertex { | ||||||
|  |         HardwareVertex(const Pica::VertexShader::OutputVertex& v) { | ||||||
|  |             position[0] = v.pos.x.ToFloat32(); | ||||||
|  |             position[1] = v.pos.y.ToFloat32(); | ||||||
|  |             position[2] = v.pos.z.ToFloat32(); | ||||||
|  |             position[3] = v.pos.w.ToFloat32(); | ||||||
|  |             color[0] = v.color.x.ToFloat32(); | ||||||
|  |             color[1] = v.color.y.ToFloat32(); | ||||||
|  |             color[2] = v.color.z.ToFloat32(); | ||||||
|  |             color[3] = v.color.w.ToFloat32(); | ||||||
|  |             tex_coord0[0] = v.tc0.x.ToFloat32(); | ||||||
|  |             tex_coord0[1] = v.tc0.y.ToFloat32(); | ||||||
|  |             tex_coord1[0] = v.tc1.x.ToFloat32(); | ||||||
|  |             tex_coord1[1] = v.tc1.y.ToFloat32(); | ||||||
|  |             tex_coord2[0] = v.tc2.x.ToFloat32(); | ||||||
|  |             tex_coord2[1] = v.tc2.y.ToFloat32(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         GLfloat position[4]; | ||||||
|  |         GLfloat color[4]; | ||||||
|  |         GLfloat tex_coord0[2]; | ||||||
|  |         GLfloat tex_coord1[2]; | ||||||
|  |         GLfloat tex_coord2[2]; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /// Reconfigure the OpenGL color texture to use the given format and dimensions | ||||||
|  |     void ReconfigColorTexture(TextureInfo& texture, u32 format, u32 width, u32 height); | ||||||
|  |  | ||||||
|  |     /// Reconfigure the OpenGL depth texture to use the given format and dimensions | ||||||
|  |     void ReconfigDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height); | ||||||
|  |  | ||||||
|  |     /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer | ||||||
|  |     void SyncFramebuffer(); | ||||||
|  |  | ||||||
|  |     /// Syncs the OpenGL drawing state to match the current PICA state | ||||||
|  |     void SyncDrawState(); | ||||||
|  |  | ||||||
|  |     /// Copies the 3ds color framebuffer into the OpenGL color framebuffer texture | ||||||
|  |     void ReloadColorBuffer(); | ||||||
|  |  | ||||||
|  |     /// Copies the 3ds depth framebuffer into the OpenGL depth framebuffer texture | ||||||
|  |     void ReloadDepthBuffer(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |     * Save the current OpenGL framebuffer to the current PICA framebuffer in 3ds memory | ||||||
|  |     * Loads the OpenGL framebuffer textures into temporary buffers | ||||||
|  |     * Then copies into the 3ds framebuffer using proper Morton order | ||||||
|  |     */ | ||||||
|  |     void CommitFramebuffer(); | ||||||
|  |  | ||||||
|  |     EmuWindow* render_window; | ||||||
|  |     RasterizerCacheOpenGL res_cache; | ||||||
|  |  | ||||||
|  |     std::vector<HardwareVertex> vertex_batch; | ||||||
|  |  | ||||||
|  |     OpenGLState state; | ||||||
|  |  | ||||||
|  |     PAddr last_fb_color_addr; | ||||||
|  |     PAddr last_fb_depth_addr; | ||||||
|  |  | ||||||
|  |     // Hardware rasterizer | ||||||
|  |     TextureInfo fb_color_texture; | ||||||
|  |     DepthTextureInfo fb_depth_texture; | ||||||
|  |     OGLShader shader; | ||||||
|  |     OGLVertexArray vertex_array; | ||||||
|  |     OGLBuffer vertex_buffer; | ||||||
|  |     OGLFramebuffer framebuffer; | ||||||
|  |  | ||||||
|  |     // Hardware vertex shader | ||||||
|  |     GLuint attrib_position; | ||||||
|  |     GLuint attrib_color; | ||||||
|  |     GLuint attrib_texcoords; | ||||||
|  |  | ||||||
|  |     // Hardware fragment shader | ||||||
|  |     GLuint uniform_alphatest_func; | ||||||
|  |     GLuint uniform_alphatest_ref; | ||||||
|  |     GLuint uniform_tex; | ||||||
|  |     TEVConfigUniforms uniform_tev_cfgs[6]; | ||||||
|  |     GLuint uniform_out_maps; | ||||||
|  |     GLuint uniform_tex_envs; | ||||||
|  | }; | ||||||
							
								
								
									
										82
									
								
								src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "core/mem_map.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_pica_to_gl.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | ||||||
|  | #include "video_core/debug_utils/debug_utils.h" | ||||||
|  | #include "video_core/math.h" | ||||||
|  |  | ||||||
|  | RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { | ||||||
|  |     FullFlush(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Loads a texture from 3ds memory to OpenGL and caches it (if not already cached) | ||||||
|  | void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, int texture_unit, const Pica::Regs::FullTextureConfig& config) { | ||||||
|  |     PAddr tex_paddr = config.config.GetPhysicalAddress(); | ||||||
|  |  | ||||||
|  |     auto cached_texture = texture_cache.find(tex_paddr); | ||||||
|  |  | ||||||
|  |     if (cached_texture != texture_cache.end()) { | ||||||
|  |         state.texture_unit[texture_unit].texture_2d = cached_texture->second->texture.GetHandle(); | ||||||
|  |         state.Apply(); | ||||||
|  |     } else { | ||||||
|  |         std::shared_ptr<CachedTexture> new_texture(new CachedTexture()); | ||||||
|  |  | ||||||
|  |         new_texture->texture.Create(); | ||||||
|  |         state.texture_unit[texture_unit].texture_2d = new_texture->texture.GetHandle(); | ||||||
|  |         state.Apply(); | ||||||
|  |  | ||||||
|  |         // TODO: Need to choose filters that correspond to PICA once register is declared | ||||||
|  |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||||
|  |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||||
|  |  | ||||||
|  |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(config.config.wrap_s.Value())); | ||||||
|  |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(config.config.wrap_t.Value())); | ||||||
|  |  | ||||||
|  |         auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format); | ||||||
|  |  | ||||||
|  |         new_texture->width = info.width; | ||||||
|  |         new_texture->height = info.height; | ||||||
|  |         new_texture->size = info.width * info.height * Pica::Regs::NibblesPerPixel(info.format); | ||||||
|  |  | ||||||
|  |         Math::Vec4<u8>* rgba_tex = new Math::Vec4<u8>[info.width * info.height]; | ||||||
|  |  | ||||||
|  |         for (int i = 0; i < info.width; i++) | ||||||
|  |         { | ||||||
|  |             for (int j = 0; j < info.height; j++) | ||||||
|  |             { | ||||||
|  |                 rgba_tex[i + info.width * j] = Pica::DebugUtils::LookupTexture(Memory::GetPhysicalPointer(tex_paddr), i, info.height - 1 - j, info); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba_tex); | ||||||
|  |  | ||||||
|  |         delete[] rgba_tex; | ||||||
|  |  | ||||||
|  |         texture_cache.emplace(tex_paddr, new_texture); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Flush any cached resource that touches the flushed region | ||||||
|  | void RasterizerCacheOpenGL::NotifyFlush(u32 paddr, u32 size) { | ||||||
|  |     // Flush any texture that falls in the flushed region | ||||||
|  |     for (auto it = texture_cache.begin(); it != texture_cache.end();) { | ||||||
|  |         u32 max_lower = std::max(paddr, it->first); | ||||||
|  |         u32 min_upper = std::min(paddr + size, it->first + it->second->size); | ||||||
|  |  | ||||||
|  |         if (max_lower <= min_upper) { | ||||||
|  |             it = texture_cache.erase(it); | ||||||
|  |         } else { | ||||||
|  |             ++it; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Flush all cached resources | ||||||
|  | void RasterizerCacheOpenGL::FullFlush() { | ||||||
|  |     for (auto it = texture_cache.begin(); it != texture_cache.end();) { | ||||||
|  |         it = texture_cache.erase(it); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								src/video_core/renderer_opengl/gl_rasterizer_cache.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/video_core/renderer_opengl/gl_rasterizer_cache.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "gl_state.h" | ||||||
|  | #include "gl_resource_manager.h" | ||||||
|  | #include "video_core/pica.h" | ||||||
|  |  | ||||||
|  | #include <memory> | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
|  | class RasterizerCacheOpenGL : NonCopyable { | ||||||
|  | public: | ||||||
|  |     ~RasterizerCacheOpenGL(); | ||||||
|  |  | ||||||
|  |     /// Loads a texture from 3ds memory to OpenGL and caches it (if not already cached) | ||||||
|  |     void LoadAndBindTexture(OpenGLState &state, int texture_unit, const Pica::Regs::FullTextureConfig& config); | ||||||
|  |  | ||||||
|  |     /// Flush any cached resource that touches the flushed region | ||||||
|  |     void NotifyFlush(u32 paddr, u32 size); | ||||||
|  |  | ||||||
|  |     /// Flush all cached OpenGL resources tracked by this cache manager | ||||||
|  |     void FullFlush(); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     struct CachedTexture { | ||||||
|  |         OGLTexture texture; | ||||||
|  |         GLuint width; | ||||||
|  |         GLuint height; | ||||||
|  |         u32 size; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     std::map<u32, std::shared_ptr<CachedTexture>> texture_cache; | ||||||
|  | }; | ||||||
							
								
								
									
										109
									
								
								src/video_core/renderer_opengl/gl_resource_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/video_core/renderer_opengl/gl_resource_manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_shader_util.h" | ||||||
|  |  | ||||||
|  | // OGLResource base class | ||||||
|  | OGLResource::OGLResource() : handle(0) { | ||||||
|  |      | ||||||
|  | } | ||||||
|  |  | ||||||
|  | OGLResource::~OGLResource() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLResource::Release() { | ||||||
|  |      | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Textures | ||||||
|  | OGLTexture::~OGLTexture() { | ||||||
|  |     Release(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLTexture::Create() { | ||||||
|  |     if (handle != 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     glGenTextures(1, &handle); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLTexture::Release() { | ||||||
|  |     glDeleteTextures(1, &handle); | ||||||
|  |     handle = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Shaders | ||||||
|  | OGLShader::~OGLShader() { | ||||||
|  |     Release(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLShader::Create(const char* vert_shader, const char* frag_shader) { | ||||||
|  |     if (handle != 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     handle = ShaderUtil::LoadShaders(vert_shader, frag_shader); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLShader::Release() { | ||||||
|  |     glDeleteProgram(handle); | ||||||
|  |     handle = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Buffer objects | ||||||
|  | OGLBuffer::~OGLBuffer() { | ||||||
|  |     Release(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLBuffer::Create() { | ||||||
|  |     if (handle != 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     glGenBuffers(1, &handle); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLBuffer::Release() { | ||||||
|  |     glDeleteBuffers(1, &handle); | ||||||
|  |     handle = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Vertex array objects | ||||||
|  | OGLVertexArray::~OGLVertexArray() { | ||||||
|  |     Release(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLVertexArray::Create() { | ||||||
|  |     if (handle != 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     glGenVertexArrays(1, &handle); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLVertexArray::Release() { | ||||||
|  |     glDeleteVertexArrays(1, &handle); | ||||||
|  |     handle = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Framebuffers | ||||||
|  | OGLFramebuffer::~OGLFramebuffer() { | ||||||
|  |     Release(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLFramebuffer::Create() { | ||||||
|  |     if (handle != 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     glGenFramebuffers(1, &handle); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void OGLFramebuffer::Release() { | ||||||
|  |     glDeleteFramebuffers(1, &handle); | ||||||
|  |     handle = 0; | ||||||
|  | } | ||||||
							
								
								
									
										81
									
								
								src/video_core/renderer_opengl/gl_resource_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/video_core/renderer_opengl/gl_resource_manager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | #include "generated/gl_3_2_core.h" | ||||||
|  |  | ||||||
|  | class OGLResource : NonCopyable { | ||||||
|  | public: | ||||||
|  |     OGLResource(); | ||||||
|  |     virtual ~OGLResource(); | ||||||
|  |  | ||||||
|  |     /// Returns the internal OpenGL resource handle for this resource | ||||||
|  |     inline GLuint GetHandle() { | ||||||
|  |         return handle; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Deletes the internal OpenGL resource | ||||||
|  |     virtual void Release(); | ||||||
|  |  | ||||||
|  | protected: | ||||||
|  |     GLuint handle; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class OGLTexture : public OGLResource { | ||||||
|  | public: | ||||||
|  |     ~OGLTexture() override; | ||||||
|  |  | ||||||
|  |     /// Creates a new internal OpenGL resource and stores the handle | ||||||
|  |     void Create(); | ||||||
|  |  | ||||||
|  |     /// Deletes the internal OpenGL resource | ||||||
|  |     void Release() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class OGLShader : public OGLResource { | ||||||
|  | public: | ||||||
|  |     ~OGLShader() override; | ||||||
|  |  | ||||||
|  |     /// Creates a new internal OpenGL resource and stores the handle | ||||||
|  |     void Create(const char* vert_shader, const char* frag_shader); | ||||||
|  |  | ||||||
|  |     /// Deletes the internal OpenGL resource | ||||||
|  |     void Release() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class OGLBuffer : public OGLResource { | ||||||
|  | public: | ||||||
|  |     ~OGLBuffer() override; | ||||||
|  |  | ||||||
|  |     /// Creates a new internal OpenGL resource and stores the handle | ||||||
|  |     void Create(); | ||||||
|  |  | ||||||
|  |     /// Deletes the internal OpenGL resource | ||||||
|  |     void Release() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class OGLVertexArray : public OGLResource { | ||||||
|  | public: | ||||||
|  |     ~OGLVertexArray() override; | ||||||
|  |  | ||||||
|  |     /// Creates a new internal OpenGL resource and stores the handle | ||||||
|  |     void Create(); | ||||||
|  |  | ||||||
|  |     /// Deletes the internal OpenGL resource | ||||||
|  |     void Release() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class OGLFramebuffer : public OGLResource { | ||||||
|  | public: | ||||||
|  |     ~OGLFramebuffer() override; | ||||||
|  |  | ||||||
|  |     /// Creates a new internal OpenGL resource and stores the handle | ||||||
|  |     void Create(); | ||||||
|  |  | ||||||
|  |     /// Deletes the internal OpenGL resource | ||||||
|  |     void Release() override; | ||||||
|  | }; | ||||||
| @@ -42,4 +42,296 @@ void main() { | |||||||
| } | } | ||||||
| )"; | )"; | ||||||
|  |  | ||||||
|  | const char g_vertex_shader_hw[] = R"( | ||||||
|  | #version 150 core | ||||||
|  |  | ||||||
|  | in vec4 vert_position; | ||||||
|  | in vec4 vert_color; | ||||||
|  | in vec2 vert_texcoords[3]; | ||||||
|  |  | ||||||
|  | out vec4 o[7]; | ||||||
|  |  | ||||||
|  | uniform int out_maps[7*4]; | ||||||
|  |  | ||||||
|  | void SetVal(int map_idx, float val) { | ||||||
|  |     o[out_maps[map_idx] / 4][out_maps[map_idx] % 4] = val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void main() { | ||||||
|  |     SetVal(8, vert_color.x); | ||||||
|  |     SetVal(9, vert_color.y); | ||||||
|  |     SetVal(10, vert_color.z); | ||||||
|  |     SetVal(11, vert_color.w); | ||||||
|  |     SetVal(12, vert_texcoords[0].x); | ||||||
|  |     SetVal(13, vert_texcoords[0].y); | ||||||
|  |  | ||||||
|  |     // TODO: These seem like the wrong map indices | ||||||
|  |     SetVal(14, vert_texcoords[1].x); | ||||||
|  |     SetVal(15, vert_texcoords[1].y); | ||||||
|  |     SetVal(16, vert_texcoords[2].x); | ||||||
|  |     SetVal(17, vert_texcoords[2].y); | ||||||
|  |  | ||||||
|  |     gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); | ||||||
|  | } | ||||||
|  | )"; | ||||||
|  |  | ||||||
|  | // TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms | ||||||
|  | const char g_fragment_shader_hw[] = R"( | ||||||
|  | #version 150 core | ||||||
|  |  | ||||||
|  | in vec4 o[7]; | ||||||
|  | out vec4 color; | ||||||
|  |  | ||||||
|  | uniform int alphatest_func; | ||||||
|  | uniform float alphatest_ref; | ||||||
|  |  | ||||||
|  | uniform sampler2D tex[3]; | ||||||
|  |  | ||||||
|  | struct TEVConfig | ||||||
|  | { | ||||||
|  |     ivec3 color_sources; | ||||||
|  |     ivec3 alpha_sources; | ||||||
|  |     ivec3 color_modifiers; | ||||||
|  |     ivec3 alpha_modifiers; | ||||||
|  |     ivec2 color_alpha_op; | ||||||
|  |     vec2 color_alpha_multiplier; | ||||||
|  |     vec4 const_color; | ||||||
|  |     bvec2 updates_combiner_buffer_color_alpha; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | uniform TEVConfig tev_cfgs[6]; | ||||||
|  |  | ||||||
|  | uniform int out_maps[7*4]; | ||||||
|  |  | ||||||
|  | vec4 g_combiner_buffer; | ||||||
|  | vec4 g_last_tex_env_out; | ||||||
|  | vec4 g_const_color; | ||||||
|  |  | ||||||
|  | float GetVal(int map_idx) { | ||||||
|  |     return o[out_maps[map_idx] / 4][out_maps[map_idx] % 4]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | vec4 GetSource(int source) { | ||||||
|  |     if (source == 0) { | ||||||
|  |         // HACK: Should use values 8/9/10/11 but hurts framerate | ||||||
|  |         return o[out_maps[8] >> 2]; | ||||||
|  |     } | ||||||
|  |     else if (source == 1) { | ||||||
|  |         return o[out_maps[8] >> 2]; | ||||||
|  |     } | ||||||
|  |     else if (source == 3) { | ||||||
|  |         return texture(tex[0], vec2(GetVal(12), GetVal(13))); | ||||||
|  |     } | ||||||
|  |     else if (source == 4) { | ||||||
|  |         return texture(tex[1], vec2(GetVal(14), GetVal(15))); | ||||||
|  |     } | ||||||
|  |     else if (source == 5) { | ||||||
|  |         // TODO: Unverified | ||||||
|  |         return texture(tex[2], vec2(GetVal(16), GetVal(17))); | ||||||
|  |     } | ||||||
|  |     else if (source == 6) { | ||||||
|  |         // TODO: no 4th texture? | ||||||
|  |     } | ||||||
|  |     else if (source == 13) { | ||||||
|  |         return g_combiner_buffer; | ||||||
|  |     } | ||||||
|  |     else if (source == 14) { | ||||||
|  |         return g_const_color; | ||||||
|  |     } | ||||||
|  |     else if (source == 15) { | ||||||
|  |         return g_last_tex_env_out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return vec4(0.0, 0.0, 0.0, 0.0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | vec3 GetColorModifier(int factor, vec4 color) { | ||||||
|  |     if (factor == 0) { | ||||||
|  |         return color.rgb; | ||||||
|  |     } | ||||||
|  |     else if (factor == 1) { | ||||||
|  |         return vec3(1.0, 1.0, 1.0) - color.rgb; | ||||||
|  |     } | ||||||
|  |     else if (factor == 2) { | ||||||
|  |         return color.aaa; | ||||||
|  |     } | ||||||
|  |     else if (factor == 3) { | ||||||
|  |         return vec3(1.0, 1.0, 1.0) - color.aaa; | ||||||
|  |     } | ||||||
|  |     else if (factor == 4) { | ||||||
|  |         return color.rrr; | ||||||
|  |     } | ||||||
|  |     else if (factor == 5) { | ||||||
|  |         return vec3(1.0, 1.0, 1.0) - color.rrr; | ||||||
|  |     } | ||||||
|  |     else if (factor == 8) { | ||||||
|  |         return color.ggg; | ||||||
|  |     } | ||||||
|  |     else if (factor == 9) { | ||||||
|  |         return vec3(1.0, 1.0, 1.0) - color.ggg; | ||||||
|  |     } | ||||||
|  |     else if (factor == 12) { | ||||||
|  |         return color.bbb; | ||||||
|  |     } | ||||||
|  |     else if (factor == 13) { | ||||||
|  |         return vec3(1.0, 1.0, 1.0) - color.bbb; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return vec3(0.0, 0.0, 0.0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float GetAlphaModifier(int factor, vec4 color) { | ||||||
|  |     if (factor == 0) { | ||||||
|  |         return color.a; | ||||||
|  |     } | ||||||
|  |     else if (factor == 1) { | ||||||
|  |         return 1.0 - color.a; | ||||||
|  |     } | ||||||
|  |     else if (factor == 2) { | ||||||
|  |         return color.r; | ||||||
|  |     } | ||||||
|  |     else if (factor == 3) { | ||||||
|  |         return 1.0 - color.r; | ||||||
|  |     } | ||||||
|  |     else if (factor == 4) { | ||||||
|  |         return color.g; | ||||||
|  |     } | ||||||
|  |     else if (factor == 5) { | ||||||
|  |         return 1.0 - color.g; | ||||||
|  |     } | ||||||
|  |     else if (factor == 6) { | ||||||
|  |         return color.b; | ||||||
|  |     } | ||||||
|  |     else if (factor == 7) { | ||||||
|  |         return 1.0 - color.b; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0.0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | vec3 ColorCombine(int op, vec3 color[3]) { | ||||||
|  |     if (op == 0) { | ||||||
|  |         return color[0]; | ||||||
|  |     } | ||||||
|  |     else if (op == 1) { | ||||||
|  |         return color[0] * color[1]; | ||||||
|  |     } | ||||||
|  |     else if (op == 2) { | ||||||
|  |         return min(color[0] + color[1], 1.0); | ||||||
|  |     } | ||||||
|  |     else if (op == 3) { | ||||||
|  |         return color[0] + color[1] - vec3(0.5, 0.5, 0.5); | ||||||
|  |     } | ||||||
|  |     else if (op == 4) { | ||||||
|  |         return color[0] * color[2] + color[1] * (vec3(1.0, 1.0, 1.0) - color[2]); | ||||||
|  |     } | ||||||
|  |     else if (op == 5) { | ||||||
|  |         return max(color[0] - color[1], 0.0); | ||||||
|  |     } | ||||||
|  |     else if (op == 8) { | ||||||
|  |         return min(color[0] * color[1] + color[2], 1.0); | ||||||
|  |     } | ||||||
|  |     else if (op == 9) { | ||||||
|  |         return min(color[0] + color[1], 1.0) * color[2]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return vec3(0.0, 0.0, 0.0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float AlphaCombine(int op, float alpha[3]) { | ||||||
|  |     if (op == 0) { | ||||||
|  |         return alpha[0]; | ||||||
|  |     } | ||||||
|  |     else if (op == 1) { | ||||||
|  |         return alpha[0] * alpha[1]; | ||||||
|  |     } | ||||||
|  |     else if (op == 2) { | ||||||
|  |         return min(alpha[0] + alpha[1], 1.0); | ||||||
|  |     } | ||||||
|  |     else if (op == 3) { | ||||||
|  |         return alpha[0] + alpha[1] - 0.5; | ||||||
|  |     } | ||||||
|  |     else if (op == 4) { | ||||||
|  |         return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]); | ||||||
|  |     } | ||||||
|  |     else if (op == 5) { | ||||||
|  |         return max(alpha[0] - alpha[1], 0.0); | ||||||
|  |     } | ||||||
|  |     else if (op == 8) { | ||||||
|  |         return min(alpha[0] * alpha[1] + alpha[2], 1.0); | ||||||
|  |     } | ||||||
|  |     else if (op == 9) { | ||||||
|  |         return min(alpha[0] + alpha[1], 1.0) * alpha[2]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0.0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ProcessTexEnv(int tex_env_idx) { | ||||||
|  |     g_const_color = tev_cfgs[tex_env_idx].const_color; | ||||||
|  |  | ||||||
|  |     vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)), | ||||||
|  |                                     GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)), | ||||||
|  |                                     GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z))); | ||||||
|  |     vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results); | ||||||
|  |  | ||||||
|  |     float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)), | ||||||
|  |                                       GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)), | ||||||
|  |                                       GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z))); | ||||||
|  |     float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results); | ||||||
|  |  | ||||||
|  |     g_last_tex_env_out = vec4(min(color_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.x, 1.0), min(alpha_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.y, 1.0)); | ||||||
|  |  | ||||||
|  |     if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) { | ||||||
|  |         g_combiner_buffer.rgb = g_last_tex_env_out.rgb; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) { | ||||||
|  |         g_combiner_buffer.a = g_last_tex_env_out.a; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void main(void) { | ||||||
|  |     for (int i = 0; i < 6; ++i) { | ||||||
|  |         ProcessTexEnv(i); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (alphatest_func == 0) { | ||||||
|  |         discard; | ||||||
|  |     } | ||||||
|  |     else if (alphatest_func == 2) { | ||||||
|  |         if (g_last_tex_env_out.a != alphatest_ref) { | ||||||
|  |             discard; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (alphatest_func == 3) { | ||||||
|  |         if (g_last_tex_env_out.a == alphatest_ref) { | ||||||
|  |             discard; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (alphatest_func == 4) { | ||||||
|  |         if (g_last_tex_env_out.a > alphatest_ref) { | ||||||
|  |             discard; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (alphatest_func == 5) { | ||||||
|  |         if (g_last_tex_env_out.a >= alphatest_ref) { | ||||||
|  |             discard; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (alphatest_func == 6) { | ||||||
|  |         if (g_last_tex_env_out.a < alphatest_ref) { | ||||||
|  |             discard; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (alphatest_func == 7) { | ||||||
|  |         if (g_last_tex_env_out.a <= alphatest_ref) { | ||||||
|  |             discard; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     color = g_last_tex_env_out; | ||||||
|  | } | ||||||
|  | )"; | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										162
									
								
								src/video_core/renderer_opengl/gl_state.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/video_core/renderer_opengl/gl_state.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "video_core/renderer_opengl/gl_state.h" | ||||||
|  |  | ||||||
|  | OpenGLState OpenGLState::cur_state; | ||||||
|  |  | ||||||
|  | OpenGLState::OpenGLState() | ||||||
|  | { | ||||||
|  |     // These all match default OpenGL values | ||||||
|  |     cull.enabled = false; | ||||||
|  |     cull.mode = GL_BACK; | ||||||
|  |  | ||||||
|  |     depth.test_enabled = false; | ||||||
|  |     depth.test_func = GL_LESS; | ||||||
|  |     depth.write_mask = GL_TRUE; | ||||||
|  |  | ||||||
|  |     stencil.test_enabled = false; | ||||||
|  |     stencil.test_func = GL_ALWAYS; | ||||||
|  |     stencil.test_ref = 0; | ||||||
|  |     stencil.test_mask = -1; | ||||||
|  |     stencil.write_mask = -1; | ||||||
|  |  | ||||||
|  |     blend.enabled = false; | ||||||
|  |     blend.src_rgb_func = GL_ONE; | ||||||
|  |     blend.dst_rgb_func = GL_ZERO; | ||||||
|  |     blend.src_a_func = GL_ONE; | ||||||
|  |     blend.dst_a_func = GL_ZERO; | ||||||
|  |     blend.color.red = 0.0f; | ||||||
|  |     blend.color.green = 0.0f; | ||||||
|  |     blend.color.blue = 0.0f; | ||||||
|  |     blend.color.alpha = 0.0f; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 3; i++) { | ||||||
|  |         texture_unit[i].enabled_2d = false; | ||||||
|  |         texture_unit[i].texture_2d = 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     draw.framebuffer = 0; | ||||||
|  |     draw.vertex_array = 0; | ||||||
|  |     draw.vertex_buffer = 0; | ||||||
|  |     draw.shader_program = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Apply this state as the current OpenGL state | ||||||
|  | void OpenGLState::Apply() { | ||||||
|  |     // Culling | ||||||
|  |     if (cull.enabled) { | ||||||
|  |         if (cull.enabled != cur_state.cull.enabled) { | ||||||
|  |             glEnable(GL_CULL_FACE); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cull.mode != cur_state.cull.mode) { | ||||||
|  |             glCullFace(cull.mode); | ||||||
|  |         } | ||||||
|  |     } else if (cull.enabled != cur_state.cull.enabled) { | ||||||
|  |         glDisable(GL_CULL_FACE); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     //Depth test | ||||||
|  |     if (depth.test_enabled) { | ||||||
|  |         if (depth.test_enabled != cur_state.depth.test_enabled) { | ||||||
|  |             glEnable(GL_DEPTH_TEST); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (depth.test_func != cur_state.depth.test_func) { | ||||||
|  |             glDepthFunc(depth.test_func); | ||||||
|  |         } | ||||||
|  |     } else if (depth.test_enabled != cur_state.depth.test_enabled) { | ||||||
|  |         glDisable(GL_DEPTH_TEST); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Depth mask | ||||||
|  |     if (depth.write_mask != cur_state.depth.write_mask) { | ||||||
|  |         glDepthMask(depth.write_mask); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Stencil test | ||||||
|  |     if (stencil.test_enabled) { | ||||||
|  |         if (stencil.test_enabled != cur_state.stencil.test_enabled) { | ||||||
|  |             glEnable(GL_STENCIL_TEST); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (stencil.test_func != cur_state.stencil.test_func || | ||||||
|  |             stencil.test_ref != cur_state.stencil.test_ref || | ||||||
|  |             stencil.test_mask != cur_state.stencil.test_mask) { | ||||||
|  |             glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask); | ||||||
|  |         } | ||||||
|  |     } else if (stencil.test_enabled != cur_state.stencil.test_enabled) { | ||||||
|  |         glDisable(GL_STENCIL_TEST); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Stencil mask | ||||||
|  |     if (stencil.write_mask != cur_state.stencil.write_mask) { | ||||||
|  |         glStencilMask(stencil.write_mask); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Blending | ||||||
|  |     if (blend.enabled) { | ||||||
|  |         if (blend.enabled != cur_state.blend.enabled) { | ||||||
|  |             glEnable(GL_BLEND); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (blend.color.red != cur_state.blend.color.red || | ||||||
|  |             blend.color.green != cur_state.blend.color.green || | ||||||
|  |             blend.color.blue != cur_state.blend.color.blue || | ||||||
|  |             blend.color.alpha != cur_state.blend.color.alpha) { | ||||||
|  |             glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (blend.src_rgb_func != cur_state.blend.src_rgb_func || | ||||||
|  |             blend.dst_rgb_func != cur_state.blend.dst_rgb_func || | ||||||
|  |             blend.src_a_func != cur_state.blend.src_a_func || | ||||||
|  |             blend.dst_a_func != cur_state.blend.dst_a_func) { | ||||||
|  |             glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, blend.dst_a_func); | ||||||
|  |         } | ||||||
|  |     } else if (blend.enabled != cur_state.blend.enabled) { | ||||||
|  |         glDisable(GL_BLEND); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Textures | ||||||
|  |     for (int i = 0; i < 3; i++) { | ||||||
|  |         if (texture_unit[i].enabled_2d) { | ||||||
|  |             if (texture_unit[i].enabled_2d != texture_unit[i].enabled_2d) { | ||||||
|  |                 glActiveTexture(GL_TEXTURE0 + i); | ||||||
|  |                 glEnable(GL_TEXTURE_2D); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (texture_unit[i].texture_2d != cur_state.texture_unit[i].texture_2d) { | ||||||
|  |                 glActiveTexture(GL_TEXTURE0 + i); | ||||||
|  |                 glBindTexture(GL_TEXTURE_2D, texture_unit[i].texture_2d); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else if (texture_unit[i].enabled_2d != cur_state.texture_unit[i].enabled_2d) { | ||||||
|  |             glActiveTexture(GL_TEXTURE0 + i); | ||||||
|  |             glDisable(GL_TEXTURE_2D); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Framebuffer | ||||||
|  |     if (draw.framebuffer != cur_state.draw.framebuffer) { | ||||||
|  |         glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Vertex array | ||||||
|  |     if (draw.vertex_array != cur_state.draw.vertex_array) { | ||||||
|  |         glBindVertexArray(draw.vertex_array); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Vertex buffer | ||||||
|  |     if (draw.vertex_buffer != cur_state.draw.vertex_buffer) { | ||||||
|  |         glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Shader program | ||||||
|  |     if (draw.shader_program != cur_state.draw.shader_program) { | ||||||
|  |         glUseProgram(draw.shader_program); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cur_state = *this; | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								src/video_core/renderer_opengl/gl_state.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/video_core/renderer_opengl/gl_state.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | // Copyright 2015 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "generated/gl_3_2_core.h" | ||||||
|  |  | ||||||
|  | class OpenGLState { | ||||||
|  | public: | ||||||
|  |     struct { | ||||||
|  |         bool enabled; // GL_CULL_FACE | ||||||
|  |         GLenum mode; // GL_CULL_FACE_MODE | ||||||
|  |     } cull; | ||||||
|  |  | ||||||
|  |     struct { | ||||||
|  |         bool test_enabled; // GL_DEPTH_TEST | ||||||
|  |         GLenum test_func; // GL_DEPTH_FUNC | ||||||
|  |         GLboolean write_mask; // GL_DEPTH_WRITEMASK | ||||||
|  |     } depth; | ||||||
|  |  | ||||||
|  |     struct { | ||||||
|  |         bool test_enabled; // GL_STENCIL_TEST | ||||||
|  |         GLenum test_func; // GL_STENCIL_FUNC | ||||||
|  |         GLint test_ref; // GL_STENCIL_REF | ||||||
|  |         GLuint test_mask; // GL_STENCIL_VALUE_MASK | ||||||
|  |         GLuint write_mask; // GL_STENCIL_WRITEMASK | ||||||
|  |     } stencil; | ||||||
|  |  | ||||||
|  |     struct { | ||||||
|  |         bool enabled; // GL_BLEND | ||||||
|  |         GLenum src_rgb_func; // GL_BLEND_SRC_RGB | ||||||
|  |         GLenum dst_rgb_func; // GL_BLEND_DST_RGB | ||||||
|  |         GLenum src_a_func; // GL_BLEND_SRC_ALPHA | ||||||
|  |         GLenum dst_a_func; // GL_BLEND_DST_ALPHA | ||||||
|  |  | ||||||
|  |         struct { | ||||||
|  |             GLclampf red; | ||||||
|  |             GLclampf green; | ||||||
|  |             GLclampf blue; | ||||||
|  |             GLclampf alpha; | ||||||
|  |         } color; // GL_BLEND_COLOR | ||||||
|  |     } blend; | ||||||
|  |  | ||||||
|  |     struct { | ||||||
|  |         bool enabled_2d; // GL_TEXTURE_2D | ||||||
|  |         GLuint texture_2d; // GL_TEXTURE_BINDING_2D | ||||||
|  |     } texture_unit[3]; | ||||||
|  |  | ||||||
|  |     struct { | ||||||
|  |         GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING | ||||||
|  |         GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING | ||||||
|  |         GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING | ||||||
|  |         GLuint shader_program; // GL_CURRENT_PROGRAM | ||||||
|  |     } draw; | ||||||
|  |  | ||||||
|  |     OpenGLState(); | ||||||
|  |  | ||||||
|  |     /// Get the currently active OpenGL state | ||||||
|  |     static OpenGLState GetCurState() { | ||||||
|  |         return cur_state; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /// Apply this state as the current OpenGL state | ||||||
|  |     void Apply(); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     static OpenGLState cur_state; | ||||||
|  | }; | ||||||
| @@ -53,18 +53,22 @@ static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const | |||||||
|  |  | ||||||
| /// RendererOpenGL constructor | /// RendererOpenGL constructor | ||||||
| RendererOpenGL::RendererOpenGL() { | RendererOpenGL::RendererOpenGL() { | ||||||
|  |     hw_rasterizer = new RasterizerOpenGL(); | ||||||
|     resolution_width  = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth); |     resolution_width  = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth); | ||||||
|     resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight; |     resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// RendererOpenGL destructor | /// RendererOpenGL destructor | ||||||
| RendererOpenGL::~RendererOpenGL() { | RendererOpenGL::~RendererOpenGL() { | ||||||
|  |     delete hw_rasterizer; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Swap buffers (render frame) | /// Swap buffers (render frame) | ||||||
| void RendererOpenGL::SwapBuffers() { | void RendererOpenGL::SwapBuffers() { | ||||||
|     render_window->MakeCurrent(); |     render_window->MakeCurrent(); | ||||||
|  |  | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|     for(int i : {0, 1}) { |     for(int i : {0, 1}) { | ||||||
|         const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; |         const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; | ||||||
|  |  | ||||||
| @@ -75,7 +79,7 @@ void RendererOpenGL::SwapBuffers() { | |||||||
|         LCD::Read(color_fill.raw, lcd_color_addr); |         LCD::Read(color_fill.raw, lcd_color_addr); | ||||||
|  |  | ||||||
|         if (color_fill.is_enabled) { |         if (color_fill.is_enabled) { | ||||||
|             LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, textures[i]); |             LoadColorToActiveGLTexture(state, color_fill.color_r, color_fill.color_g, color_fill.color_b, textures[i]); | ||||||
|  |  | ||||||
|             // Resize the texture in case the framebuffer size has changed |             // Resize the texture in case the framebuffer size has changed | ||||||
|             textures[i].width = 1; |             textures[i].width = 1; | ||||||
| @@ -87,9 +91,9 @@ void RendererOpenGL::SwapBuffers() { | |||||||
|                 // Reallocate texture if the framebuffer size has changed. |                 // Reallocate texture if the framebuffer size has changed. | ||||||
|                 // This is expected to not happen very often and hence should not be a |                 // This is expected to not happen very often and hence should not be a | ||||||
|                 // performance problem. |                 // performance problem. | ||||||
|                 ConfigureFramebufferTexture(textures[i], framebuffer); |                 ConfigureFramebufferTexture(state, textures[i], framebuffer); | ||||||
|             } |             } | ||||||
|             LoadFBToActiveGLTexture(framebuffer, textures[i]); |             LoadFBToActiveGLTexture(state, framebuffer, textures[i]); | ||||||
|  |  | ||||||
|             // Resize the texture in case the framebuffer size has changed |             // Resize the texture in case the framebuffer size has changed | ||||||
|             textures[i].width = framebuffer.width; |             textures[i].width = framebuffer.width; | ||||||
| @@ -116,7 +120,7 @@ void RendererOpenGL::SwapBuffers() { | |||||||
| /** | /** | ||||||
|  * Loads framebuffer from emulated memory into the active OpenGL texture. |  * Loads framebuffer from emulated memory into the active OpenGL texture. | ||||||
|  */ |  */ | ||||||
| void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, | void RendererOpenGL::LoadFBToActiveGLTexture(OpenGLState &state, const GPU::Regs::FramebufferConfig& framebuffer, | ||||||
|                                              const TextureInfo& texture) { |                                              const TextureInfo& texture) { | ||||||
|  |  | ||||||
|     const PAddr framebuffer_addr = framebuffer.active_fb == 0 ? |     const PAddr framebuffer_addr = framebuffer.active_fb == 0 ? | ||||||
| @@ -139,7 +143,11 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& | |||||||
|     // only allows rows to have a memory alignement of 4. |     // only allows rows to have a memory alignement of 4. | ||||||
|     ASSERT(pixel_stride % 4 == 0); |     ASSERT(pixel_stride % 4 == 0); | ||||||
|  |  | ||||||
|     glBindTexture(GL_TEXTURE_2D, texture.handle); |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |     state.texture_unit[0].texture_2d = texture.handle; | ||||||
|  |     state.Apply(); | ||||||
|  |      | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); |     glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); | ||||||
|  |  | ||||||
|     // Update existing texture |     // Update existing texture | ||||||
| @@ -151,7 +159,6 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& | |||||||
|                     texture.gl_format, texture.gl_type, framebuffer_data); |                     texture.gl_format, texture.gl_type, framebuffer_data); | ||||||
|  |  | ||||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||||
|     glBindTexture(GL_TEXTURE_2D, 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -159,15 +166,17 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& | |||||||
|  * Since the color is solid, the texture can be 1x1 but will stretch across whatever it's rendered on. |  * Since the color is solid, the texture can be 1x1 but will stretch across whatever it's rendered on. | ||||||
|  * This has the added benefit of being *really fast*. |  * This has the added benefit of being *really fast*. | ||||||
|  */ |  */ | ||||||
| void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, | void RendererOpenGL::LoadColorToActiveGLTexture(OpenGLState &state, u8 color_r, u8 color_g, u8 color_b, | ||||||
|                                                 const TextureInfo& texture) { |                                                 const TextureInfo& texture) { | ||||||
|     glBindTexture(GL_TEXTURE_2D, texture.handle); |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |     state.texture_unit[0].texture_2d = texture.handle; | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|     u8 framebuffer_data[3] = { color_r, color_g, color_b }; |     u8 framebuffer_data[3] = { color_r, color_g, color_b }; | ||||||
|  |  | ||||||
|     // Update existing texture |     // Update existing texture | ||||||
|     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data); |     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data); | ||||||
|     glBindTexture(GL_TEXTURE_2D, 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -175,7 +184,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | |||||||
|  */ |  */ | ||||||
| void RendererOpenGL::InitOpenGLObjects() { | void RendererOpenGL::InitOpenGLObjects() { | ||||||
|     glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); |     glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); | ||||||
|     glDisable(GL_DEPTH_TEST); |     state.depth.test_enabled = false; | ||||||
|  |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |  | ||||||
|     // Link shaders and get variable locations |     // Link shaders and get variable locations | ||||||
|     program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader); |     program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader); | ||||||
| @@ -189,10 +199,12 @@ void RendererOpenGL::InitOpenGLObjects() { | |||||||
|  |  | ||||||
|     // Generate VAO |     // Generate VAO | ||||||
|     glGenVertexArrays(1, &vertex_array_handle); |     glGenVertexArrays(1, &vertex_array_handle); | ||||||
|     glBindVertexArray(vertex_array_handle); |  | ||||||
|  |     state.draw.vertex_array = vertex_array_handle; | ||||||
|  |     state.draw.vertex_buffer = vertex_buffer_handle; | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|     // Attach vertex data to VAO |     // Attach vertex data to VAO | ||||||
|     glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_handle); |  | ||||||
|     glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); |     glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); | ||||||
|     glVertexAttribPointer(attrib_position,  2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, position)); |     glVertexAttribPointer(attrib_position,  2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, position)); | ||||||
|     glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, tex_coord)); |     glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), (GLvoid*)offsetof(ScreenRectVertex, tex_coord)); | ||||||
| @@ -206,17 +218,22 @@ void RendererOpenGL::InitOpenGLObjects() { | |||||||
|         // Allocation of storage is deferred until the first frame, when we |         // Allocation of storage is deferred until the first frame, when we | ||||||
|         // know the framebuffer size. |         // know the framebuffer size. | ||||||
|  |  | ||||||
|         glBindTexture(GL_TEXTURE_2D, texture.handle); |         state.texture_unit[0].enabled_2d = true; | ||||||
|  |         state.texture_unit[0].texture_2d = texture.handle; | ||||||
|  |         state.Apply(); | ||||||
|  |  | ||||||
|  |         glActiveTexture(GL_TEXTURE0); | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||||
|     } |     } | ||||||
|     glBindTexture(GL_TEXTURE_2D, 0); |  | ||||||
|  |     hw_rasterizer->InitObjects(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | void RendererOpenGL::ConfigureFramebufferTexture(OpenGLState &state, TextureInfo& texture, | ||||||
|                                                  const GPU::Regs::FramebufferConfig& framebuffer) { |                                                  const GPU::Regs::FramebufferConfig& framebuffer) { | ||||||
|     GPU::Regs::PixelFormat format = framebuffer.color_format; |     GPU::Regs::PixelFormat format = framebuffer.color_format; | ||||||
|     GLint internal_format; |     GLint internal_format; | ||||||
| @@ -264,7 +281,11 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | |||||||
|         UNIMPLEMENTED(); |         UNIMPLEMENTED(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     glBindTexture(GL_TEXTURE_2D, texture.handle); |     state.texture_unit[0].enabled_2d = true; | ||||||
|  |     state.texture_unit[0].texture_2d = texture.handle; | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|     glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, |     glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, | ||||||
|             texture.gl_format, texture.gl_type, nullptr); |             texture.gl_format, texture.gl_type, nullptr); | ||||||
| } | } | ||||||
| @@ -280,8 +301,10 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x | |||||||
|         ScreenRectVertex(x+w, y+h, 0.f, 1.f), |         ScreenRectVertex(x+w, y+h, 0.f, 1.f), | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     glBindTexture(GL_TEXTURE_2D, texture.handle); |     state.texture_unit[0].enabled_2d = true; | ||||||
|     glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_handle); |     state.texture_unit[0].texture_2d = texture.handle; | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); |     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); | ||||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||||
| } | } | ||||||
| @@ -295,7 +318,8 @@ void RendererOpenGL::DrawScreens() { | |||||||
|     glViewport(0, 0, layout.width, layout.height); |     glViewport(0, 0, layout.width, layout.height); | ||||||
|     glClear(GL_COLOR_BUFFER_BIT); |     glClear(GL_COLOR_BUFFER_BIT); | ||||||
|  |  | ||||||
|     glUseProgram(program_id); |     state.draw.shader_program = program_id; | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|     // Set projection matrix |     // Set projection matrix | ||||||
|     std::array<GLfloat, 3 * 2> ortho_matrix = MakeOrthographicMatrix((float)layout.width, |     std::array<GLfloat, 3 * 2> ortho_matrix = MakeOrthographicMatrix((float)layout.width, | ||||||
| @@ -324,6 +348,7 @@ void RendererOpenGL::UpdateFramerate() { | |||||||
|  */ |  */ | ||||||
| void RendererOpenGL::SetWindow(EmuWindow* window) { | void RendererOpenGL::SetWindow(EmuWindow* window) { | ||||||
|     render_window = window; |     render_window = window; | ||||||
|  |     hw_rasterizer->SetWindow(window); | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Initialize the renderer | /// Initialize the renderer | ||||||
|   | |||||||
| @@ -13,6 +13,8 @@ | |||||||
| #include "core/hw/gpu.h" | #include "core/hw/gpu.h" | ||||||
|  |  | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_state.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||||
|  |  | ||||||
| class EmuWindow; | class EmuWindow; | ||||||
|  |  | ||||||
| @@ -49,17 +51,17 @@ private: | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     void InitOpenGLObjects(); |     void InitOpenGLObjects(); | ||||||
|     static void ConfigureFramebufferTexture(TextureInfo& texture, |     static void ConfigureFramebufferTexture(OpenGLState &state, TextureInfo& texture, | ||||||
|                                             const GPU::Regs::FramebufferConfig& framebuffer); |                                             const GPU::Regs::FramebufferConfig& framebuffer); | ||||||
|     void DrawScreens(); |     void DrawScreens(); | ||||||
|     void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); |     void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); | ||||||
|     void UpdateFramerate(); |     void UpdateFramerate(); | ||||||
|  |  | ||||||
|     // Loads framebuffer from emulated memory into the active OpenGL texture. |     // Loads framebuffer from emulated memory into the active OpenGL texture. | ||||||
|     static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, |     static void LoadFBToActiveGLTexture(OpenGLState &state, const GPU::Regs::FramebufferConfig& framebuffer, | ||||||
|                                         const TextureInfo& texture); |                                         const TextureInfo& texture); | ||||||
|     // Fills active OpenGL texture with the given RGB color. |     // Fills active OpenGL texture with the given RGB color. | ||||||
|     static void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, |     static void LoadColorToActiveGLTexture(OpenGLState &state, u8 color_r, u8 color_g, u8 color_b, | ||||||
|                                            const TextureInfo& texture); |                                            const TextureInfo& texture); | ||||||
|  |  | ||||||
|     /// Computes the viewport rectangle |     /// Computes the viewport rectangle | ||||||
| @@ -71,6 +73,8 @@ private: | |||||||
|     int resolution_width;                         ///< Current resolution width |     int resolution_width;                         ///< Current resolution width | ||||||
|     int resolution_height;                        ///< Current resolution height |     int resolution_height;                        ///< Current resolution height | ||||||
|  |  | ||||||
|  |     OpenGLState state; | ||||||
|  |  | ||||||
|     // OpenGL object IDs |     // OpenGL object IDs | ||||||
|     GLuint vertex_array_handle; |     GLuint vertex_array_handle; | ||||||
|     GLuint vertex_buffer_handle; |     GLuint vertex_buffer_handle; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 tfarley
					tfarley