diff options
Diffstat (limited to '')
17 files changed, 797 insertions, 485 deletions
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 075192c3f..46a6c0308 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -76,7 +76,7 @@ std::tuple<u8*, GLintptr> OGLBufferCache::ReserveMemory(std::size_t size, std::s return std::make_tuple(uploaded_ptr, uploaded_offset); } -void OGLBufferCache::Map(std::size_t max_size) { +bool OGLBufferCache::Map(std::size_t max_size) { bool invalidate; std::tie(buffer_ptr, buffer_offset_base, invalidate) = stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4); @@ -85,6 +85,7 @@ void OGLBufferCache::Map(std::size_t max_size) { if (invalidate) { InvalidateAll(); } + return invalidate; } void OGLBufferCache::Unmap() { diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 91fca3f6c..c11acfb79 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -50,7 +50,7 @@ public: /// Reserves memory to be used by host's CPU. Returns mapped address and offset. std::tuple<u8*, GLintptr> ReserveMemory(std::size_t size, std::size_t alignment = 4); - void Map(std::size_t max_size); + bool Map(std::size_t max_size); void Unmap(); GLuint GetHandle() const; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index ae6aaee4c..a44bbfae8 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -98,14 +98,9 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo has_ARB_direct_state_access = true; } else if (extension == "GL_ARB_multi_bind") { has_ARB_multi_bind = true; - } else if (extension == "GL_ARB_separate_shader_objects") { - has_ARB_separate_shader_objects = true; - } else if (extension == "GL_ARB_vertex_attrib_binding") { - has_ARB_vertex_attrib_binding = true; } } - ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); OpenGLState::ApplyDefaultState(); // Create render framebuffer @@ -118,10 +113,24 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); + CheckExtensions(); } RasterizerOpenGL::~RasterizerOpenGL() {} +void RasterizerOpenGL::CheckExtensions() { + if (!GLAD_GL_ARB_texture_filter_anisotropic && !GLAD_GL_EXT_texture_filter_anisotropic) { + LOG_WARNING( + Render_OpenGL, + "Anisotropic filter is not supported! This can cause graphical issues in some games."); + } + if (!GLAD_GL_ARB_buffer_storage) { + LOG_WARNING( + Render_OpenGL, + "Buffer storage control is not supported! This can cause performance degradation."); + } +} + void RasterizerOpenGL::SetupVertexFormat() { auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); const auto& regs = gpu.regs; @@ -181,15 +190,25 @@ void RasterizerOpenGL::SetupVertexFormat() { } state.draw.vertex_array = VAO.handle; state.ApplyVertexBufferState(); + + // Rebinding the VAO invalidates the vertex buffer bindings. + gpu.dirty_flags.vertex_array = 0xFFFFFFFF; } void RasterizerOpenGL::SetupVertexBuffer() { - MICROPROFILE_SCOPE(OpenGL_VB); - const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); + auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); const auto& regs = gpu.regs; + if (!gpu.dirty_flags.vertex_array) + return; + + MICROPROFILE_SCOPE(OpenGL_VB); + // Upload all guest vertex arrays sequentially to our buffer for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { + if (~gpu.dirty_flags.vertex_array & (1u << index)) + continue; + const auto& vertex_array = regs.vertex_array[index]; if (!vertex_array.IsEnabled()) continue; @@ -216,6 +235,8 @@ void RasterizerOpenGL::SetupVertexBuffer() { // Implicit set by glBindVertexBuffer. Stupid glstate handling... state.draw.vertex_buffer = buffer_cache.GetHandle(); + + gpu.dirty_flags.vertex_array = 0; } DrawParameters RasterizerOpenGL::SetupDraw() { @@ -542,6 +563,30 @@ void RasterizerOpenGL::Clear() { ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); use_stencil = true; clear_state.stencil.test_enabled = true; + if (regs.clear_flags.stencil) { + // Stencil affects the clear so fill it with the used masks + clear_state.stencil.front.test_func = GL_ALWAYS; + clear_state.stencil.front.test_mask = regs.stencil_front_func_mask; + clear_state.stencil.front.action_stencil_fail = GL_KEEP; + clear_state.stencil.front.action_depth_fail = GL_KEEP; + clear_state.stencil.front.action_depth_pass = GL_KEEP; + clear_state.stencil.front.write_mask = regs.stencil_front_mask; + if (regs.stencil_two_side_enable) { + clear_state.stencil.back.test_func = GL_ALWAYS; + clear_state.stencil.back.test_mask = regs.stencil_back_func_mask; + clear_state.stencil.back.action_stencil_fail = GL_KEEP; + clear_state.stencil.back.action_depth_fail = GL_KEEP; + clear_state.stencil.back.action_depth_pass = GL_KEEP; + clear_state.stencil.back.write_mask = regs.stencil_back_mask; + } else { + clear_state.stencil.back.test_func = GL_ALWAYS; + clear_state.stencil.back.test_mask = 0xFFFFFFFF; + clear_state.stencil.back.write_mask = 0xFFFFFFFF; + clear_state.stencil.back.action_stencil_fail = GL_KEEP; + clear_state.stencil.back.action_depth_fail = GL_KEEP; + clear_state.stencil.back.action_depth_pass = GL_KEEP; + } + } } if (!use_color && !use_depth && !use_stencil) { @@ -553,6 +598,14 @@ void RasterizerOpenGL::Clear() { ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false, regs.clear_buffers.RT.Value()); + if (regs.clear_flags.scissor) { + SyncScissorTest(clear_state); + } + + if (regs.clear_flags.viewport) { + clear_state.EmulateViewportWithScissor(); + } + clear_state.Apply(); if (use_color) { @@ -573,7 +626,7 @@ void RasterizerOpenGL::DrawArrays() { return; MICROPROFILE_SCOPE(OpenGL_Drawing); - const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); + auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); const auto& regs = gpu.regs; ScopeAcquireGLContext acquire_context{emu_window}; @@ -588,12 +641,13 @@ void RasterizerOpenGL::DrawArrays() { SyncLogicOpState(); SyncCullMode(); SyncPrimitiveRestart(); - SyncScissorTest(); + SyncScissorTest(state); + SyncClipEnabled(); // Alpha Testing is synced on shaders. SyncTransformFeedback(); SyncPointState(); CheckAlphaTests(); - + SyncPolygonOffset(); // TODO(bunnei): Sync framebuffer_scale uniform here // TODO(bunnei): Sync scissorbox uniform(s) here @@ -626,7 +680,11 @@ void RasterizerOpenGL::DrawArrays() { // Add space for at least 18 constant buffers buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); - buffer_cache.Map(buffer_size); + bool invalidate = buffer_cache.Map(buffer_size); + if (invalidate) { + // As all cached buffers are invalidated, we need to recheck their state. + gpu.dirty_flags.vertex_array = 0xFFFFFFFF; + } SetupVertexFormat(); SetupVertexBuffer(); @@ -815,7 +873,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr } const u32 bias = config.mip_lod_bias.Value(); // Sign extend the 13-bit value. - const u32 mask = 1U << (13 - 1); + constexpr u32 mask = 1U << (13 - 1); const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f; if (lod_bias != bias_lod) { lod_bias = bias_lod; @@ -942,20 +1000,35 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; - for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { - const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; + const bool geometry_shaders_enabled = + regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); + const std::size_t viewport_count = + geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1; + for (std::size_t i = 0; i < viewport_count; i++) { auto& viewport = current_state.viewports[i]; + const auto& src = regs.viewports[i]; + const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; viewport.x = viewport_rect.left; viewport.y = viewport_rect.bottom; - viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); - viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); + viewport.width = viewport_rect.GetWidth(); + viewport.height = viewport_rect.GetHeight(); viewport.depth_range_far = regs.viewports[i].depth_range_far; viewport.depth_range_near = regs.viewports[i].depth_range_near; } + state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0; + state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0; } void RasterizerOpenGL::SyncClipEnabled() { - UNREACHABLE(); + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + state.clip_distance[0] = regs.clip_distance_enabled.c0 != 0; + state.clip_distance[1] = regs.clip_distance_enabled.c1 != 0; + state.clip_distance[2] = regs.clip_distance_enabled.c2 != 0; + state.clip_distance[3] = regs.clip_distance_enabled.c3 != 0; + state.clip_distance[4] = regs.clip_distance_enabled.c4 != 0; + state.clip_distance[5] = regs.clip_distance_enabled.c5 != 0; + state.clip_distance[6] = regs.clip_distance_enabled.c6 != 0; + state.clip_distance[7] = regs.clip_distance_enabled.c7 != 0; } void RasterizerOpenGL::SyncClipCoef() { @@ -1120,11 +1193,15 @@ void RasterizerOpenGL::SyncLogicOpState() { state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); } -void RasterizerOpenGL::SyncScissorTest() { +void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; - for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { + const bool geometry_shaders_enabled = + regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); + const std::size_t viewport_count = + geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1; + for (std::size_t i = 0; i < viewport_count; i++) { const auto& src = regs.scissor_test[i]; - auto& dst = state.viewports[i].scissor; + auto& dst = current_state.viewports[i].scissor; dst.enabled = (src.enable != 0); if (dst.enabled == 0) { return; @@ -1152,6 +1229,16 @@ void RasterizerOpenGL::SyncPointState() { state.point.size = regs.point_size; } +void RasterizerOpenGL::SyncPolygonOffset() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0; + state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0; + state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0; + state.polygon_offset.units = regs.polygon_offset_units; + state.polygon_offset.factor = regs.polygon_offset_factor; + state.polygon_offset.clamp = regs.polygon_offset_clamp; +} + void RasterizerOpenGL::CheckAlphaTests() { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 6e78ab4cd..7ec9746b1 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -91,19 +91,20 @@ private: void SyncWithConfig(const Tegra::Texture::TSCEntry& info); private: - Tegra::Texture::TextureFilter mag_filter; - Tegra::Texture::TextureFilter min_filter; - Tegra::Texture::TextureMipmapFilter mip_filter; - Tegra::Texture::WrapMode wrap_u; - Tegra::Texture::WrapMode wrap_v; - Tegra::Texture::WrapMode wrap_p; - bool uses_depth_compare; - Tegra::Texture::DepthCompareFunc depth_compare_func; - GLvec4 border_color; - float min_lod; - float max_lod; - float lod_bias; - float max_anisotropic; + Tegra::Texture::TextureFilter mag_filter = Tegra::Texture::TextureFilter::Nearest; + Tegra::Texture::TextureFilter min_filter = Tegra::Texture::TextureFilter::Nearest; + Tegra::Texture::TextureMipmapFilter mip_filter = Tegra::Texture::TextureMipmapFilter::None; + Tegra::Texture::WrapMode wrap_u = Tegra::Texture::WrapMode::ClampToEdge; + Tegra::Texture::WrapMode wrap_v = Tegra::Texture::WrapMode::ClampToEdge; + Tegra::Texture::WrapMode wrap_p = Tegra::Texture::WrapMode::ClampToEdge; + bool uses_depth_compare = false; + Tegra::Texture::DepthCompareFunc depth_compare_func = + Tegra::Texture::DepthCompareFunc::Always; + GLvec4 border_color = {}; + float min_lod = 0.0f; + float max_lod = 16.0f; + float lod_bias = 0.0f; + float max_anisotropic = 1.0f; }; /** @@ -171,7 +172,7 @@ private: void SyncMultiSampleState(); /// Syncs the scissor test state to match the guest state - void SyncScissorTest(); + void SyncScissorTest(OpenGLState& current_state); /// Syncs the transform feedback state to match the guest state void SyncTransformFeedback(); @@ -182,13 +183,18 @@ private: /// Syncs Color Mask void SyncColorMask(); + /// Syncs the polygon offsets + void SyncPolygonOffset(); + /// Check asserts for alpha testing. void CheckAlphaTests(); + /// Check for extension that are not strictly required + /// but are needed for correct emulation + void CheckExtensions(); + bool has_ARB_direct_state_access = false; bool has_ARB_multi_bind = false; - bool has_ARB_separate_shader_objects = false; - bool has_ARB_vertex_attrib_binding = false; OpenGLState state; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 9ca82c06c..dde2f468d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -15,6 +15,7 @@ #include "core/memory.h" #include "core/settings.h" #include "video_core/engines/maxwell_3d.h" +#include "video_core/morton.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_state.h" @@ -22,10 +23,11 @@ #include "video_core/surface.h" #include "video_core/textures/astc.h" #include "video_core/textures/decoders.h" -#include "video_core/utils.h" namespace OpenGL { +using VideoCore::MortonSwizzle; +using VideoCore::MortonSwizzleMode; using VideoCore::Surface::ComponentTypeFromDepthFormat; using VideoCore::Surface::ComponentTypeFromRenderTarget; using VideoCore::Surface::ComponentTypeFromTexture; @@ -95,6 +97,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, + params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1; params.srgb_conversion = config.tic.IsSrgbConversionEnabled(); params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), params.srgb_conversion); @@ -160,6 +163,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, params.block_width = 1 << config.memory_layout.block_width; params.block_height = 1 << config.memory_layout.block_height; params.block_depth = 1 << config.memory_layout.block_depth; + params.tile_width_spacing = 1; params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB || config.format == Tegra::RenderTargetFormat::RGBA8_SRGB; @@ -195,6 +199,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, params.block_width = 1 << std::min(block_width, 5U); params.block_height = 1 << std::min(block_height, 5U); params.block_depth = 1 << std::min(block_depth, 5U); + params.tile_width_spacing = 1; params.pixel_format = PixelFormatFromDepthFormat(format); params.component_type = ComponentTypeFromDepthFormat(format); params.type = GetFormatType(params.pixel_format); @@ -221,6 +226,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, + params.tile_width_spacing = 1; params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB || config.format == Tegra::RenderTargetFormat::RGBA8_SRGB; @@ -265,11 +271,11 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN2UNORM {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM - {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + {GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // BC7U - {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, - ComponentType::Float, true}, // BC6H_UF16 - {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, + {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, + true}, // BC6H_UF16 + {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, true}, // BC6H_SF16 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U @@ -306,8 +312,8 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex true}, // DXT23_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT45_SRGB - {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, - ComponentType::UNorm, true}, // BC7U_SRGB + {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // BC7U_SRGB {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB @@ -346,7 +352,7 @@ static GLenum SurfaceTargetToGL(SurfaceTarget target) { case SurfaceTarget::TextureCubemap: return GL_TEXTURE_CUBE_MAP; case SurfaceTarget::TextureCubeArray: - return GL_TEXTURE_CUBE_MAP_ARRAY_ARB; + return GL_TEXTURE_CUBE_MAP_ARRAY; } LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); UNREACHABLE(); @@ -370,174 +376,7 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const { return {0, actual_height, MipWidth(mip_level), 0}; } -template <bool morton_to_gl, PixelFormat format> -void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer, - std::size_t gl_buffer_size, VAddr addr) { - constexpr u32 bytes_per_pixel = GetBytesPerPixel(format); - - // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual - // pixel values. - const u32 tile_size_x{GetDefaultBlockWidth(format)}; - const u32 tile_size_y{GetDefaultBlockHeight(format)}; - - if (morton_to_gl) { - Tegra::Texture::UnswizzleTexture(gl_buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel, - stride, height, depth, block_height, block_depth); - } else { - Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x, - (height + tile_size_y - 1) / tile_size_y, depth, - bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), - gl_buffer, false, block_height, block_depth); - } -} - -using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), - VideoCore::Surface::MaxPixelFormat>; - -static constexpr GLConversionArray morton_to_gl_fns = { - // clang-format off - MortonCopy<true, PixelFormat::ABGR8U>, - MortonCopy<true, PixelFormat::ABGR8S>, - MortonCopy<true, PixelFormat::ABGR8UI>, - MortonCopy<true, PixelFormat::B5G6R5U>, - MortonCopy<true, PixelFormat::A2B10G10R10U>, - MortonCopy<true, PixelFormat::A1B5G5R5U>, - MortonCopy<true, PixelFormat::R8U>, - MortonCopy<true, PixelFormat::R8UI>, - MortonCopy<true, PixelFormat::RGBA16F>, - MortonCopy<true, PixelFormat::RGBA16U>, - MortonCopy<true, PixelFormat::RGBA16UI>, - MortonCopy<true, PixelFormat::R11FG11FB10F>, - MortonCopy<true, PixelFormat::RGBA32UI>, - MortonCopy<true, PixelFormat::DXT1>, - MortonCopy<true, PixelFormat::DXT23>, - MortonCopy<true, PixelFormat::DXT45>, - MortonCopy<true, PixelFormat::DXN1>, - MortonCopy<true, PixelFormat::DXN2UNORM>, - MortonCopy<true, PixelFormat::DXN2SNORM>, - MortonCopy<true, PixelFormat::BC7U>, - MortonCopy<true, PixelFormat::BC6H_UF16>, - MortonCopy<true, PixelFormat::BC6H_SF16>, - MortonCopy<true, PixelFormat::ASTC_2D_4X4>, - MortonCopy<true, PixelFormat::G8R8U>, - MortonCopy<true, PixelFormat::G8R8S>, - MortonCopy<true, PixelFormat::BGRA8>, - MortonCopy<true, PixelFormat::RGBA32F>, - MortonCopy<true, PixelFormat::RG32F>, - MortonCopy<true, PixelFormat::R32F>, - MortonCopy<true, PixelFormat::R16F>, - MortonCopy<true, PixelFormat::R16U>, - MortonCopy<true, PixelFormat::R16S>, - MortonCopy<true, PixelFormat::R16UI>, - MortonCopy<true, PixelFormat::R16I>, - MortonCopy<true, PixelFormat::RG16>, - MortonCopy<true, PixelFormat::RG16F>, - MortonCopy<true, PixelFormat::RG16UI>, - MortonCopy<true, PixelFormat::RG16I>, - MortonCopy<true, PixelFormat::RG16S>, - MortonCopy<true, PixelFormat::RGB32F>, - MortonCopy<true, PixelFormat::RGBA8_SRGB>, - MortonCopy<true, PixelFormat::RG8U>, - MortonCopy<true, PixelFormat::RG8S>, - MortonCopy<true, PixelFormat::RG32UI>, - MortonCopy<true, PixelFormat::R32UI>, - MortonCopy<true, PixelFormat::ASTC_2D_8X8>, - MortonCopy<true, PixelFormat::ASTC_2D_8X5>, - MortonCopy<true, PixelFormat::ASTC_2D_5X4>, - MortonCopy<true, PixelFormat::BGRA8_SRGB>, - MortonCopy<true, PixelFormat::DXT1_SRGB>, - MortonCopy<true, PixelFormat::DXT23_SRGB>, - MortonCopy<true, PixelFormat::DXT45_SRGB>, - MortonCopy<true, PixelFormat::BC7U_SRGB>, - MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>, - MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, - MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, - MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, - MortonCopy<true, PixelFormat::ASTC_2D_5X5>, - MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>, - MortonCopy<true, PixelFormat::ASTC_2D_10X8>, - MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>, - MortonCopy<true, PixelFormat::Z32F>, - MortonCopy<true, PixelFormat::Z16>, - MortonCopy<true, PixelFormat::Z24S8>, - MortonCopy<true, PixelFormat::S8Z24>, - MortonCopy<true, PixelFormat::Z32FS8>, - // clang-format on -}; - -static constexpr GLConversionArray gl_to_morton_fns = { - // clang-format off - MortonCopy<false, PixelFormat::ABGR8U>, - MortonCopy<false, PixelFormat::ABGR8S>, - MortonCopy<false, PixelFormat::ABGR8UI>, - MortonCopy<false, PixelFormat::B5G6R5U>, - MortonCopy<false, PixelFormat::A2B10G10R10U>, - MortonCopy<false, PixelFormat::A1B5G5R5U>, - MortonCopy<false, PixelFormat::R8U>, - MortonCopy<false, PixelFormat::R8UI>, - MortonCopy<false, PixelFormat::RGBA16F>, - MortonCopy<false, PixelFormat::RGBA16U>, - MortonCopy<false, PixelFormat::RGBA16UI>, - MortonCopy<false, PixelFormat::R11FG11FB10F>, - MortonCopy<false, PixelFormat::RGBA32UI>, - MortonCopy<false, PixelFormat::DXT1>, - MortonCopy<false, PixelFormat::DXT23>, - MortonCopy<false, PixelFormat::DXT45>, - MortonCopy<false, PixelFormat::DXN1>, - MortonCopy<false, PixelFormat::DXN2UNORM>, - MortonCopy<false, PixelFormat::DXN2SNORM>, - MortonCopy<false, PixelFormat::BC7U>, - MortonCopy<false, PixelFormat::BC6H_UF16>, - MortonCopy<false, PixelFormat::BC6H_SF16>, - // TODO(Subv): Swizzling ASTC formats are not supported - nullptr, - MortonCopy<false, PixelFormat::G8R8U>, - MortonCopy<false, PixelFormat::G8R8S>, - MortonCopy<false, PixelFormat::BGRA8>, - MortonCopy<false, PixelFormat::RGBA32F>, - MortonCopy<false, PixelFormat::RG32F>, - MortonCopy<false, PixelFormat::R32F>, - MortonCopy<false, PixelFormat::R16F>, - MortonCopy<false, PixelFormat::R16U>, - MortonCopy<false, PixelFormat::R16S>, - MortonCopy<false, PixelFormat::R16UI>, - MortonCopy<false, PixelFormat::R16I>, - MortonCopy<false, PixelFormat::RG16>, - MortonCopy<false, PixelFormat::RG16F>, - MortonCopy<false, PixelFormat::RG16UI>, - MortonCopy<false, PixelFormat::RG16I>, - MortonCopy<false, PixelFormat::RG16S>, - MortonCopy<false, PixelFormat::RGB32F>, - MortonCopy<false, PixelFormat::RGBA8_SRGB>, - MortonCopy<false, PixelFormat::RG8U>, - MortonCopy<false, PixelFormat::RG8S>, - MortonCopy<false, PixelFormat::RG32UI>, - MortonCopy<false, PixelFormat::R32UI>, - nullptr, - nullptr, - nullptr, - MortonCopy<false, PixelFormat::BGRA8_SRGB>, - MortonCopy<false, PixelFormat::DXT1_SRGB>, - MortonCopy<false, PixelFormat::DXT23_SRGB>, - MortonCopy<false, PixelFormat::DXT45_SRGB>, - MortonCopy<false, PixelFormat::BC7U_SRGB>, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - MortonCopy<false, PixelFormat::Z32F>, - MortonCopy<false, PixelFormat::Z16>, - MortonCopy<false, PixelFormat::Z24S8>, - MortonCopy<false, PixelFormat::S8Z24>, - MortonCopy<false, PixelFormat::Z32FS8>, - // clang-format on -}; - -void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, +void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params, std::vector<u8>& gl_buffer, u32 mip_level) { u32 depth = params.MipDepth(mip_level); if (params.target == SurfaceTarget::Texture2D) { @@ -550,19 +389,19 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params const u64 layer_size = params.LayerMemorySize(); const u64 gl_size = params.LayerSizeGL(mip_level); for (u32 i = 0; i < params.depth; i++) { - functions[static_cast<std::size_t>(params.pixel_format)]( - params.MipWidth(mip_level), params.MipBlockHeight(mip_level), - params.MipHeight(mip_level), params.MipBlockDepth(mip_level), 1, - gl_buffer.data() + offset_gl, gl_size, params.addr + offset); + MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), + params.MipBlockHeight(mip_level), params.MipHeight(mip_level), + params.MipBlockDepth(mip_level), params.tile_width_spacing, 1, + gl_buffer.data() + offset_gl, gl_size, params.addr + offset); offset += layer_size; offset_gl += gl_size; } } else { const u64 offset = params.GetMipmapLevelOffset(mip_level); - functions[static_cast<std::size_t>(params.pixel_format)]( - params.MipWidth(mip_level), params.MipBlockHeight(mip_level), - params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(), - gl_buffer.size(), params.addr + offset); + MortonSwizzle(mode, params.pixel_format, params.MipWidth(mip_level), + params.MipBlockHeight(mip_level), params.MipHeight(mip_level), + params.MipBlockDepth(mip_level), depth, params.tile_width_spacing, + gl_buffer.data(), gl_buffer.size(), params.addr + offset); } } @@ -726,7 +565,7 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface, const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes); glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); - glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); + glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW); if (source_format.compressed) { glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, static_cast<GLsizei>(src_params.size_in_bytes), nullptr); @@ -996,7 +835,7 @@ void CachedSurface::LoadGLBuffer() { ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast<u32>(params.target)); for (u32 i = 0; i < params.max_mip_level; i++) - SwizzleFunc(morton_to_gl_fns, params, gl_buffer[i], i); + SwizzleFunc(MortonSwizzleMode::MortonToLinear, params, gl_buffer[i], i); } else { const auto texture_src_data{Memory::GetPointer(params.addr)}; const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; @@ -1035,7 +874,7 @@ void CachedSurface::FlushGLBuffer() { ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast<u32>(params.target)); - SwizzleFunc(gl_to_morton_fns, params, gl_buffer[0], 0); + SwizzleFunc(MortonSwizzleMode::LinearToMorton, params, gl_buffer[0], 0); } else { std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer[0].data(), GetSizeInBytes()); } @@ -1275,6 +1114,31 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) { return surface; } +void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface, + const Surface& dst_surface) { + const auto& init_params{src_surface->GetSurfaceParams()}; + const auto& dst_params{dst_surface->GetSurfaceParams()}; + VAddr address = init_params.addr; + const std::size_t layer_size = dst_params.LayerMemorySize(); + for (u32 layer = 0; layer < dst_params.depth; layer++) { + for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { + const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap); + const Surface& copy = TryGet(sub_address); + if (!copy) + continue; + const auto& src_params{copy->GetSurfaceParams()}; + const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))}; + const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))}; + + glCopyImageSubData(copy->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0, + 0, 0, dst_surface->Texture().handle, + SurfaceTargetToGL(dst_params.target), mipmap, 0, 0, layer, width, + height, 1); + } + address += layer_size; + } +} + void RasterizerCacheOpenGL::FermiCopySurface( const Tegra::Engines::Fermi2D::Regs::Surface& src_config, const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) { @@ -1340,11 +1204,13 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, CopySurface(old_surface, new_surface, copy_pbo.handle); } break; - case SurfaceTarget::TextureCubemap: case SurfaceTarget::Texture3D: + AccurateCopySurface(old_surface, new_surface); + break; + case SurfaceTarget::TextureCubemap: case SurfaceTarget::Texture2DArray: case SurfaceTarget::TextureCubeArray: - AccurateCopySurface(old_surface, new_surface); + FastLayeredCopySurface(old_surface, new_surface); break; default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 494f6b903..c710aa245 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -196,9 +196,15 @@ struct SurfaceParams { /// Checks if surfaces are compatible for caching bool IsCompatibleSurface(const SurfaceParams& other) const { - return std::tie(pixel_format, type, width, height, target, depth) == - std::tie(other.pixel_format, other.type, other.width, other.height, other.target, - other.depth); + if (std::tie(pixel_format, type, width, height, target, depth, is_tiled) == + std::tie(other.pixel_format, other.type, other.width, other.height, other.target, + other.depth, other.is_tiled)) { + if (!is_tiled) + return true; + return std::tie(block_height, block_depth, tile_width_spacing) == + std::tie(other.block_height, other.block_depth, other.tile_width_spacing); + } + return false; } /// Initializes parameters for caching, should be called after everything has been initialized @@ -208,6 +214,7 @@ struct SurfaceParams { u32 block_width; u32 block_height; u32 block_depth; + u32 tile_width_spacing; PixelFormat pixel_format; ComponentType component_type; SurfaceType type; @@ -350,6 +357,7 @@ private: /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface); + void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface); /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have /// previously been used. This is to prevent surfaces from being constantly created and diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index a85a7c0c5..038b25c75 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -84,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) } entries = program_result.second; + shader_length = entries.shader_length; if (program_type != Maxwell::ShaderProgram::Geometry) { OGLShader shader; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index ffbf21831..08f470de3 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -30,7 +30,7 @@ public: } std::size_t GetSizeInBytes() const override { - return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); + return shader_length; } // We do not have to flush this cache as things in it are never modified by us. @@ -82,6 +82,7 @@ private: u32 max_vertices, const std::string& debug_name); VAddr addr; + std::size_t shader_length; Maxwell::ShaderProgram program_type; GLShader::ShaderSetup setup; GLShader::ShaderEntries entries; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 90a88b91a..0c4524d5c 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -34,6 +34,17 @@ constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); constexpr u32 MAX_GEOMETRY_BUFFERS = 6; constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested +static const char* INTERNAL_FLAG_NAMES[] = {"zero_flag", "sign_flag", "carry_flag", + "overflow_flag"}; + +enum class InternalFlag : u64 { + ZeroFlag = 0, + SignFlag = 1, + CarryFlag = 2, + OverflowFlag = 3, + Amount +}; + class DecompileFail : public std::runtime_error { public: using std::runtime_error::runtime_error; @@ -84,7 +95,8 @@ struct Subroutine { class ControlFlowAnalyzer { public: ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix) - : program_code(program_code) { + : program_code(program_code), shader_coverage_begin(main_offset), + shader_coverage_end(main_offset + 1) { // Recursively finds all subroutines. const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix); @@ -96,10 +108,16 @@ public: return std::move(subroutines); } + std::size_t GetShaderLength() const { + return shader_coverage_end * sizeof(u64); + } + private: const ProgramCode& program_code; std::set<Subroutine> subroutines; std::map<std::pair<u32, u32>, ExitMethod> exit_method_map; + u32 shader_coverage_begin; + u32 shader_coverage_end; /// Adds and analyzes a new subroutine if it is not added yet. const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) { @@ -141,6 +159,9 @@ private: return exit_method; for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { + shader_coverage_begin = std::min(shader_coverage_begin, offset); + shader_coverage_end = std::max(shader_coverage_end, offset + 1); + const Instruction instr = {program_code[offset]}; if (const auto opcode = OpCode::Decode(instr)) { switch (opcode->get().GetId()) { @@ -257,14 +278,6 @@ private: const std::string& suffix; }; -enum class InternalFlag : u64 { - ZeroFlag = 0, - CarryFlag = 1, - OverflowFlag = 2, - NaNFlag = 3, - Amount -}; - /** * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state * of all registers (e.g. whether they are currently being used as Floats or Integers), and @@ -371,7 +384,7 @@ public: if (sets_cc) { const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); - LOG_WARNING(HW_GPU, "Control Codes Imcomplete."); + LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete."); } } @@ -454,23 +467,25 @@ public: shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");"); } - std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { + std::string GetConditionCode(const Tegra::Shader::ConditionCode cc) const { switch (cc) { - case Tegra::Shader::ControlCode::NEU: + case Tegra::Shader::ConditionCode::NEU: return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; default: - UNIMPLEMENTED_MSG("Unimplemented Control Code: {}", static_cast<u32>(cc)); + UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); return "false"; } } - std::string GetInternalFlag(const InternalFlag ii) const { - const u32 code = static_cast<u32>(ii); - return "internalFlag_" + std::to_string(code) + suffix; + std::string GetInternalFlag(const InternalFlag flag) const { + const auto index = static_cast<u32>(flag); + ASSERT(index < static_cast<u32>(InternalFlag::Amount)); + + return std::string(INTERNAL_FLAG_NAMES[index]) + '_' + suffix; } - void SetInternalFlag(const InternalFlag ii, const std::string& value) const { - shader.AddLine(GetInternalFlag(ii) + " = " + value + ';'); + void SetInternalFlag(const InternalFlag flag, const std::string& value) const { + shader.AddLine(GetInternalFlag(flag) + " = " + value + ';'); } /** @@ -485,27 +500,42 @@ public: const Register& buf_reg) { const std::string dest = GetOutputAttribute(attribute); const std::string src = GetRegisterAsFloat(val_reg); + if (dest.empty()) + return; - if (!dest.empty()) { - // Can happen with unknown/unimplemented output attributes, in which case we ignore the - // instruction for now. - if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { - // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry - // shader. These instructions use a dirty register as buffer index, to avoid some - // drivers from complaining about out of boundary writes, guard them. - const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " + - std::to_string(MAX_GEOMETRY_BUFFERS) + ')'}; - shader.AddLine("amem[" + buf_index + "][" + - std::to_string(static_cast<u32>(attribute)) + ']' + - GetSwizzle(elem) + " = " + src + ';'); - } else { - if (attribute == Attribute::Index::PointSize) { - fixed_pipeline_output_attributes_used.insert(attribute); - shader.AddLine(dest + " = " + src + ';'); - } else { - shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); - } - } + // Can happen with unknown/unimplemented output attributes, in which case we ignore the + // instruction for now. + if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { + // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry + // shader. These instructions use a dirty register as buffer index, to avoid some + // drivers from complaining about out of boundary writes, guard them. + const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " + + std::to_string(MAX_GEOMETRY_BUFFERS) + ')'}; + shader.AddLine("amem[" + buf_index + "][" + + std::to_string(static_cast<u32>(attribute)) + ']' + GetSwizzle(elem) + + " = " + src + ';'); + return; + } + + switch (attribute) { + case Attribute::Index::ClipDistances0123: + case Attribute::Index::ClipDistances4567: { + const u64 index = (attribute == Attribute::Index::ClipDistances4567 ? 4 : 0) + elem; + UNIMPLEMENTED_IF_MSG( + ((header.vtg.clip_distances >> index) & 1) == 0, + "Shader is setting gl_ClipDistance{} without enabling it in the header", index); + + fixed_pipeline_output_attributes_used.insert(attribute); + shader.AddLine(dest + '[' + std::to_string(index) + "] = " + src + ';'); + break; + } + case Attribute::Index::PointSize: + fixed_pipeline_output_attributes_used.insert(attribute); + shader.AddLine(dest + " = " + src + ';'); + break; + default: + shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); + break; } } @@ -621,8 +651,8 @@ private: /// Generates declarations for internal flags. void GenerateInternalFlags() { - for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { - const InternalFlag code = static_cast<InternalFlag>(ii); + for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { + const InternalFlag code = static_cast<InternalFlag>(flag); declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); } declarations.AddNewLine(); @@ -725,12 +755,19 @@ private: void GenerateVertex() { if (stage != Maxwell3D::Regs::ShaderStage::Vertex) return; + bool clip_distances_declared = false; + declarations.AddLine("out gl_PerVertex {"); ++declarations.scope; declarations.AddLine("vec4 gl_Position;"); for (auto& o : fixed_pipeline_output_attributes_used) { if (o == Attribute::Index::PointSize) declarations.AddLine("float gl_PointSize;"); + if (!clip_distances_declared && (o == Attribute::Index::ClipDistances0123 || + o == Attribute::Index::ClipDistances4567)) { + declarations.AddLine("float gl_ClipDistance[];"); + clip_distances_declared = true; + } } --declarations.scope; declarations.AddLine("};"); @@ -830,7 +867,8 @@ private: // vertex shader, and what's the value of the fourth element when inside a Tess Eval // shader. ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex); - return "vec4(0, 0, uintBitsToFloat(instance_id.x), uintBitsToFloat(gl_VertexID))"; + // Config pack's first value is instance_id. + return "vec4(0, 0, uintBitsToFloat(config_pack[0]), uintBitsToFloat(gl_VertexID))"; case Attribute::Index::FrontFacing: // TODO(Subv): Find out what the values are for the other elements. ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); @@ -901,6 +939,10 @@ private: return "gl_PointSize"; case Attribute::Index::Position: return "position"; + case Attribute::Index::ClipDistances0123: + case Attribute::Index::ClipDistances4567: { + return "gl_ClipDistance"; + } default: const u32 index{static_cast<u32>(attribute) - static_cast<u32>(Attribute::Index::Attribute_0)}; @@ -939,9 +981,10 @@ private: class GLSLGenerator { public: GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, - u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) + u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix, + std::size_t shader_length) : subroutines(subroutines), program_code(program_code), main_offset(main_offset), - stage(stage), suffix(suffix) { + stage(stage), suffix(suffix), shader_length(shader_length) { std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); local_memory_size = header.GetLocalMemorySize(); regs.SetLocalMemory(local_memory_size); @@ -954,7 +997,7 @@ public: /// Returns entries in the shader that are useful for external functions ShaderEntries GetEntries() const { - return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()}; + return {regs.GetConstBuffersDeclarations(), regs.GetSamplers(), shader_length}; } private: @@ -1059,11 +1102,17 @@ private: const std::string& op_a, const std::string& op_b) const { using Tegra::Shader::PredCondition; static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { - {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, - {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, - {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, - {PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="}, - {PredCondition::GreaterThanWithNan, ">"}, {PredCondition::GreaterEqualWithNan, ">="}}; + {PredCondition::LessThan, "<"}, + {PredCondition::Equal, "=="}, + {PredCondition::LessEqual, "<="}, + {PredCondition::GreaterThan, ">"}, + {PredCondition::NotEqual, "!="}, + {PredCondition::GreaterEqual, ">="}, + {PredCondition::LessThanWithNan, "<"}, + {PredCondition::NotEqualWithNan, "!="}, + {PredCondition::LessEqualWithNan, "<="}, + {PredCondition::GreaterThanWithNan, ">"}, + {PredCondition::GreaterEqualWithNan, ">="}}; const auto& comparison{PredicateComparisonStrings.find(condition)}; UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonStrings.end(), @@ -1072,6 +1121,7 @@ private: std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; if (condition == PredCondition::LessThanWithNan || condition == PredCondition::NotEqualWithNan || + condition == PredCondition::LessEqualWithNan || condition == PredCondition::GreaterThanWithNan || condition == PredCondition::GreaterEqualWithNan) { predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; @@ -1250,6 +1300,7 @@ private: shader.AddLine('{'); ++shader.scope; shader.AddLine(coord); + shader.AddLine("vec4 texture_tmp = " + texture + ';'); // TEXS has two destination registers and a swizzle. The first two elements in the swizzle // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 @@ -1262,18 +1313,17 @@ private: if (written_components < 2) { // Write the first two swizzle components to gpr0 and gpr0+1 - regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false, + regs.SetRegisterToFloat(instr.gpr0, component, "texture_tmp", 1, 4, false, written_components % 2); } else { ASSERT(instr.texs.HasTwoDestinations()); // Write the rest of the swizzle components to gpr28 and gpr28+1 - regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false, + regs.SetRegisterToFloat(instr.gpr28, component, "texture_tmp", 1, 4, false, written_components % 2); } ++written_components; } - --shader.scope; shader.AddLine('}'); } @@ -1508,9 +1558,8 @@ private: instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented", instr.fmul.tab5c68_0 .Value()); // SMO typical sends 1 here which seems to be the default - UNIMPLEMENTED_IF_MSG(instr.fmul.cc != 0, "FMUL cc is not implemented"); UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "FMUL Generates an unhandled Control Code"); + "Condition codes generation in FMUL is not implemented"); op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); @@ -1522,7 +1571,7 @@ private: case OpCode::Id::FADD_R: case OpCode::Id::FADD_IMM: { UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "FADD Generates an unhandled Control Code"); + "Condition codes generation in FADD is not implemented"); op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); @@ -1572,7 +1621,7 @@ private: case OpCode::Id::FMNMX_R: case OpCode::Id::FMNMX_IMM: { UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "FMNMX Generates an unhandled Control Code"); + "Condition codes generation in FMNMX is not implemented"); op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); @@ -1609,7 +1658,7 @@ private: } case OpCode::Id::FMUL32_IMM: { UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, - "FMUL32 Generates an unhandled Control Code"); + "Condition codes generation in FMUL32 is not implemented"); regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8) + " * " + @@ -1619,7 +1668,7 @@ private: } case OpCode::Id::FADD32I: { UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, - "FADD32 Generates an unhandled Control Code"); + "Condition codes generation in FADD32I is not implemented"); std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); std::string op_b = GetImmediate32(instr); @@ -1654,7 +1703,8 @@ private: switch (opcode->get().GetId()) { case OpCode::Id::BFE_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "BFE Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in BFE is not implemented"); std::string inner_shift = '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; @@ -1672,6 +1722,26 @@ private: break; } + case OpCode::Type::Bfi: { + UNIMPLEMENTED_IF(instr.generates_cc); + + const auto [base, packed_shift] = [&]() -> std::tuple<std::string, std::string> { + switch (opcode->get().GetId()) { + case OpCode::Id::BFI_IMM_R: + return {regs.GetRegisterAsInteger(instr.gpr39, 0, false), + std::to_string(instr.alu.GetSignedImm20_20())}; + default: + UNREACHABLE(); + } + }(); + const std::string offset = '(' + packed_shift + " & 0xff)"; + const std::string bits = "((" + packed_shift + " >> 8) & 0xff)"; + const std::string insert = regs.GetRegisterAsInteger(instr.gpr8, 0, false); + regs.SetRegisterToInteger( + instr.gpr0, false, 0, + "bitfieldInsert(" + base + ", " + insert + ", " + offset + ", " + bits + ')', 1, 1); + break; + } case OpCode::Type::Shift: { std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); std::string op_b; @@ -1691,7 +1761,8 @@ private: case OpCode::Id::SHR_C: case OpCode::Id::SHR_R: case OpCode::Id::SHR_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "SHR Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in SHR is not implemented"); if (!instr.shift.is_signed) { // Logical shift right @@ -1706,8 +1777,8 @@ private: case OpCode::Id::SHL_C: case OpCode::Id::SHL_R: case OpCode::Id::SHL_IMM: - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "SHL Generates an unhandled Control Code"); - + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in SHL is not implemented"); regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); break; default: { @@ -1723,7 +1794,7 @@ private: switch (opcode->get().GetId()) { case OpCode::Id::IADD32I: UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, - "IADD32 Generates an unhandled Control Code"); + "Condition codes generation in IADD32I is not implemented"); if (instr.iadd32i.negate_a) op_a = "-(" + op_a + ')'; @@ -1733,7 +1804,7 @@ private: break; case OpCode::Id::LOP32I: { UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc, - "LOP32I Generates an unhandled Control Code"); + "Condition codes generation in LOP32I is not implemented"); if (instr.alu.lop32i.invert_a) op_a = "~(" + op_a + ')'; @@ -1772,7 +1843,7 @@ private: case OpCode::Id::IADD_R: case OpCode::Id::IADD_IMM: { UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "IADD Generates an unhandled Control Code"); + "Condition codes generation in IADD is not implemented"); if (instr.alu_integer.negate_a) op_a = "-(" + op_a + ')'; @@ -1788,7 +1859,7 @@ private: case OpCode::Id::IADD3_R: case OpCode::Id::IADD3_IMM: { UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "IADD3 Generates an unhandled Control Code"); + "Condition codes generation in IADD3 is not implemented"); std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); @@ -1851,7 +1922,7 @@ private: case OpCode::Id::ISCADD_R: case OpCode::Id::ISCADD_IMM: { UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "ISCADD Generates an unhandled Control Code"); + "Condition codes generation in ISCADD is not implemented"); if (instr.alu_integer.negate_a) op_a = "-(" + op_a + ')'; @@ -1886,7 +1957,8 @@ private: case OpCode::Id::LOP_C: case OpCode::Id::LOP_R: case OpCode::Id::LOP_IMM: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "LOP Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in LOP is not implemented"); if (instr.alu.lop.invert_a) op_a = "~(" + op_a + ')'; @@ -1902,7 +1974,7 @@ private: case OpCode::Id::LOP3_R: case OpCode::Id::LOP3_IMM: { UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "LOP3 Generates an unhandled Control Code"); + "Condition codes generation in LOP3 is not implemented"); const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); std::string lut; @@ -1921,7 +1993,7 @@ private: case OpCode::Id::IMNMX_IMM: { UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None); UNIMPLEMENTED_IF_MSG(instr.generates_cc, - "IMNMX Generates an unhandled Control Code"); + "Condition codes generation in IMNMX is not implemented"); const std::string condition = GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); @@ -2094,7 +2166,8 @@ private: instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented", instr.ffma.tab5980_1.Value()); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "FFMA Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in FFMA is not implemented"); switch (opcode->get().GetId()) { case OpCode::Id::FFMA_CR: { @@ -2204,7 +2277,8 @@ private: case OpCode::Id::I2F_C: { UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); UNIMPLEMENTED_IF(instr.conversion.selector); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "I2F Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in I2F is not implemented"); std::string op_a{}; @@ -2234,7 +2308,8 @@ private: case OpCode::Id::F2F_R: { UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "F2F Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in F2F is not implemented"); std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); if (instr.conversion.abs_a) { @@ -2272,7 +2347,8 @@ private: case OpCode::Id::F2I_R: case OpCode::Id::F2I_C: { UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "F2I Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in F2I is not implemented"); std::string op_a{}; if (instr.is_b_gpr) { @@ -2491,61 +2567,83 @@ private: const bool depth_compare = instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); u32 num_coordinates = TextureCoordinates(texture_type); - if (depth_compare) - num_coordinates += 1; + u32 start_index = 0; + std::string array_elem; + if (is_array) { + array_elem = regs.GetRegisterAsInteger(instr.gpr8); + start_index = 1; + } + const auto process_mode = instr.tex.GetTextureProcessMode(); + u32 start_index_b = 0; + std::string lod_value; + if (process_mode != Tegra::Shader::TextureProcessMode::LZ && + process_mode != Tegra::Shader::TextureProcessMode::None) { + start_index_b = 1; + lod_value = regs.GetRegisterAsFloat(instr.gpr20); + } + + std::string depth_value; + if (depth_compare) { + depth_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + start_index_b); + } + + bool depth_compare_extra = false; switch (num_coordinates) { case 1: { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + index + ");"; + if (depth_compare) { + coord = "vec3 coords = vec3(" + x + ", " + depth_value + ", " + + array_elem + ");"; + } else { + coord = "vec2 coords = vec2(" + x + ", " + array_elem + ");"; + } } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - coord = "float coords = " + x + ';'; + if (depth_compare) { + coord = "vec2 coords = vec2(" + x + ", " + depth_value + ");"; + } else { + coord = "float coords = " + x + ';'; + } } break; } case 2: { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); + const std::string y = + regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1); if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; + if (depth_compare) { + coord = "vec4 coords = vec4(" + x + ", " + y + ", " + depth_value + + ", " + array_elem + ");"; + } else { + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + array_elem + ");"; + } } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + if (depth_compare) { + coord = + "vec3 coords = vec3(" + x + ", " + y + ", " + depth_value + ");"; + } else { + coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + } } break; } case 3: { - if (depth_compare) { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + - ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - } + const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); + const std::string y = + regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1); + const std::string z = + regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 2); + if (is_array) { + depth_compare_extra = depth_compare; + coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + + array_elem + ");"; } else { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3); - coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + - ");"; + if (depth_compare) { + coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + + depth_value + ");"; } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; } } @@ -2561,79 +2659,88 @@ private: coord = "vec2 coords = vec2(" + x + ", " + y + ");"; texture_type = Tegra::Shader::TextureType::Texture2D; } - // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias - // or lod. - std::string op_c; const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare); // Add an extra scope and declare the texture coords inside to prevent // overwriting them in case they are used as outputs of the texs instruction. - shader.AddLine("{"); + shader.AddLine('{'); ++shader.scope; shader.AddLine(coord); std::string texture; switch (instr.tex.GetTextureProcessMode()) { case Tegra::Shader::TextureProcessMode::None: { - texture = "texture(" + sampler + ", coords)"; + if (!depth_compare_extra) { + texture = "texture(" + sampler + ", coords)"; + } else { + texture = "texture(" + sampler + ", coords, " + depth_value + ')'; + } break; } case Tegra::Shader::TextureProcessMode::LZ: { - texture = "textureLod(" + sampler + ", coords, 0.0)"; + if (!depth_compare_extra) { + texture = "textureLod(" + sampler + ", coords, 0.0)"; + } else { + texture = "texture(" + sampler + ", coords, " + depth_value + ')'; + } break; } case Tegra::Shader::TextureProcessMode::LB: case Tegra::Shader::TextureProcessMode::LBA: { - if (depth_compare) { - if (is_array) - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); - else - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + // TODO: Figure if A suffix changes the equation at all. + if (!depth_compare_extra) { + texture = "texture(" + sampler + ", coords, " + lod_value + ')'; } else { - op_c = regs.GetRegisterAsFloat(instr.gpr20); + texture = "texture(" + sampler + ", coords, " + depth_value + ')'; + LOG_WARNING(HW_GPU, + "OpenGL Limitation: can't set bias value along depth compare"); } - // TODO: Figure if A suffix changes the equation at all. - texture = "texture(" + sampler + ", coords, " + op_c + ')'; break; } case Tegra::Shader::TextureProcessMode::LL: case Tegra::Shader::TextureProcessMode::LLA: { - if (num_coordinates <= 2) { - op_c = regs.GetRegisterAsFloat(instr.gpr20); + // TODO: Figure if A suffix changes the equation at all. + if (!depth_compare_extra) { + texture = "textureLod(" + sampler + ", coords, " + lod_value + ')'; } else { - op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + texture = "texture(" + sampler + ", coords, " + depth_value + ')'; + LOG_WARNING(HW_GPU, + "OpenGL Limitation: can't set lod value along depth compare"); } - // TODO: Figure if A suffix changes the equation at all. - texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; break; } default: { - texture = "texture(" + sampler + ", coords)"; + if (!depth_compare_extra) { + texture = "texture(" + sampler + ", coords)"; + } else { + texture = "texture(" + sampler + ", coords, " + depth_value + ')'; + } UNIMPLEMENTED_MSG("Unhandled texture process mode {}", static_cast<u32>(instr.tex.GetTextureProcessMode())); } } if (!depth_compare) { + shader.AddLine("vec4 texture_tmp = " + texture + ';'); std::size_t dest_elem{}; for (std::size_t elem = 0; elem < 4; ++elem) { if (!instr.tex.IsComponentEnabled(elem)) { // Skip disabled components continue; } - regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); + regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, + dest_elem); ++dest_elem; } } else { regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); } --shader.scope; - shader.AddLine("}"); + shader.AddLine('}'); break; } case OpCode::Id::TEXS: { - std::string coord; Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; bool is_array{instr.texs.IsArrayTexture()}; @@ -2643,37 +2750,76 @@ private: const bool depth_compare = instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); u32 num_coordinates = TextureCoordinates(texture_type); - if (depth_compare) - num_coordinates += 1; + const auto process_mode = instr.texs.GetTextureProcessMode(); + std::string lod_value; + std::string coord; + u32 lod_offset = 0; + if (process_mode == Tegra::Shader::TextureProcessMode::LL) { + if (num_coordinates > 2) { + lod_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + lod_offset = 2; + } else { + lod_value = regs.GetRegisterAsFloat(instr.gpr20); + lod_offset = 1; + } + } switch (num_coordinates) { + case 1: { + coord = "float coords = " + regs.GetRegisterAsFloat(instr.gpr8) + ';'; + break; + } case 2: { if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; + if (depth_compare) { + const std::string index = regs.GetRegisterAsInteger(instr.gpr8); + const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string y = regs.GetRegisterAsFloat(instr.gpr20); + const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); + coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + + ");"; + } else { + const std::string index = regs.GetRegisterAsInteger(instr.gpr8); + const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string y = regs.GetRegisterAsFloat(instr.gpr20); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; + } } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + if (lod_offset != 0) { + if (depth_compare) { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = + regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string z = + regs.GetRegisterAsFloat(instr.gpr20.Value() + lod_offset); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + } else { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = + regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + } + } else { + if (depth_compare) { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = + regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string z = regs.GetRegisterAsFloat(instr.gpr20); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + } else { + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = regs.GetRegisterAsFloat(instr.gpr20); + coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + } + } } break; } case 3: { - if (is_array) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = - "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - } + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); + const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string z = regs.GetRegisterAsFloat(instr.gpr20); + coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; break; } default: @@ -2690,7 +2836,7 @@ private: const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, depth_compare); std::string texture; - switch (instr.texs.GetTextureProcessMode()) { + switch (process_mode) { case Tegra::Shader::TextureProcessMode::None: { texture = "texture(" + sampler + ", coords)"; break; @@ -2704,8 +2850,7 @@ private: break; } case Tegra::Shader::TextureProcessMode::LL: { - const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; + texture = "textureLod(" + sampler + ", coords, " + lod_value + ')'; break; } default: { @@ -2719,10 +2864,10 @@ private: } else { WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); } + break; } case OpCode::Id::TLDS: { - std::string coord; const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; const bool is_array{instr.tlds.IsArrayTexture()}; @@ -2736,12 +2881,17 @@ private: UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), "MZ is not implemented"); - u32 op_c_offset = 0; + u32 extra_op_offset = 0; + + // Scope to avoid variable name overlaps. + shader.AddLine('{'); + ++shader.scope; + std::string coords; switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: { const std::string x = regs.GetRegisterAsInteger(instr.gpr8); - coord = "int coords = " + x + ';'; + coords = "float coords = " + x + ';'; break; } case Tegra::Shader::TextureType::Texture2D: { @@ -2749,8 +2899,9 @@ private: const std::string x = regs.GetRegisterAsInteger(instr.gpr8); const std::string y = regs.GetRegisterAsInteger(instr.gpr20); - coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; - op_c_offset = 1; + // shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");"); + coords = "ivec2 coords = ivec2(" + x + ", " + y + ");"; + extra_op_offset = 1; break; } default: @@ -2765,9 +2916,10 @@ private: break; } case Tegra::Shader::TextureProcessMode::LL: { - const std::string op_c = - regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset); - texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; + shader.AddLine( + "float lod = " + + regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';'); + texture = "texelFetch(" + sampler + ", coords, lod)"; break; } default: { @@ -2776,7 +2928,10 @@ private: static_cast<u32>(instr.tlds.GetTextureProcessMode())); } } - WriteTexsInstruction(instr, coord, texture); + WriteTexsInstruction(instr, coords, texture); + + --shader.scope; + shader.AddLine('}'); break; } case OpCode::Id::TLD4: { @@ -2799,18 +2954,23 @@ private: if (depth_compare) num_coordinates += 1; + // Add an extra scope and declare the texture coords inside to prevent + // overwriting them in case they are used as outputs of the texs instruction. + shader.AddLine('{'); + ++shader.scope; + switch (num_coordinates) { case 2: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); break; } case 3: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; + shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); break; } default: @@ -2818,34 +2978,33 @@ private: static_cast<u32>(num_coordinates)); const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); texture_type = Tegra::Shader::TextureType::Texture2D; } const std::string sampler = GetSampler(instr.sampler, texture_type, false, depth_compare); - // Add an extra scope and declare the texture coords inside to prevent - // overwriting them in case they are used as outputs of the texs instruction. - shader.AddLine("{"); - ++shader.scope; - shader.AddLine(coord); + const std::string texture = "textureGather(" + sampler + ", coords, " + std::to_string(instr.tld4.component) + ')'; + if (!depth_compare) { + shader.AddLine("vec4 texture_tmp = " + texture + ';'); std::size_t dest_elem{}; for (std::size_t elem = 0; elem < 4; ++elem) { if (!instr.tex.IsComponentEnabled(elem)) { // Skip disabled components continue; } - regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); + regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, + dest_elem); ++dest_elem; } } else { regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); } --shader.scope; - shader.AddLine("}"); + shader.AddLine('}'); break; } case OpCode::Id::TLD4S: { @@ -2856,6 +3015,11 @@ private: instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), "AOFFI is not implemented"); + // Scope to avoid variable name overlaps. + shader.AddLine('{'); + ++shader.scope; + std::string coords; + const bool depth_compare = instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); @@ -2863,28 +3027,32 @@ private: // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. const std::string sampler = GetSampler( instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); - std::string coord; if (!depth_compare) { - coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; + coords = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; } else { // Note: TLD4S coordinate encoding works just like TEXS's - const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");"; + const std::string op_y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + coords = "vec3 coords = vec3(" + op_a + ", " + op_y + ", " + op_b + ");"; } const std::string texture = "textureGather(" + sampler + ", coords, " + std::to_string(instr.tld4s.component) + ')'; if (!depth_compare) { - WriteTexsInstruction(instr, coord, texture); + WriteTexsInstruction(instr, coords, texture); } else { - WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); + WriteTexsInstruction(instr, coords, "vec4(" + texture + ')'); } + + --shader.scope; + shader.AddLine('}'); break; } case OpCode::Id::TXQ: { UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); + ++shader.scope; + shader.AddLine('{'); // TODO: the new commits on the texture refactor, change the way samplers work. // Sadly, not all texture instructions specify the type of texture their sampler // uses. This must be fixed at a later instance. @@ -2892,8 +3060,14 @@ private: GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); switch (instr.txq.query_type) { case Tegra::Shader::TextureQueryType::Dimension: { - const std::string texture = "textureQueryLevels(" + sampler + ')'; - regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1); + const std::string texture = "textureSize(" + sampler + ", " + + regs.GetRegisterAsInteger(instr.gpr8) + ')'; + const std::string mip_level = "textureQueryLevels(" + sampler + ')'; + shader.AddLine("ivec2 sizes = " + texture + ';'); + regs.SetRegisterToInteger(instr.gpr0, true, 0, "sizes.x", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1); + regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1); break; } default: { @@ -2901,6 +3075,8 @@ private: static_cast<u32>(instr.txq.query_type.Value())); } } + --shader.scope; + shader.AddLine('}'); break; } case OpCode::Id::TMML: { @@ -3083,7 +3259,8 @@ private: break; } case OpCode::Type::PredicateSetRegister: { - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "PSET Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in PSET is not implemented"); const std::string op_a = GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); @@ -3142,14 +3319,14 @@ private: const std::string pred = GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); const std::string combiner = GetPredicateCombiner(instr.csetp.op); - const std::string control_code = regs.GetControlCode(instr.csetp.cc); + const std::string condition_code = regs.GetConditionCode(instr.csetp.cc); if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { SetPredicate(instr.csetp.pred3, - '(' + control_code + ") " + combiner + " (" + pred + ')'); + '(' + condition_code + ") " + combiner + " (" + pred + ')'); } if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { SetPredicate(instr.csetp.pred0, - "!(" + control_code + ") " + combiner + " (" + pred + ')'); + "!(" + condition_code + ") " + combiner + " (" + pred + ')'); } break; } @@ -3159,6 +3336,34 @@ private: } break; } + case OpCode::Type::RegisterSetPredicate: { + UNIMPLEMENTED_IF(instr.r2p.mode != Tegra::Shader::R2pMode::Pr); + + const std::string apply_mask = [&]() { + switch (opcode->get().GetId()) { + case OpCode::Id::R2P_IMM: + return std::to_string(instr.r2p.immediate_mask); + default: + UNREACHABLE(); + } + }(); + const std::string mask = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + + " >> " + std::to_string(instr.r2p.byte) + ')'; + + constexpr u64 programmable_preds = 7; + for (u64 pred = 0; pred < programmable_preds; ++pred) { + const auto shift = std::to_string(1 << pred); + + shader.AddLine("if ((" + apply_mask + " & " + shift + ") != 0) {"); + ++shader.scope; + + SetPredicate(pred, '(' + mask + " & " + shift + ") != 0"); + + --shader.scope; + shader.AddLine('}'); + } + break; + } case OpCode::Type::FloatSet: { const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fset.abs_a != 0, instr.fset.neg_a != 0); @@ -3196,6 +3401,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1, 1); } + if (instr.generates_cc.Value() != 0) { + regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate); + LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete"); + } break; } case OpCode::Type::IntegerSet: { @@ -3280,7 +3489,8 @@ private: case OpCode::Type::Xmad: { UNIMPLEMENTED_IF(instr.xmad.sign_a); UNIMPLEMENTED_IF(instr.xmad.sign_b); - UNIMPLEMENTED_IF_MSG(instr.generates_cc, "XMAD Generates an unhandled Control Code"); + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in XMAD is not implemented"); std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; std::string op_b; @@ -3372,9 +3582,9 @@ private: default: { switch (opcode->get().GetId()) { case OpCode::Id::EXIT: { - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ControlCode::T, - "EXIT Control Code used: {}", static_cast<u32>(cc)); + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; + UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, + "EXIT condition code used: {}", static_cast<u32>(cc)); if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { EmitFragmentOutputsWrite(); @@ -3406,9 +3616,9 @@ private: case OpCode::Id::KIL: { UNIMPLEMENTED_IF(instr.flow.cond != Tegra::Shader::FlowCondition::Always); - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ControlCode::T, - "KIL Control Code used: {}", static_cast<u32>(cc)); + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; + UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, + "KIL condition code used: {}", static_cast<u32>(cc)); // Enclose "discard" in a conditional, so that GLSL compilation does not complain // about unexecuted instructions that may follow this. @@ -3448,6 +3658,11 @@ private: regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1); break; } + case Tegra::Shader::SystemVariable::Ydirection: { + // Config pack's third value is Y_NEGATE's state. + regs.SetRegisterToFloat(instr.gpr0, 0, "uintBitsToFloat(config_pack[2])", 1, 1); + break; + } default: { UNIMPLEMENTED_MSG("Unhandled system move: {}", static_cast<u32>(instr.sys20.Value())); @@ -3470,12 +3685,18 @@ private: UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, "BRA with constant buffers are not implemented"); - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ControlCode::T, - "BRA Control Code used: {}", static_cast<u32>(cc)); - + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; const u32 target = offset + instr.bra.GetBranchTarget(); - shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + if (cc != Tegra::Shader::ConditionCode::T) { + const std::string condition_code = regs.GetConditionCode(cc); + shader.AddLine("if (" + condition_code + "){"); + shader.scope++; + shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + shader.scope--; + shader.AddLine('}'); + } else { + shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + } break; } case OpCode::Id::IPA: { @@ -3515,9 +3736,9 @@ private: break; } case OpCode::Id::SYNC: { - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ControlCode::T, - "SYNC Control Code used: {}", static_cast<u32>(cc)); + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; + UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, + "SYNC condition code used: {}", static_cast<u32>(cc)); // The SYNC opcode jumps to the address previously set by the SSY opcode EmitPopFromFlowStack(); @@ -3525,10 +3746,10 @@ private: } case OpCode::Id::BRK: { // The BRK opcode jumps to the address previously set by the PBK opcode - const Tegra::Shader::ControlCode cc = instr.flow_control_code; - if (cc != Tegra::Shader::ControlCode::T) { - UNIMPLEMENTED_MSG("BRK Control Code used: {}", static_cast<u32>(cc)); - } + const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; + UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, + "BRK condition code used: {}", static_cast<u32>(cc)); + EmitPopFromFlowStack(); break; } @@ -3539,6 +3760,9 @@ private: break; } case OpCode::Id::VMAD: { + UNIMPLEMENTED_IF_MSG(instr.generates_cc, + "Condition codes generation in VMAD is not implemented"); + const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; const std::string op_a = GetVideoOperandA(instr); const std::string op_b = GetVideoOperandB(instr); @@ -3558,10 +3782,6 @@ private: regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, instr.vmad.saturate == 1, 0, Register::Size::Word, instr.vmad.cc); - if (instr.generates_cc) { - UNIMPLEMENTED_MSG("VMAD Generates an unhandled Control Code"); - } - break; } case OpCode::Id::VSETP: { @@ -3713,6 +3933,7 @@ private: Maxwell3D::Regs::ShaderStage stage; const std::string& suffix; u64 local_memory_size; + std::size_t shader_length; ShaderWriter shader; ShaderWriter declarations; @@ -3731,9 +3952,10 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) { try { - const auto subroutines = - ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); - GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); + ControlFlowAnalyzer analyzer(program_code, main_offset, suffix); + const auto subroutines = analyzer.GetSubroutines(); + GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix, + analyzer.GetShaderLength()); return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; } catch (const DecompileFail& exception) { LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index eea090e52..23ed91e27 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -24,8 +24,7 @@ layout (location = 0) out vec4 position; layout(std140) uniform vs_config { vec4 viewport_flip; - uvec4 instance_id; - uvec4 flip_stage; + uvec4 config_pack; // instance_id, flip_stage, y_direction, padding uvec4 alpha_test; }; )"; @@ -63,7 +62,8 @@ void main() { out += R"( // Check if the flip stage is VertexB - if (flip_stage[0] == 1) { + // Config pack's second value is flip_stage + if (config_pack[1] == 1) { // Viewport can be flipped, which is unsupported by glViewport position.xy *= viewport_flip.xy; } @@ -71,7 +71,7 @@ void main() { // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 // For now, this is here to bring order in lieu of proper emulation - if (flip_stage[0] == 1) { + if (config_pack[1] == 1) { position.w = 1.0; } } @@ -101,8 +101,7 @@ layout (location = 0) out vec4 position; layout (std140) uniform gs_config { vec4 viewport_flip; - uvec4 instance_id; - uvec4 flip_stage; + uvec4 config_pack; // instance_id, flip_stage, y_direction, padding uvec4 alpha_test; }; @@ -139,8 +138,7 @@ layout (location = 0) in vec4 position; layout (std140) uniform fs_config { vec4 viewport_flip; - uvec4 instance_id; - uvec4 flip_stage; + uvec4 config_pack; // instance_id, flip_stage, y_direction, padding uvec4 alpha_test; }; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 520b9d4e3..b425d98ae 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -163,6 +163,7 @@ private: struct ShaderEntries { std::vector<ConstBufferEntry> const_buffer_entries; std::vector<SamplerEntry> texture_samplers; + std::size_t shader_length; }; using ProgramResult = std::pair<std::string, ShaderEntries>; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 8b8869ecb..6a30c28d2 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -27,16 +27,18 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh alpha_test.func = func; alpha_test.ref = regs.alpha_test_ref; - // We only assign the instance to the first component of the vector, the rest is just padding. - instance_id[0] = state.current_instance; + instance_id = state.current_instance; // Assign in which stage the position has to be flipped // (the last stage before the fragment shader). if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) { - flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); + flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); } else { - flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); + flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); } + + // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value. + y_direction = regs.screen_y_control.y_negate == 0 ? 1.f : -1.f; } } // namespace OpenGL::GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 9a5d7e289..b757f5f44 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -21,8 +21,11 @@ using Tegra::Engines::Maxwell3D; struct MaxwellUniformData { void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); alignas(16) GLvec4 viewport_flip; - alignas(16) GLuvec4 instance_id; - alignas(16) GLuvec4 flip_stage; + struct alignas(16) { + GLuint instance_id; + GLuint flip_stage; + GLfloat y_direction; + }; struct alignas(16) { GLuint enabled; GLuint func; @@ -30,7 +33,7 @@ struct MaxwellUniformData { GLuint padding; } alpha_test; }; -static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect"); +static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect"); static_assert(sizeof(MaxwellUniformData) < 16384, "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index d9910c6e8..dc0a5ed5e 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -92,6 +92,14 @@ OpenGLState::OpenGLState() { point.size = 1; fragment_color_clamp.enabled = false; + depth_clamp.far_plane = false; + depth_clamp.near_plane = false; + polygon_offset.fill_enable = false; + polygon_offset.line_enable = false; + polygon_offset.point_enable = false; + polygon_offset.factor = 0.0f; + polygon_offset.units = 0.0f; + polygon_offset.clamp = 0.0f; } void OpenGLState::ApplyDefaultState() { @@ -140,7 +148,7 @@ void OpenGLState::ApplyCulling() const { } void OpenGLState::ApplyColorMask() const { - if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) { + if (independant_blend.enabled) { for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { const auto& updated = color_mask[i]; const auto& current = cur_state.color_mask[i]; @@ -233,16 +241,40 @@ void OpenGLState::ApplyStencilTest() const { config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); } } +// Viewport does not affects glClearBuffer so emulate viewport using scissor test +void OpenGLState::EmulateViewportWithScissor() { + auto& current = viewports[0]; + if (current.scissor.enabled) { + const GLint left = std::max(current.x, current.scissor.x); + const GLint right = + std::max(current.x + current.width, current.scissor.x + current.scissor.width); + const GLint bottom = std::max(current.y, current.scissor.y); + const GLint top = + std::max(current.y + current.height, current.scissor.y + current.scissor.height); + current.scissor.x = std::max(left, 0); + current.scissor.y = std::max(bottom, 0); + current.scissor.width = std::max(right - left, 0); + current.scissor.height = std::max(top - bottom, 0); + } else { + current.scissor.enabled = true; + current.scissor.x = current.x; + current.scissor.y = current.y; + current.scissor.width = current.width; + current.scissor.height = current.height; + } +} void OpenGLState::ApplyViewport() const { - if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) { + if (geometry_shaders.enabled) { for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports); i++) { const auto& current = cur_state.viewports[i]; const auto& updated = viewports[i]; if (updated.x != current.x || updated.y != current.y || updated.width != current.width || updated.height != current.height) { - glViewportIndexedf(i, updated.x, updated.y, updated.width, updated.height); + glViewportIndexedf( + i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y), + static_cast<GLfloat>(updated.width), static_cast<GLfloat>(updated.height)); } if (updated.depth_range_near != current.depth_range_near || updated.depth_range_far != current.depth_range_far) { @@ -270,8 +302,7 @@ void OpenGLState::ApplyViewport() const { const auto& updated = viewports[0]; if (updated.x != current.x || updated.y != current.y || updated.width != current.width || updated.height != current.height) { - glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y), - static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height)); + glViewport(updated.x, updated.y, updated.width, updated.height); } if (updated.depth_range_near != current.depth_range_near || updated.depth_range_far != current.depth_range_far) { @@ -339,14 +370,14 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { if (blend_changed || updated.src_rgb_func != current.src_rgb_func || updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { - glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func, - updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); + glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func, + updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); } if (blend_changed || updated.rgb_equation != current.rgb_equation || updated.a_equation != current.a_equation) { - glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation, - updated.a_equation); + glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation, + updated.a_equation); } } @@ -383,6 +414,55 @@ void OpenGLState::ApplyLogicOp() const { } } +void OpenGLState::ApplyPolygonOffset() const { + + const bool fill_enable_changed = + polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable; + const bool line_enable_changed = + polygon_offset.line_enable != cur_state.polygon_offset.line_enable; + const bool point_enable_changed = + polygon_offset.point_enable != cur_state.polygon_offset.point_enable; + const bool factor_changed = polygon_offset.factor != cur_state.polygon_offset.factor; + const bool units_changed = polygon_offset.units != cur_state.polygon_offset.units; + const bool clamp_changed = polygon_offset.clamp != cur_state.polygon_offset.clamp; + + if (fill_enable_changed) { + if (polygon_offset.fill_enable) { + glEnable(GL_POLYGON_OFFSET_FILL); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + } + + if (line_enable_changed) { + if (polygon_offset.line_enable) { + glEnable(GL_POLYGON_OFFSET_LINE); + } else { + glDisable(GL_POLYGON_OFFSET_LINE); + } + } + + if (point_enable_changed) { + if (polygon_offset.point_enable) { + glEnable(GL_POLYGON_OFFSET_POINT); + } else { + glDisable(GL_POLYGON_OFFSET_POINT); + } + } + + if ((polygon_offset.fill_enable || polygon_offset.line_enable || polygon_offset.point_enable) && + (factor_changed || units_changed || clamp_changed)) { + + if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) { + glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp); + } else { + glPolygonOffset(polygon_offset.factor, polygon_offset.units); + UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0, + "Unimplemented Depth polygon offset clamp."); + } + } +} + void OpenGLState::ApplyTextures() const { for (std::size_t i = 0; i < std::size(texture_units); ++i) { const auto& texture_unit = texture_units[i]; @@ -446,6 +526,21 @@ void OpenGLState::ApplyVertexBufferState() const { } } +void OpenGLState::ApplyDepthClamp() const { + if (depth_clamp.far_plane == cur_state.depth_clamp.far_plane && + depth_clamp.near_plane == cur_state.depth_clamp.near_plane) { + return; + } + if (depth_clamp.far_plane != depth_clamp.near_plane) { + UNIMPLEMENTED_MSG("Unimplemented Depth Clamp Separation!"); + } + if (depth_clamp.far_plane || depth_clamp.near_plane) { + glEnable(GL_DEPTH_CLAMP); + } else { + glDisable(GL_DEPTH_CLAMP); + } +} + void OpenGLState::Apply() const { ApplyFramebufferState(); ApplyVertexBufferState(); @@ -477,11 +572,9 @@ void OpenGLState::Apply() const { if (point.size != cur_state.point.size) { glPointSize(point.size); } - if (GLAD_GL_ARB_color_buffer_float) { - if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) { - glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB, - fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE); - } + if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) { + glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB, + fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE); } if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) { if (multisample_control.alpha_to_coverage) { @@ -497,7 +590,7 @@ void OpenGLState::Apply() const { glDisable(GL_SAMPLE_ALPHA_TO_ONE); } } - + ApplyDepthClamp(); ApplyColorMask(); ApplyViewport(); ApplyStencilTest(); @@ -509,6 +602,7 @@ void OpenGLState::Apply() const { ApplyLogicOp(); ApplyTextures(); ApplySamplers(); + ApplyPolygonOffset(); cur_state = *this; } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index bdc743b0f..439bfbc98 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -49,6 +49,11 @@ public: } fragment_color_clamp; struct { + bool far_plane; + bool near_plane; + } depth_clamp; // GL_DEPTH_CLAMP + + struct { bool enabled; // viewports arrays are only supported when geometry shaders are enabled. } geometry_shaders; @@ -156,10 +161,10 @@ public: } draw; struct viewport { - GLfloat x; - GLfloat y; - GLfloat width; - GLfloat height; + GLint x; + GLint y; + GLint width; + GLint height; GLfloat depth_range_near; // GL_DEPTH_RANGE GLfloat depth_range_far; // GL_DEPTH_RANGE struct { @@ -176,7 +181,16 @@ public: float size; // GL_POINT_SIZE } point; - std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE + struct { + bool point_enable; + bool line_enable; + bool fill_enable; + GLfloat units; + GLfloat factor; + GLfloat clamp; + } polygon_offset; + + std::array<bool, 8> clip_distance; // GL_CLIP_DISTANCE OpenGLState(); @@ -206,6 +220,7 @@ public: OpenGLState& ResetBuffer(GLuint handle); OpenGLState& ResetVertexArray(GLuint handle); OpenGLState& ResetFramebuffer(GLuint handle); + void EmulateViewportWithScissor(); private: static OpenGLState cur_state; @@ -225,6 +240,8 @@ private: void ApplyLogicOp() const; void ApplyTextures() const; void ApplySamplers() const; + void ApplyDepthClamp() const; + void ApplyPolygonOffset() const; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 065b3929c..a8833c06e 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -218,14 +218,19 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { switch (equation) { case Maxwell::Blend::Equation::Add: + case Maxwell::Blend::Equation::AddGL: return GL_FUNC_ADD; case Maxwell::Blend::Equation::Subtract: + case Maxwell::Blend::Equation::SubtractGL: return GL_FUNC_SUBTRACT; case Maxwell::Blend::Equation::ReverseSubtract: + case Maxwell::Blend::Equation::ReverseSubtractGL: return GL_FUNC_REVERSE_SUBTRACT; case Maxwell::Blend::Equation::Min: + case Maxwell::Blend::Equation::MinGL: return GL_MIN; case Maxwell::Blend::Equation::Max: + case Maxwell::Blend::Equation::MaxGL: return GL_MAX; } LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 27b5b8960..4fd0d66c5 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -19,9 +19,9 @@ #include "core/settings.h" #include "core/telemetry_session.h" #include "core/tracer/recorder.h" +#include "video_core/morton.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/renderer_opengl.h" -#include "video_core/utils.h" namespace OpenGL { @@ -490,7 +490,7 @@ bool RendererOpenGL::Init() { Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model); Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version); - if (!GLAD_GL_VERSION_3_3) { + if (!GLAD_GL_VERSION_4_3) { return false; } |