diff options
Diffstat (limited to 'src/video_core/renderer_opengl')
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 395 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 35 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 285 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.h | 47 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 2 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 4 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_util.cpp | 20 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.h | 2 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/maxwell_to_gl.h | 50 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 241 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 17 |
11 files changed, 734 insertions, 364 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 24cfff229..911890f16 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -14,11 +14,16 @@ #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/vector_math.h" +#include "core/core.h" +#include "core/hle/kernel/process.h" #include "core/settings.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_gen.h" +#include "video_core/renderer_opengl/maxwell_to_gl.h" #include "video_core/renderer_opengl/renderer_opengl.h" +using Maxwell = Tegra::Engines::Maxwell3D::Regs; using PixelFormat = SurfaceParams::PixelFormat; using SurfaceType = SurfaceParams::SurfaceType; @@ -54,6 +59,8 @@ static void SetShaderUniformBlockBindings(GLuint shader) { } RasterizerOpenGL::RasterizerOpenGL() { + shader_dirty = true; + has_ARB_buffer_storage = false; has_ARB_direct_state_access = false; has_ARB_separate_shader_objects = false; @@ -106,8 +113,6 @@ RasterizerOpenGL::RasterizerOpenGL() { state.draw.vertex_buffer = stream_buffer->GetHandle(); pipeline.Create(); - vs_input_index_min = 0; - vs_input_index_max = 0; state.draw.program_pipeline = pipeline.handle; state.draw.shader_program = 0; state.draw.vertex_array = hw_vao.handle; @@ -120,20 +125,14 @@ RasterizerOpenGL::RasterizerOpenGL() { glBufferData(GL_UNIFORM_BUFFER, sizeof(VSUniformData), nullptr, GL_STREAM_COPY); glBindBufferBase(GL_UNIFORM_BUFFER, 1, vs_uniform_buffer.handle); } else { - UNIMPLEMENTED(); + UNREACHABLE(); } accelerate_draw = AccelDraw::Disabled; glEnable(GL_BLEND); - // Sync fixed function OpenGL state - SyncClipEnabled(); - SyncClipCoef(); - SyncCullMode(); - SyncBlendEnabled(); - SyncBlendFuncs(); - SyncBlendColor(); + LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); } RasterizerOpenGL::~RasterizerOpenGL() { @@ -144,47 +143,235 @@ RasterizerOpenGL::~RasterizerOpenGL() { } } -static constexpr std::array<GLenum, 4> vs_attrib_types{ - GL_BYTE, // VertexAttributeFormat::BYTE - GL_UNSIGNED_BYTE, // VertexAttributeFormat::UBYTE - GL_SHORT, // VertexAttributeFormat::SHORT - GL_FLOAT // VertexAttributeFormat::FLOAT -}; - void RasterizerOpenGL::AnalyzeVertexArray(bool is_indexed) { - UNIMPLEMENTED(); + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + if (is_indexed) { + UNREACHABLE(); + } + + // TODO(bunnei): Add support for 1+ vertex arrays + vs_input_size = regs.vertex_buffer.count * regs.vertex_array[0].stride; } void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) { MICROPROFILE_SCOPE(OpenGL_VAO); - UNIMPLEMENTED(); + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + const auto& memory_manager = Core::System().GetInstance().GPU().memory_manager; + + state.draw.vertex_array = hw_vao.handle; + state.draw.vertex_buffer = stream_buffer->GetHandle(); + state.Apply(); + + // TODO(bunnei): Add support for 1+ vertex arrays + const auto& vertex_array{regs.vertex_array[0]}; + ASSERT_MSG(vertex_array.enable, "vertex array 0 is disabled?"); + ASSERT_MSG(!vertex_array.divisor, "vertex array 0 divisor is unimplemented!"); + for (unsigned index = 1; index < Maxwell::NumVertexArrays; ++index) { + ASSERT_MSG(!regs.vertex_array[index].enable, "vertex array %d is unimplemented!", index); + } + + // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. + // Enables the first 16 vertex attributes always, as we don't know which ones are actually used + // until shader time. Note, Tegra technically supports 32, but we're cappinig this to 16 for now + // to avoid OpenGL errors. + for (unsigned index = 0; index < 16; ++index) { + auto& attrib = regs.vertex_attrib_format[index]; + glVertexAttribPointer(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), + attrib.IsNormalized() ? GL_TRUE : GL_FALSE, vertex_array.stride, + reinterpret_cast<GLvoid*>(buffer_offset + attrib.offset)); + glEnableVertexAttribArray(index); + hw_vao_enabled_attributes[index] = true; + } + + // Copy vertex array data + const u32 data_size{vertex_array.stride * regs.vertex_buffer.count}; + const VAddr data_addr{memory_manager->PhysicalToVirtualAddress(vertex_array.StartAddress())}; + res_cache.FlushRegion(data_addr, data_size, nullptr); + Memory::ReadBlock(data_addr, array_ptr, data_size); + + array_ptr += data_size; + buffer_offset += data_size; } void RasterizerOpenGL::SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset) { MICROPROFILE_SCOPE(OpenGL_VS); - UNIMPLEMENTED(); + LOG_CRITICAL(Render_OpenGL, "Emulated shaders are not supported! Using a passthrough shader."); + glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_shader->shader.handle); } void RasterizerOpenGL::SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset) { MICROPROFILE_SCOPE(OpenGL_FS); - UNIMPLEMENTED(); + UNREACHABLE(); } bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { if (!has_ARB_separate_shader_objects) { - UNIMPLEMENTED(); + UNREACHABLE(); return false; } accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; - DrawTriangles(); + DrawArrays(); return true; } -void RasterizerOpenGL::DrawTriangles() { +void RasterizerOpenGL::DrawArrays() { + if (accelerate_draw == AccelDraw::Disabled) + return; + MICROPROFILE_SCOPE(OpenGL_Drawing); - UNIMPLEMENTED(); + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + // TODO(bunnei): Implement these + const bool has_stencil = false; + const bool using_color_fb = true; + const bool using_depth_fb = false; + const MathUtil::Rectangle<s32> viewport_rect{regs.viewport[0].GetRect()}; + + const bool write_color_fb = + state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE || + state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE; + + const bool write_depth_fb = + (state.depth.test_enabled && state.depth.write_mask == GL_TRUE) || + (has_stencil && state.stencil.test_enabled && state.stencil.write_mask != 0); + + Surface color_surface; + Surface depth_surface; + MathUtil::Rectangle<u32> surfaces_rect; + std::tie(color_surface, depth_surface, surfaces_rect) = + res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect); + + const u16 res_scale = color_surface != nullptr + ? color_surface->res_scale + : (depth_surface == nullptr ? 1u : depth_surface->res_scale); + + MathUtil::Rectangle<u32> draw_rect{ + static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.left) + + viewport_rect.left * res_scale, + surfaces_rect.left, surfaces_rect.right)), // Left + static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + + viewport_rect.top * res_scale, + surfaces_rect.bottom, surfaces_rect.top)), // Top + static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.left) + + viewport_rect.right * res_scale, + surfaces_rect.left, surfaces_rect.right)), // Right + static_cast<u32>(MathUtil::Clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + + viewport_rect.bottom * res_scale, + surfaces_rect.bottom, surfaces_rect.top))}; // Bottom + + // Bind the framebuffer surfaces + BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); + + // Sync the viewport + SyncViewport(surfaces_rect, res_scale); + + // TODO(bunnei): Sync framebuffer_scale uniform here + // TODO(bunnei): Sync scissorbox uniform(s) here + // TODO(bunnei): Sync and bind the texture surfaces + + // Sync and bind the shader + if (shader_dirty) { + SetShader(); + shader_dirty = false; + } + + // Sync the uniform data + if (uniform_block_data.dirty) { + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformData), &uniform_block_data.data); + uniform_block_data.dirty = false; + } + + // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable + // scissor test to prevent drawing outside of the framebuffer region + state.scissor.enabled = true; + state.scissor.x = draw_rect.left; + state.scissor.y = draw_rect.bottom; + state.scissor.width = draw_rect.GetWidth(); + state.scissor.height = draw_rect.GetHeight(); + state.Apply(); + + // Draw the vertex batch + const bool is_indexed = accelerate_draw == AccelDraw::Indexed; + AnalyzeVertexArray(is_indexed); + state.draw.vertex_buffer = stream_buffer->GetHandle(); + state.Apply(); + + size_t buffer_size = static_cast<size_t>(vs_input_size); + if (is_indexed) { + UNREACHABLE(); + } + buffer_size += sizeof(VSUniformData); + + size_t ptr_pos = 0; + u8* buffer_ptr; + GLintptr buffer_offset; + std::tie(buffer_ptr, buffer_offset) = + stream_buffer->Map(static_cast<GLsizeiptr>(buffer_size), 4); + + SetupVertexArray(buffer_ptr, buffer_offset); + ptr_pos += vs_input_size; + + GLintptr index_buffer_offset = 0; + if (is_indexed) { + UNREACHABLE(); + } + + SetupVertexShader(reinterpret_cast<VSUniformData*>(&buffer_ptr[ptr_pos]), + buffer_offset + static_cast<GLintptr>(ptr_pos)); + const GLintptr vs_ubo_offset = buffer_offset + static_cast<GLintptr>(ptr_pos); + ptr_pos += sizeof(VSUniformData); + + stream_buffer->Unmap(); + + const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { + if (has_ARB_direct_state_access) { + glCopyNamedBufferSubData(stream_buffer->GetHandle(), handle, offset, 0, size); + } else { + glBindBuffer(GL_COPY_WRITE_BUFFER, handle); + glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, offset, 0, size); + } + }; + + copy_buffer(vs_uniform_buffer.handle, vs_ubo_offset, sizeof(VSUniformData)); + + glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_shader->shader.handle); + + if (is_indexed) { + UNREACHABLE(); + } else { + glDrawArrays(MaxwellToGL::PrimitiveTopology(regs.draw.topology), 0, + regs.vertex_buffer.count); + } + + // Disable scissor test + state.scissor.enabled = false; + + accelerate_draw = AccelDraw::Disabled; + + // Unbind textures for potential future use as framebuffer attachments + for (auto& texture_unit : state.texture_units) { + texture_unit.texture_2d = 0; + } + state.Apply(); + + // Mark framebuffer surfaces as dirty + MathUtil::Rectangle<u32> draw_rect_unscaled{ + draw_rect.left / res_scale, draw_rect.top / res_scale, draw_rect.right / res_scale, + draw_rect.bottom / res_scale}; + + if (color_surface != nullptr && write_color_fb) { + auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled); + res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval), + color_surface); + } + if (depth_surface != nullptr && write_depth_fb) { + auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled); + res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval), + depth_surface); + } } void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {} @@ -194,17 +381,17 @@ void RasterizerOpenGL::FlushAll() { res_cache.FlushAll(); } -void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { +void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, size); } -void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) { +void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.InvalidateRegion(addr, size, nullptr); } -void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) { +void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, size); res_cache.InvalidateRegion(addr, size, nullptr); @@ -212,58 +399,180 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) { bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) { MICROPROFILE_SCOPE(OpenGL_Blits); - UNIMPLEMENTED(); + UNREACHABLE(); return true; } bool RasterizerOpenGL::AccelerateTextureCopy(const void* config) { - UNIMPLEMENTED(); + UNREACHABLE(); return true; } bool RasterizerOpenGL::AccelerateFill(const void* config) { - UNIMPLEMENTED(); + UNREACHABLE(); return true; } -bool RasterizerOpenGL::AccelerateDisplay(const void* config, PAddr framebuffer_addr, - u32 pixel_stride, ScreenInfo& screen_info) { - UNIMPLEMENTED(); +bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer, + VAddr framebuffer_addr, u32 pixel_stride, + ScreenInfo& screen_info) { + if (framebuffer_addr == 0) { + return false; + } + MICROPROFILE_SCOPE(OpenGL_CacheManagement); + + SurfaceParams src_params; + src_params.addr = framebuffer_addr; + src_params.width = std::min(framebuffer.width, pixel_stride); + src_params.height = framebuffer.height; + src_params.stride = pixel_stride; + src_params.is_tiled = false; + src_params.pixel_format = + SurfaceParams::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format); + src_params.UpdateParams(); + + MathUtil::Rectangle<u32> src_rect; + Surface src_surface; + std::tie(src_surface, src_rect) = + res_cache.GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true); + + if (src_surface == nullptr) { + return false; + } + + u32 scaled_width = src_surface->GetScaledWidth(); + u32 scaled_height = src_surface->GetScaledHeight(); + + screen_info.display_texcoords = MathUtil::Rectangle<float>( + (float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width, + (float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width); + + screen_info.display_texture = src_surface->texture.handle; + return true; } void RasterizerOpenGL::SetShader() { - UNIMPLEMENTED(); + // TODO(bunnei): The below sets up a static test shader for passing untransformed vertices to + // OpenGL for rendering. This should be removed/replaced when we start emulating Maxwell + // shaders. + + static constexpr char vertex_shader[] = R"( +#version 150 core + +in vec2 vert_position; +in vec2 vert_tex_coord; +out vec2 frag_tex_coord; + +void main() { + // Multiply input position by the rotscale part of the matrix and then manually translate by + // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector + // to `vec3(vert_position.xy, 1.0)` + gl_Position = vec4(mat2(mat3x2(0.0015625f, 0.0, 0.0, -0.0027778, -1.0, 1.0)) * vert_position + mat3x2(0.0015625f, 0.0, 0.0, -0.0027778, -1.0, 1.0)[2], 0.0, 1.0); + frag_tex_coord = vert_tex_coord; +} +)"; + + static constexpr char fragment_shader[] = R"( +#version 150 core + +in vec2 frag_tex_coord; +out vec4 color; + +uniform sampler2D color_texture; + +void main() { + color = vec4(1.0, 0.0, 1.0, 0.0); +} +)"; + + if (current_shader) { + return; + } + + LOG_CRITICAL(Render_OpenGL, "Emulated shaders are not supported! Using a passthrough shader."); + + current_shader = &test_shader; + if (has_ARB_separate_shader_objects) { + test_shader.shader.Create(vertex_shader, nullptr, fragment_shader, {}, true); + glActiveShaderProgram(pipeline.handle, test_shader.shader.handle); + } else { + UNREACHABLE(); + } + + state.draw.shader_program = test_shader.shader.handle; + state.Apply(); + + if (has_ARB_separate_shader_objects) { + state.draw.shader_program = 0; + state.Apply(); + } +} + +void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, + const Surface& depth_surface, bool has_stencil) { + state.draw.draw_framebuffer = framebuffer.handle; + state.Apply(); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + color_surface != nullptr ? color_surface->texture.handle : 0, 0); + if (depth_surface != nullptr) { + if (has_stencil) { + // attach both depth and stencil + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, + depth_surface->texture.handle, 0); + } else { + // attach depth + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + depth_surface->texture.handle, 0); + // clear stencil attachment + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + } + } else { + // clear both depth and stencil attachment + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, + 0); + } +} + +void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale) { + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + const MathUtil::Rectangle<s32> viewport_rect{regs.viewport[0].GetRect()}; + + state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left * res_scale; + state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale; + state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth() * res_scale); + state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight() * res_scale); } void RasterizerOpenGL::SyncClipEnabled() { - UNIMPLEMENTED(); + UNREACHABLE(); } void RasterizerOpenGL::SyncClipCoef() { - UNIMPLEMENTED(); + UNREACHABLE(); } void RasterizerOpenGL::SyncCullMode() { - UNIMPLEMENTED(); + UNREACHABLE(); } void RasterizerOpenGL::SyncDepthScale() { - UNIMPLEMENTED(); + UNREACHABLE(); } void RasterizerOpenGL::SyncDepthOffset() { - UNIMPLEMENTED(); + UNREACHABLE(); } void RasterizerOpenGL::SyncBlendEnabled() { - UNIMPLEMENTED(); + UNREACHABLE(); } void RasterizerOpenGL::SyncBlendFuncs() { - UNIMPLEMENTED(); + UNREACHABLE(); } void RasterizerOpenGL::SyncBlendColor() { - UNIMPLEMENTED(); + UNREACHABLE(); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 893fc530f..fd53e94cd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -29,19 +29,25 @@ public: RasterizerOpenGL(); ~RasterizerOpenGL() override; - void DrawTriangles() override; + void DrawArrays() override; void NotifyMaxwellRegisterChanged(u32 id) override; void FlushAll() override; - void FlushRegion(PAddr addr, u32 size) override; - void InvalidateRegion(PAddr addr, u32 size) override; - void FlushAndInvalidateRegion(PAddr addr, u32 size) override; + void FlushRegion(VAddr addr, u64 size) override; + void InvalidateRegion(VAddr addr, u64 size) override; + void FlushAndInvalidateRegion(VAddr addr, u64 size) override; bool AccelerateDisplayTransfer(const void* config) override; bool AccelerateTextureCopy(const void* config) override; bool AccelerateFill(const void* config) override; - bool AccelerateDisplay(const void* config, PAddr framebuffer_addr, u32 pixel_stride, - ScreenInfo& screen_info) override; + bool AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer, VAddr framebuffer_addr, + u32 pixel_stride, ScreenInfo& screen_info) override; bool AccelerateDrawBatch(bool is_indexed) override; + /// OpenGL shader generated for a given Maxwell register state + struct MaxwellShader { + /// OpenGL shader resource + OGLShader shader; + }; + struct VertexShader { OGLShader shader; }; @@ -81,6 +87,13 @@ public: private: struct SamplerInfo {}; + /// Binds the framebuffer color and depth surface + void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, + bool has_stencil); + + /// Syncs the viewport to match the guest state + void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale); + /// Syncs the clip enabled status to match the guest state void SyncClipEnabled(); @@ -117,6 +130,12 @@ private: RasterizerCacheOpenGL res_cache; + /// Shader used for test renderering - to be removed once we have emulated shaders + MaxwellShader test_shader{}; + + const MaxwellShader* current_shader{}; + bool shader_dirty{}; + struct { UniformData data; bool dirty; @@ -127,7 +146,7 @@ private: OGLVertexArray hw_vao; std::array<bool, 16> hw_vao_enabled_attributes; - std::array<SamplerInfo, 3> texture_samplers; + std::array<SamplerInfo, 32> texture_samplers; static constexpr size_t VERTEX_BUFFER_SIZE = 128 * 1024 * 1024; std::unique_ptr<OGLStreamBuffer> vertex_buffer; OGLBuffer uniform_buffer; @@ -136,8 +155,6 @@ private: static constexpr size_t STREAM_BUFFER_SIZE = 4 * 1024 * 1024; std::unique_ptr<OGLStreamBuffer> stream_buffer; - GLint vs_input_index_min; - GLint vs_input_index_max; GLsizeiptr vs_input_size; void AnalyzeVertexArray(bool is_indexed); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 884637ca5..2ffbd3bab 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -21,9 +21,13 @@ #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/vector_math.h" +#include "core/core.h" #include "core/frontend/emu_window.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/vm_manager.h" #include "core/memory.h" #include "core/settings.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_state.h" #include "video_core/utils.h" @@ -107,67 +111,28 @@ static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) { } template <bool morton_to_gl, PixelFormat format> -static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, PAddr base, PAddr start, PAddr end) { +static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) { constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8; - constexpr u32 tile_size = bytes_per_pixel * 64; - constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); - static_assert(gl_bytes_per_pixel >= bytes_per_pixel, ""); - gl_buffer += gl_bytes_per_pixel - bytes_per_pixel; - - const PAddr aligned_down_start = base + Common::AlignDown(start - base, tile_size); - const PAddr aligned_start = base + Common::AlignUp(start - base, tile_size); - const PAddr aligned_end = base + Common::AlignDown(end - base, tile_size); - - ASSERT(!morton_to_gl || (aligned_start == start && aligned_end == end)); - - const u64 begin_pixel_index = (aligned_down_start - base) / bytes_per_pixel; - u32 x = static_cast<u32>((begin_pixel_index % (stride * 8)) / 8); - u32 y = static_cast<u32>((begin_pixel_index / (stride * 8)) * 8); - - gl_buffer += ((height - 8 - y) * stride + x) * gl_bytes_per_pixel; - - auto glbuf_next_tile = [&] { - x = (x + 8) % stride; - gl_buffer += 8 * gl_bytes_per_pixel; - if (!x) { - y += 8; - gl_buffer -= stride * 9 * gl_bytes_per_pixel; - } - }; - u8* tile_buffer = Memory::GetPhysicalPointer(start); - - if (start < aligned_start && !morton_to_gl) { - std::array<u8, tile_size> tmp_buf; - MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer); - std::memcpy(tile_buffer, &tmp_buf[start - aligned_down_start], - std::min(aligned_start, end) - start); - - tile_buffer += aligned_start - start; - glbuf_next_tile(); - } - - const u8* const buffer_end = tile_buffer + aligned_end - aligned_start; - while (tile_buffer < buffer_end) { - MortonCopyTile<morton_to_gl, format>(stride, tile_buffer, gl_buffer); - tile_buffer += tile_size; - glbuf_next_tile(); - } - - if (end > std::max(aligned_start, aligned_end) && !morton_to_gl) { - std::array<u8, tile_size> tmp_buf; - MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer); - std::memcpy(tile_buffer, &tmp_buf[0], end - aligned_end); - } + // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check the + // configuration for this and perform more generic un/swizzle + LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); + VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, + Memory::GetPointer(base), gl_buffer, morton_to_gl); } -static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> morton_to_gl_fns = { - MortonCopy<true, PixelFormat::RGBA8>, // 0 - MortonCopy<true, PixelFormat::RGB8>, // 1 - MortonCopy<true, PixelFormat::RGB5A1>, // 2 - MortonCopy<true, PixelFormat::RGB565>, // 3 - MortonCopy<true, PixelFormat::RGBA4>, // 4 +static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 18> morton_to_gl_fns = { + MortonCopy<true, PixelFormat::RGBA8>, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, nullptr, nullptr, nullptr, @@ -176,19 +141,19 @@ static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> mo nullptr, nullptr, nullptr, - nullptr, // 5 - 13 - MortonCopy<true, PixelFormat::D16>, // 14 - nullptr, // 15 - MortonCopy<true, PixelFormat::D24>, // 16 - MortonCopy<true, PixelFormat::D24S8> // 17 }; -static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> gl_to_morton_fns = { - MortonCopy<false, PixelFormat::RGBA8>, // 0 - MortonCopy<false, PixelFormat::RGB8>, // 1 - MortonCopy<false, PixelFormat::RGB5A1>, // 2 - MortonCopy<false, PixelFormat::RGB565>, // 3 - MortonCopy<false, PixelFormat::RGBA4>, // 4 +static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 18> gl_to_morton_fns = { + MortonCopy<false, PixelFormat::RGBA8>, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, nullptr, nullptr, nullptr, @@ -197,11 +162,6 @@ static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> gl nullptr, nullptr, nullptr, - nullptr, // 5 - 13 - MortonCopy<false, PixelFormat::D16>, // 14 - nullptr, // 15 - MortonCopy<false, PixelFormat::D24>, // 16 - MortonCopy<false, PixelFormat::D24S8> // 17 }; // Allocate an uninitialized texture of appropriate size and format for the surface @@ -290,17 +250,17 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec static bool FillSurface(const Surface& surface, const u8* fill_data, const MathUtil::Rectangle<u32>& fill_rect, GLuint draw_fb_handle) { - UNIMPLEMENTED(); - return true; + UNREACHABLE(); + return {}; } SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const { SurfaceParams params = *this; const u32 tiled_size = is_tiled ? 8 : 1; const u64 stride_tiled_bytes = BytesInPixels(stride * tiled_size); - PAddr aligned_start = + VAddr aligned_start = addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes); - PAddr aligned_end = + VAddr aligned_end = addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes); if (aligned_end - aligned_start > stride_tiled_bytes) { @@ -527,10 +487,10 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac } MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); -void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) { +void CachedSurface::LoadGLBuffer(VAddr load_start, VAddr load_end) { ASSERT(type != SurfaceType::Fill); - const u8* const texture_src_data = Memory::GetPhysicalPointer(addr); + u8* const texture_src_data = Memory::GetPointer(addr); if (texture_src_data == nullptr) return; @@ -539,35 +499,30 @@ void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) { gl_buffer.reset(new u8[gl_buffer_size]); } - // TODO: Should probably be done in ::Memory:: and check for other regions too - if (load_start < Memory::VRAM_VADDR_END && load_end > Memory::VRAM_VADDR_END) - load_end = Memory::VRAM_VADDR_END; - - if (load_start < Memory::VRAM_VADDR && load_end > Memory::VRAM_VADDR) - load_start = Memory::VRAM_VADDR; - MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); ASSERT(load_start >= addr && load_end <= end); - const u32 start_offset = load_start - addr; + const u64 start_offset = load_start - addr; if (!is_tiled) { ASSERT(type == SurfaceType::Color); - std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset, - load_end - load_start); + const u32 bytes_per_pixel{GetFormatBpp() >> 3}; + + // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check + // the configuration for this and perform more generic un/swizzle + LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); + VideoCore::MortonCopyPixels128(width, height, bytes_per_pixel, 4, + texture_src_data + start_offset, &gl_buffer[start_offset], + true); } else { - if (type == SurfaceType::Texture) { - UNIMPLEMENTED(); - } else { - morton_to_gl_fns[static_cast<size_t>(pixel_format)](stride, height, &gl_buffer[0], addr, - load_start, load_end); - } + morton_to_gl_fns[static_cast<size_t>(pixel_format)](stride, height, &gl_buffer[0], addr, + load_start, load_end); } } MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); -void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) { - u8* const dst_buffer = Memory::GetPhysicalPointer(addr); +void CachedSurface::FlushGLBuffer(VAddr flush_start, VAddr flush_end) { + u8* const dst_buffer = Memory::GetPointer(addr); if (dst_buffer == nullptr) return; @@ -1102,18 +1057,106 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& } Surface RasterizerCacheOpenGL::GetTextureSurface(const void* config) { - UNIMPLEMENTED(); + UNREACHABLE(); return {}; } SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( - bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport_rect) { - UNIMPLEMENTED(); - return {}; + bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) { + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + const auto& memory_manager = Core::System().GetInstance().GPU().memory_manager; + const auto& config = regs.rt[0]; + + // TODO(bunnei): This is hard corded to use just the first render buffer + LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!"); + + // update resolution_scale_factor and reset cache if changed + // TODO (bunnei): This code was ported as-is from Citra, and is technically not thread-safe. We + // need to fix this before making the renderer multi-threaded. + static u16 resolution_scale_factor = GetResolutionScaleFactor(); + if (resolution_scale_factor != GetResolutionScaleFactor()) { + resolution_scale_factor = GetResolutionScaleFactor(); + FlushAll(); + while (!surface_cache.empty()) + UnregisterSurface(*surface_cache.begin()->second.begin()); + } + + MathUtil::Rectangle<u32> viewport_clamped{ + static_cast<u32>(MathUtil::Clamp(viewport.left, 0, static_cast<s32>(config.width))), + static_cast<u32>(MathUtil::Clamp(viewport.top, 0, static_cast<s32>(config.height))), + static_cast<u32>(MathUtil::Clamp(viewport.right, 0, static_cast<s32>(config.width))), + static_cast<u32>(MathUtil::Clamp(viewport.bottom, 0, static_cast<s32>(config.height)))}; + + // get color and depth surfaces + SurfaceParams color_params; + color_params.is_tiled = true; + color_params.res_scale = resolution_scale_factor; + color_params.width = config.width; + color_params.height = config.height; + SurfaceParams depth_params = color_params; + + color_params.addr = memory_manager->PhysicalToVirtualAddress(config.Address()); + color_params.pixel_format = SurfaceParams::PixelFormatFromRenderTargetFormat(config.format); + color_params.UpdateParams(); + + ASSERT_MSG(!using_depth_fb, "depth buffer is unimplemented"); + // depth_params.addr = config.GetDepthBufferPhysicalAddress(); + // depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format); + // depth_params.UpdateParams(); + + auto color_vp_interval = color_params.GetSubRectInterval(viewport_clamped); + auto depth_vp_interval = depth_params.GetSubRectInterval(viewport_clamped); + + // Make sure that framebuffers don't overlap if both color and depth are being used + if (using_color_fb && using_depth_fb && + boost::icl::length(color_vp_interval & depth_vp_interval)) { + LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; " + "overlapping framebuffers not supported!"); + using_depth_fb = false; + } + + MathUtil::Rectangle<u32> color_rect{}; + Surface color_surface = nullptr; + if (using_color_fb) + std::tie(color_surface, color_rect) = + GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); + + MathUtil::Rectangle<u32> depth_rect{}; + Surface depth_surface = nullptr; + if (using_depth_fb) + std::tie(depth_surface, depth_rect) = + GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); + + MathUtil::Rectangle<u32> fb_rect{}; + if (color_surface != nullptr && depth_surface != nullptr) { + fb_rect = color_rect; + // Color and Depth surfaces must have the same dimensions and offsets + if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top || + color_rect.left != depth_rect.left || color_rect.right != depth_rect.right) { + color_surface = GetSurface(color_params, ScaleMatch::Exact, false); + depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false); + fb_rect = color_surface->GetScaledRect(); + } + } else if (color_surface != nullptr) { + fb_rect = color_rect; + } else if (depth_surface != nullptr) { + fb_rect = depth_rect; + } + + if (color_surface != nullptr) { + ValidateSurface(color_surface, boost::icl::first(color_vp_interval), + boost::icl::length(color_vp_interval)); + } + if (depth_surface != nullptr) { + ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval), + boost::icl::length(depth_vp_interval)); + } + + return std::make_tuple(color_surface, depth_surface, fb_rect); } Surface RasterizerCacheOpenGL::GetFillSurface(const void* config) { - UNIMPLEMENTED(); + UNREACHABLE(); return {}; } @@ -1167,7 +1210,7 @@ void RasterizerCacheOpenGL::DuplicateSurface(const Surface& src_surface, } } -void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, u64 size) { +void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, VAddr addr, u64 size) { if (size == 0) return; @@ -1227,7 +1270,7 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, } } -void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u64 size, Surface flush_surface) { +void RasterizerCacheOpenGL::FlushRegion(VAddr addr, u64 size, Surface flush_surface) { if (size == 0) return; @@ -1260,10 +1303,10 @@ void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u64 size, Surface flush_surf } void RasterizerCacheOpenGL::FlushAll() { - FlushRegion(0, 0xFFFFFFFF); + FlushRegion(0, Kernel::VMManager::MAX_ADDRESS); } -void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u64 size, const Surface& region_owner) { +void RasterizerCacheOpenGL::InvalidateRegion(VAddr addr, u64 size, const Surface& region_owner) { if (size == 0) return; @@ -1356,6 +1399,34 @@ void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) { surface_cache.subtract({surface->GetInterval(), SurfaceSet{surface}}); } -void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u64 size, int delta) { - UNIMPLEMENTED(); +void RasterizerCacheOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { + const u64 num_pages = + ((addr + size - 1) >> Memory::PAGE_BITS) - (addr >> Memory::PAGE_BITS) + 1; + const u64 page_start = addr >> Memory::PAGE_BITS; + const u64 page_end = page_start + num_pages; + + // Interval maps will erase segments if count reaches 0, so if delta is negative we have to + // subtract after iterating + const auto pages_interval = PageMap::interval_type::right_open(page_start, page_end); + if (delta > 0) + cached_pages.add({pages_interval, delta}); + + for (const auto& pair : RangeFromInterval(cached_pages, pages_interval)) { + const auto interval = pair.first & pages_interval; + const int count = pair.second; + + const VAddr interval_start_addr = boost::icl::first(interval) << Memory::PAGE_BITS; + const VAddr interval_end_addr = boost::icl::last_next(interval) << Memory::PAGE_BITS; + const u64 interval_size = interval_end_addr - interval_start_addr; + + if (delta > 0 && count == delta) + Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, true); + else if (delta < 0 && count == -delta) + Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, false); + else + ASSERT(count >= 0); + } + + if (delta < 0) + cached_pages.add({pages_interval, delta}); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 17ce0fee7..1f660d30c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -22,15 +22,16 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/math_util.h" +#include "video_core/gpu.h" #include "video_core/renderer_opengl/gl_resource_manager.h" struct CachedSurface; using Surface = std::shared_ptr<CachedSurface>; using SurfaceSet = std::set<Surface>; -using SurfaceRegions = boost::icl::interval_set<PAddr>; -using SurfaceMap = boost::icl::interval_map<PAddr, Surface>; -using SurfaceCache = boost::icl::interval_map<PAddr, SurfaceSet>; +using SurfaceRegions = boost::icl::interval_set<VAddr>; +using SurfaceMap = boost::icl::interval_map<VAddr, Surface>; +using SurfaceCache = boost::icl::interval_map<VAddr, SurfaceSet>; using SurfaceInterval = SurfaceCache::interval_type; static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() && @@ -40,7 +41,7 @@ static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>; using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; -using PageMap = boost::icl::interval_map<u32, int>; +using PageMap = boost::icl::interval_map<u64, int>; enum class ScaleMatch { Exact, // only accept same res scale @@ -115,6 +116,24 @@ struct SurfaceParams { return GetFormatBpp(pixel_format); } + static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { + switch (format) { + case Tegra::RenderTargetFormat::RGBA8_UNORM: + return PixelFormat::RGBA8; + default: + UNREACHABLE(); + } + } + + static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { + switch (format) { + case Tegra::FramebufferConfig::PixelFormat::ABGR8: + return PixelFormat::RGBA8; + default: + UNREACHABLE(); + } + } + static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { SurfaceType a_type = GetFormatType(pixel_format_a); SurfaceType b_type = GetFormatType(pixel_format_b); @@ -211,8 +230,8 @@ struct SurfaceParams { MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const; MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const; - PAddr addr = 0; - PAddr end = 0; + VAddr addr = 0; + VAddr end = 0; u64 size = 0; u32 width = 0; @@ -257,9 +276,9 @@ struct CachedSurface : SurfaceParams { std::unique_ptr<u8[]> gl_buffer; size_t gl_buffer_size = 0; - // Read/Write data in 3DS memory to/from gl_buffer - void LoadGLBuffer(PAddr load_start, PAddr load_end); - void FlushGLBuffer(PAddr flush_start, PAddr flush_end); + // Read/Write data in Switch memory to/from gl_buffer + void LoadGLBuffer(VAddr load_start, VAddr load_end); + void FlushGLBuffer(VAddr flush_start, VAddr flush_end); // Upload/Download data in gl_buffer in/to this surface's texture void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, @@ -298,7 +317,7 @@ public: /// Get the color and depth surfaces based on the framebuffer configuration SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, - const MathUtil::Rectangle<s32>& viewport_rect); + const MathUtil::Rectangle<s32>& viewport); /// Get a surface that matches the fill config Surface GetFillSurface(const void* config); @@ -307,10 +326,10 @@ public: SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params); /// Write any cached resources overlapping the region back to memory (if dirty) - void FlushRegion(PAddr addr, u64 size, Surface flush_surface = nullptr); + void FlushRegion(VAddr addr, u64 size, Surface flush_surface = nullptr); /// Mark region as being invalidated by region_owner (nullptr if 3DS memory) - void InvalidateRegion(PAddr addr, u64 size, const Surface& region_owner); + void InvalidateRegion(VAddr addr, u64 size, const Surface& region_owner); /// Flush all cached resources tracked by this cache manager void FlushAll(); @@ -319,7 +338,7 @@ private: void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface); /// Update surface's texture for given region when necessary - void ValidateSurface(const Surface& surface, PAddr addr, u64 size); + void ValidateSurface(const Surface& surface, VAddr addr, u64 size); /// Create a new surface Surface CreateSurface(const SurfaceParams& params); @@ -331,7 +350,7 @@ private: void UnregisterSurface(const Surface& surface); /// Increase/decrease the number of surface in pages touching the specified region - void UpdatePagesCachedCount(PAddr addr, u64 size, int delta); + void UpdatePagesCachedCount(VAddr addr, u64 size, int delta); SurfaceCache surface_cache; PageMap cached_pages; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 0e0ef18cc..564ea8f9e 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -26,7 +26,7 @@ public: sanitize_mul(sanitize_mul), emit_cb(emit_cb), setemit_cb(setemit_cb) {} std::string Decompile() { - UNIMPLEMENTED(); + UNREACHABLE(); return {}; } diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index f242bce1d..8f3c98800 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -8,12 +8,12 @@ namespace GLShader { std::string GenerateVertexShader(const MaxwellVSConfig& config) { - UNIMPLEMENTED(); + UNREACHABLE(); return {}; } std::string GenerateFragmentShader(const MaxwellFSConfig& config) { - UNIMPLEMENTED(); + UNREACHABLE(); return {}; } diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index a3ba16761..a6c6204d5 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -38,8 +38,8 @@ GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader, if (result == GL_TRUE) { LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]); } else { - LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", - &vertex_shader_error[0]); + LOG_CRITICAL(Render_OpenGL, "Error compiling vertex shader:\n%s", + &vertex_shader_error[0]); } } } @@ -62,8 +62,8 @@ GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader, if (result == GL_TRUE) { LOG_DEBUG(Render_OpenGL, "%s", &geometry_shader_error[0]); } else { - LOG_ERROR(Render_OpenGL, "Error compiling geometry shader:\n%s", - &geometry_shader_error[0]); + LOG_CRITICAL(Render_OpenGL, "Error compiling geometry shader:\n%s", + &geometry_shader_error[0]); } } } @@ -86,8 +86,8 @@ GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader, if (result == GL_TRUE) { LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]); } else { - LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s", - &fragment_shader_error[0]); + LOG_CRITICAL(Render_OpenGL, "Error compiling fragment shader:\n%s", + &fragment_shader_error[0]); } } } @@ -128,20 +128,20 @@ GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader, if (result == GL_TRUE) { LOG_DEBUG(Render_OpenGL, "%s", &program_error[0]); } else { - LOG_ERROR(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]); + LOG_CRITICAL(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]); } } // If the program linking failed at least one of the shaders was probably bad if (result == GL_FALSE) { if (vertex_shader) { - LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader); + LOG_CRITICAL(Render_OpenGL, "Vertex shader:\n%s", vertex_shader); } if (geometry_shader) { - LOG_ERROR(Render_OpenGL, "Geometry shader:\n%s", geometry_shader); + LOG_CRITICAL(Render_OpenGL, "Geometry shader:\n%s", geometry_shader); } if (fragment_shader) { - LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader); + LOG_CRITICAL(Render_OpenGL, "Fragment shader:\n%s", fragment_shader); } } ASSERT_MSG(result == GL_TRUE, "Shader not linked"); diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 940575dfa..c1f4efc8c 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -85,7 +85,7 @@ public: struct { GLuint texture_2d; // GL_TEXTURE_BINDING_2D GLuint sampler; // GL_SAMPLER_BINDING - } texture_units[3]; + } texture_units[32]; struct { GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h new file mode 100644 index 000000000..d847317ac --- /dev/null +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -0,0 +1,50 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <glad/glad.h> +#include "common/common_types.h" +#include "common/logging/log.h" +#include "video_core/engines/maxwell_3d.h" + +namespace MaxwellToGL { + +using Maxwell = Tegra::Engines::Maxwell3D::Regs; + +inline GLenum VertexType(Maxwell::VertexAttribute attrib) { + switch (attrib.type) { + case Maxwell::VertexAttribute::Type::UnsignedNorm: { + + switch (attrib.size) { + case Maxwell::VertexAttribute::Size::Size_8_8_8_8: + return GL_UNSIGNED_BYTE; + } + + LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size=%s", attrib.SizeString().c_str()); + UNREACHABLE(); + return {}; + } + + case Maxwell::VertexAttribute::Type::Float: + return GL_FLOAT; + } + + LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type=%s", attrib.TypeString().c_str()); + UNREACHABLE(); + return {}; +} + +inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::TriangleStrip: + return GL_TRIANGLE_STRIP; + } + LOG_CRITICAL(Render_OpenGL, "Unimplemented primitive topology=%d", topology); + UNREACHABLE(); + return {}; +} + +} // namespace MaxwellToGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 65d38ade5..78b50b227 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -20,6 +20,7 @@ #include "core/settings.h" #include "core/tracer/recorder.h" #include "video_core/renderer_opengl/renderer_opengl.h" +#include "video_core/utils.h" #include "video_core/video_core.h" static const char vertex_shader[] = R"( @@ -98,197 +99,88 @@ RendererOpenGL::RendererOpenGL() = default; RendererOpenGL::~RendererOpenGL() = default; /// Swap buffers (render frame) -void RendererOpenGL::SwapBuffers(boost::optional<const FramebufferInfo&> framebuffer_info) { +void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { + Core::System::GetInstance().perf_stats.EndSystemFrame(); + // Maintain the rasterizer's state as a priority OpenGLState prev_state = OpenGLState::GetCurState(); state.Apply(); - if (framebuffer_info != boost::none) { - // If framebuffer_info is provided, reload it from memory to a texture - if (screen_info.texture.width != (GLsizei)framebuffer_info->width || - screen_info.texture.height != (GLsizei)framebuffer_info->height || - screen_info.texture.pixel_format != framebuffer_info->pixel_format) { + if (framebuffer != boost::none) { + // If framebuffer is provided, reload it from memory to a texture + if (screen_info.texture.width != (GLsizei)framebuffer->width || + screen_info.texture.height != (GLsizei)framebuffer->height || + screen_info.texture.pixel_format != framebuffer->pixel_format) { // Reallocate texture if the framebuffer size has changed. // This is expected to not happen very often and hence should not be a // performance problem. - ConfigureFramebufferTexture(screen_info.texture, *framebuffer_info); + ConfigureFramebufferTexture(screen_info.texture, *framebuffer); } - LoadFBToScreenInfo(*framebuffer_info, screen_info); - } - - DrawScreens(); - Core::System::GetInstance().perf_stats.EndSystemFrame(); + // Load the framebuffer from memory, draw it to the screen, and swap buffers + LoadFBToScreenInfo(*framebuffer, screen_info); + DrawScreen(); + render_window->SwapBuffers(); + } - // Swap buffers render_window->PollEvents(); - render_window->SwapBuffers(); Core::System::GetInstance().frame_limiter.DoFrameLimiting(CoreTiming::GetGlobalTimeUs()); Core::System::GetInstance().perf_stats.BeginSystemFrame(); + // Restore the rasterizer state prev_state.Apply(); RefreshRasterizerSetting(); } -static inline u32 MortonInterleave128(u32 x, u32 y) { - // 128x128 Z-Order coordinate from 2D coordinates - static constexpr u32 xlut[] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042, - 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809, - 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000, - 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043, - 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a, - 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001, - 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048, - 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b, - 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002, - 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049, - 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840, - 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003, - 0x0008, 0x0009, 0x000a, 0x000b, 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a, - 0x004b, 0x0800, 0x0801, 0x0802, 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841, - 0x0842, 0x0843, 0x0848, 0x0849, 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008, - 0x1009, 0x100a, 0x100b, 0x1040, 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b, - 0x1800, 0x1801, 0x1802, 0x1803, 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842, - 0x1843, 0x1848, 0x1849, 0x184a, 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009, - 0x200a, 0x200b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800, - 0x2801, 0x2802, 0x2803, 0x2808, 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843, - 0x2848, 0x2849, 0x284a, 0x284b, 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, - 0x300b, 0x3040, 0x3041, 0x3042, 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801, - 0x3802, 0x3803, 0x3808, 0x3809, 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848, - 0x3849, 0x384a, 0x384b, 0x0000, 0x0001, 0x0002, 0x0003, 0x0008, 0x0009, 0x000a, 0x000b, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0048, 0x0049, 0x004a, 0x004b, 0x0800, 0x0801, 0x0802, - 0x0803, 0x0808, 0x0809, 0x080a, 0x080b, 0x0840, 0x0841, 0x0842, 0x0843, 0x0848, 0x0849, - 0x084a, 0x084b, 0x1000, 0x1001, 0x1002, 0x1003, 0x1008, 0x1009, 0x100a, 0x100b, 0x1040, - 0x1041, 0x1042, 0x1043, 0x1048, 0x1049, 0x104a, 0x104b, 0x1800, 0x1801, 0x1802, 0x1803, - 0x1808, 0x1809, 0x180a, 0x180b, 0x1840, 0x1841, 0x1842, 0x1843, 0x1848, 0x1849, 0x184a, - 0x184b, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009, 0x200a, 0x200b, 0x2040, 0x2041, - 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2800, 0x2801, 0x2802, 0x2803, 0x2808, - 0x2809, 0x280a, 0x280b, 0x2840, 0x2841, 0x2842, 0x2843, 0x2848, 0x2849, 0x284a, 0x284b, - 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x3040, 0x3041, 0x3042, - 0x3043, 0x3048, 0x3049, 0x304a, 0x304b, 0x3800, 0x3801, 0x3802, 0x3803, 0x3808, 0x3809, - 0x380a, 0x380b, 0x3840, 0x3841, 0x3842, 0x3843, 0x3848, 0x3849, 0x384a, 0x384b, - }; - static constexpr u32 ylut[] = { - 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090, - 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124, - 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200, - 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294, - 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330, - 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404, - 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0, - 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534, - 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610, - 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4, - 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780, - 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014, - 0x0020, 0x0024, 0x0030, 0x0034, 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0, - 0x00b4, 0x0100, 0x0104, 0x0110, 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184, - 0x0190, 0x0194, 0x01a0, 0x01a4, 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220, - 0x0224, 0x0230, 0x0234, 0x0280, 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4, - 0x0300, 0x0304, 0x0310, 0x0314, 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390, - 0x0394, 0x03a0, 0x03a4, 0x03b0, 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424, - 0x0430, 0x0434, 0x0480, 0x0484, 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500, - 0x0504, 0x0510, 0x0514, 0x0520, 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594, - 0x05a0, 0x05a4, 0x05b0, 0x05b4, 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630, - 0x0634, 0x0680, 0x0684, 0x0690, 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704, - 0x0710, 0x0714, 0x0720, 0x0724, 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0, - 0x07a4, 0x07b0, 0x07b4, 0x0000, 0x0004, 0x0010, 0x0014, 0x0020, 0x0024, 0x0030, 0x0034, - 0x0080, 0x0084, 0x0090, 0x0094, 0x00a0, 0x00a4, 0x00b0, 0x00b4, 0x0100, 0x0104, 0x0110, - 0x0114, 0x0120, 0x0124, 0x0130, 0x0134, 0x0180, 0x0184, 0x0190, 0x0194, 0x01a0, 0x01a4, - 0x01b0, 0x01b4, 0x0200, 0x0204, 0x0210, 0x0214, 0x0220, 0x0224, 0x0230, 0x0234, 0x0280, - 0x0284, 0x0290, 0x0294, 0x02a0, 0x02a4, 0x02b0, 0x02b4, 0x0300, 0x0304, 0x0310, 0x0314, - 0x0320, 0x0324, 0x0330, 0x0334, 0x0380, 0x0384, 0x0390, 0x0394, 0x03a0, 0x03a4, 0x03b0, - 0x03b4, 0x0400, 0x0404, 0x0410, 0x0414, 0x0420, 0x0424, 0x0430, 0x0434, 0x0480, 0x0484, - 0x0490, 0x0494, 0x04a0, 0x04a4, 0x04b0, 0x04b4, 0x0500, 0x0504, 0x0510, 0x0514, 0x0520, - 0x0524, 0x0530, 0x0534, 0x0580, 0x0584, 0x0590, 0x0594, 0x05a0, 0x05a4, 0x05b0, 0x05b4, - 0x0600, 0x0604, 0x0610, 0x0614, 0x0620, 0x0624, 0x0630, 0x0634, 0x0680, 0x0684, 0x0690, - 0x0694, 0x06a0, 0x06a4, 0x06b0, 0x06b4, 0x0700, 0x0704, 0x0710, 0x0714, 0x0720, 0x0724, - 0x0730, 0x0734, 0x0780, 0x0784, 0x0790, 0x0794, 0x07a0, 0x07a4, 0x07b0, 0x07b4, - }; - return xlut[x % 128] + ylut[y % 128]; -} - -static inline u32 GetMortonOffset128(u32 x, u32 y, u32 bytes_per_pixel) { - // Calculates the offset of the position of the pixel in Morton order - // Framebuffer images are split into 128x128 tiles. - - const unsigned int block_height = 128; - const unsigned int coarse_x = x & ~127; - - u32 i = MortonInterleave128(x, y); - - const unsigned int offset = coarse_x * block_height; - - return (i + offset) * bytes_per_pixel; -} - -static void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixel, u32 gl_bytes_per_pixel, - u8* morton_data, u8* gl_data, bool morton_to_gl) { - u8* data_ptrs[2]; - for (unsigned y = 0; y < height; ++y) { - for (unsigned x = 0; x < width; ++x) { - const u32 coarse_y = y & ~127; - u32 morton_offset = - GetMortonOffset128(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel; - u32 gl_pixel_index = (x + (height - 1 - y) * width) * gl_bytes_per_pixel; - - data_ptrs[morton_to_gl] = morton_data + morton_offset; - data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index]; - - memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); - } - } -} - /** * Loads framebuffer from emulated memory into the active OpenGL texture. */ -void RendererOpenGL::LoadFBToScreenInfo(const FramebufferInfo& framebuffer_info, +void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer, ScreenInfo& screen_info) { - const u32 bpp{FramebufferInfo::BytesPerPixel(framebuffer_info.pixel_format)}; - const u32 size_in_bytes{framebuffer_info.stride * framebuffer_info.height * bpp}; + const u32 bytes_per_pixel{Tegra::FramebufferConfig::BytesPerPixel(framebuffer.pixel_format)}; + const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel}; + const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; - MortonCopyPixels128(framebuffer_info.width, framebuffer_info.height, bpp, 4, - Memory::GetPointer(framebuffer_info.address), gl_framebuffer_data.data(), - true); - - LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%llx(%dx%d), fmt %x", size_in_bytes, - framebuffer_info.address, framebuffer_info.width, framebuffer_info.height, - (int)framebuffer_info.pixel_format); + // Framebuffer orientation handling + framebuffer_transform_flags = framebuffer.transform_flags; // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default // only allows rows to have a memory alignement of 4. - ASSERT(framebuffer_info.stride % 4 == 0); + ASSERT(framebuffer.stride % 4 == 0); - framebuffer_flip_vertical = framebuffer_info.flip_vertical; + if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride, + screen_info)) { + // Reset the screen info's display texture to its own permanent texture + screen_info.display_texture = screen_info.texture.resource.handle; + screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f); - // Reset the screen info's display texture to its own permanent texture - screen_info.display_texture = screen_info.texture.resource.handle; - screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f); + Rasterizer()->FlushRegion(framebuffer_addr, size_in_bytes); - // Memory::RasterizerFlushRegion(framebuffer_info.address, size_in_bytes); + VideoCore::MortonCopyPixels128(framebuffer.width, framebuffer.height, bytes_per_pixel, 4, + Memory::GetPointer(framebuffer_addr), + gl_framebuffer_data.data(), true); - state.texture_units[0].texture_2d = screen_info.texture.resource.handle; - state.Apply(); + state.texture_units[0].texture_2d = screen_info.texture.resource.handle; + state.Apply(); - glActiveTexture(GL_TEXTURE0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)framebuffer_info.stride); + glActiveTexture(GL_TEXTURE0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); - // Update existing texture - // TODO: Test what happens on hardware when you change the framebuffer dimensions so that - // they differ from the LCD resolution. - // TODO: Applications could theoretically crash Citra here by specifying too large - // framebuffer sizes. We should make sure that this cannot happen. - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer_info.width, framebuffer_info.height, - screen_info.texture.gl_format, screen_info.texture.gl_type, - gl_framebuffer_data.data()); + // Update existing texture + // TODO: Test what happens on hardware when you change the framebuffer dimensions so that + // they differ from the LCD resolution. + // TODO: Applications could theoretically crash yuzu here by specifying too large + // framebuffer sizes. We should make sure that this cannot happen. + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, + screen_info.texture.gl_format, screen_info.texture.gl_type, + gl_framebuffer_data.data()); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - state.texture_units[0].texture_2d = 0; - state.Apply(); + state.texture_units[0].texture_2d = 0; + state.Apply(); + } } /** @@ -372,14 +264,14 @@ void RendererOpenGL::InitOpenGLObjects() { } void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, - const FramebufferInfo& framebuffer_info) { + const Tegra::FramebufferConfig& framebuffer) { - texture.width = framebuffer_info.width; - texture.height = framebuffer_info.height; + texture.width = framebuffer.width; + texture.height = framebuffer.height; GLint internal_format; - switch (framebuffer_info.pixel_format) { - case FramebufferInfo::PixelFormat::ABGR8: + switch (framebuffer.pixel_format) { + case Tegra::FramebufferConfig::PixelFormat::ABGR8: // Use RGBA8 and swap in the fragment shader internal_format = GL_RGBA; texture.gl_format = GL_RGBA; @@ -387,7 +279,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, gl_framebuffer_data.resize(texture.width * texture.height * 4); break; default: - UNIMPLEMENTED(); + UNREACHABLE(); } state.texture_units[0].texture_2d = texture.resource.handle; @@ -401,11 +293,22 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, state.Apply(); } -void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, - float h) { +void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, + float h) { const auto& texcoords = screen_info.display_texcoords; - const auto& left = framebuffer_flip_vertical ? texcoords.right : texcoords.left; - const auto& right = framebuffer_flip_vertical ? texcoords.left : texcoords.right; + auto left = texcoords.left; + auto right = texcoords.right; + if (framebuffer_transform_flags != Tegra::FramebufferConfig::TransformFlags::Unset) + if (framebuffer_transform_flags == Tegra::FramebufferConfig::TransformFlags::FlipV) { + // Flip the framebuffer vertically + left = texcoords.right; + right = texcoords.left; + } else { + // Other transformations are unsupported + LOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags=%d", + framebuffer_transform_flags); + UNIMPLEMENTED(); + } std::array<ScreenRectVertex, 4> vertices = {{ ScreenRectVertex(x, y, texcoords.top, right), @@ -427,7 +330,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl /** * Draws the emulated screens to the emulator window. */ -void RendererOpenGL::DrawScreens() { +void RendererOpenGL::DrawScreen() { const auto& layout = render_window->GetFramebufferLayout(); const auto& screen = layout.screen; @@ -443,8 +346,8 @@ void RendererOpenGL::DrawScreens() { glActiveTexture(GL_TEXTURE0); glUniform1i(uniform_color_texture, 0); - DrawSingleScreen(screen_info, (float)screen.left, (float)screen.top, (float)screen.GetWidth(), - (float)screen.GetHeight()); + DrawScreenTriangles(screen_info, (float)screen.left, (float)screen.top, + (float)screen.GetWidth(), (float)screen.GetHeight()); m_current_frame++; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 05bb3c5cf..fffd0f9f4 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -21,7 +21,7 @@ struct TextureInfo { GLsizei height; GLenum gl_format; GLenum gl_type; - RendererBase::FramebufferInfo::PixelFormat pixel_format; + Tegra::FramebufferConfig::PixelFormat pixel_format; }; /// Structure used for storing information about the display target for each 3DS screen @@ -37,7 +37,7 @@ public: ~RendererOpenGL() override; /// Swap buffers (render frame) - void SwapBuffers(boost::optional<const FramebufferInfo&> framebuffer_info) override; + void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) override; /** * Set the emulator window to use for renderer @@ -53,13 +53,14 @@ public: private: void InitOpenGLObjects(); - void ConfigureFramebufferTexture(TextureInfo& texture, const FramebufferInfo& framebuffer_info); - void DrawScreens(); - void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h); + void ConfigureFramebufferTexture(TextureInfo& texture, + const Tegra::FramebufferConfig& framebuffer); + void DrawScreen(); + void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); void UpdateFramerate(); // Loads framebuffer from emulated memory into the display information structure - void LoadFBToScreenInfo(const FramebufferInfo& framebuffer_info, ScreenInfo& screen_info); + void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer, ScreenInfo& screen_info); // Fills active OpenGL texture with the given RGBA color. void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, const TextureInfo& texture); @@ -87,6 +88,6 @@ private: GLuint attrib_position; GLuint attrib_tex_coord; - /// Flips the framebuffer vertically when true - bool framebuffer_flip_vertical; + /// Used for transforming the framebuffer orientation + Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; }; |