mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #1256 from bunnei/tex-target-support
Initial support for non-2D textures
This commit is contained in:
		
						commit
						af074ee422
					
				| @ -293,10 +293,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { | ||||
|                    tic_entry.header_version == Texture::TICHeaderVersion::Pitch, | ||||
|                "TIC versions other than BlockLinear or Pitch are unimplemented"); | ||||
| 
 | ||||
|     ASSERT_MSG((tic_entry.texture_type == Texture::TextureType::Texture2D) || | ||||
|                    (tic_entry.texture_type == Texture::TextureType::Texture2DNoMipmap), | ||||
|                "Texture types other than Texture2D are unimplemented"); | ||||
| 
 | ||||
|     auto r_type = tic_entry.r_type.Value(); | ||||
|     auto g_type = tic_entry.g_type.Value(); | ||||
|     auto b_type = tic_entry.b_type.Value(); | ||||
|  | ||||
| @ -586,7 +586,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, | ||||
| void RasterizerOpenGL::SamplerInfo::Create() { | ||||
|     sampler.Create(); | ||||
|     mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear; | ||||
|     wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap; | ||||
|     wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap; | ||||
| 
 | ||||
|     // default is GL_LINEAR_MIPMAP_LINEAR
 | ||||
|     glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
| @ -613,8 +613,13 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr | ||||
|         wrap_v = config.wrap_v; | ||||
|         glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v)); | ||||
|     } | ||||
|     if (wrap_p != config.wrap_p) { | ||||
|         wrap_p = config.wrap_p; | ||||
|         glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p)); | ||||
|     } | ||||
| 
 | ||||
|     if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) { | ||||
|     if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || | ||||
|         wrap_p == Tegra::Texture::WrapMode::Border) { | ||||
|         const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, | ||||
|                                           config.border_color_b, config.border_color_a}}; | ||||
|         if (border_color != new_border_color) { | ||||
| @ -698,14 +703,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, | ||||
|         const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); | ||||
| 
 | ||||
|         if (!texture.enabled) { | ||||
|             state.texture_units[current_bindpoint].texture_2d = 0; | ||||
|             state.texture_units[current_bindpoint].texture = 0; | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); | ||||
|         Surface surface = res_cache.GetTextureSurface(texture); | ||||
|         if (surface != nullptr) { | ||||
|             state.texture_units[current_bindpoint].texture_2d = surface->Texture().handle; | ||||
|             state.texture_units[current_bindpoint].texture = surface->Texture().handle; | ||||
|             state.texture_units[current_bindpoint].target = surface->Target(); | ||||
|             state.texture_units[current_bindpoint].swizzle.r = | ||||
|                 MaxwellToGL::SwizzleSource(texture.tic.x_source); | ||||
|             state.texture_units[current_bindpoint].swizzle.g = | ||||
| @ -716,7 +722,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, | ||||
|                 MaxwellToGL::SwizzleSource(texture.tic.w_source); | ||||
|         } else { | ||||
|             // Can occur when texture addr is null or its memory is unmapped/invalid
 | ||||
|             state.texture_units[current_bindpoint].texture_2d = 0; | ||||
|             state.texture_units[current_bindpoint].texture = 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -93,6 +93,7 @@ private: | ||||
|         Tegra::Texture::TextureFilter min_filter; | ||||
|         Tegra::Texture::WrapMode wrap_u; | ||||
|         Tegra::Texture::WrapMode wrap_v; | ||||
|         Tegra::Texture::WrapMode wrap_p; | ||||
|         GLvec4 border_color; | ||||
|     }; | ||||
| 
 | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| 
 | ||||
| #include "common/alignment.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/microprofile.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "core/core.h" | ||||
| @ -51,10 +52,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | ||||
|     params.type = GetFormatType(params.pixel_format); | ||||
|     params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); | ||||
|     params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); | ||||
|     params.depth = config.tic.Depth(); | ||||
|     params.unaligned_height = config.tic.Height(); | ||||
|     params.size_in_bytes = params.SizeInBytes(); | ||||
|     params.cache_width = Common::AlignUp(params.width, 16); | ||||
|     params.cache_height = Common::AlignUp(params.height, 16); | ||||
|     params.cache_width = Common::AlignUp(params.width, 8); | ||||
|     params.cache_height = Common::AlignUp(params.height, 8); | ||||
|     params.target = SurfaceTargetFromTextureType(config.tic.texture_type); | ||||
|     return params; | ||||
| } | ||||
| 
 | ||||
| @ -69,10 +72,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | ||||
|     params.type = GetFormatType(params.pixel_format); | ||||
|     params.width = config.width; | ||||
|     params.height = config.height; | ||||
|     params.depth = 1; | ||||
|     params.unaligned_height = config.height; | ||||
|     params.size_in_bytes = params.SizeInBytes(); | ||||
|     params.cache_width = Common::AlignUp(params.width, 16); | ||||
|     params.cache_height = Common::AlignUp(params.height, 16); | ||||
|     params.cache_width = Common::AlignUp(params.width, 8); | ||||
|     params.cache_height = Common::AlignUp(params.height, 8); | ||||
|     params.target = SurfaceTarget::Texture2D; | ||||
|     return params; | ||||
| } | ||||
| 
 | ||||
| @ -86,13 +91,14 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | ||||
|     params.pixel_format = PixelFormatFromDepthFormat(format); | ||||
|     params.component_type = ComponentTypeFromDepthFormat(format); | ||||
|     params.type = GetFormatType(params.pixel_format); | ||||
|     params.size_in_bytes = params.SizeInBytes(); | ||||
|     params.width = zeta_width; | ||||
|     params.height = zeta_height; | ||||
|     params.depth = 1; | ||||
|     params.unaligned_height = zeta_height; | ||||
|     params.size_in_bytes = params.SizeInBytes(); | ||||
|     params.cache_width = Common::AlignUp(params.width, 16); | ||||
|     params.cache_height = Common::AlignUp(params.height, 16); | ||||
|     params.cache_width = Common::AlignUp(params.width, 8); | ||||
|     params.cache_height = Common::AlignUp(params.height, 8); | ||||
|     params.target = SurfaceTarget::Texture2D; | ||||
|     return params; | ||||
| } | ||||
| 
 | ||||
| @ -166,6 +172,26 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | ||||
|      ComponentType::Float, false}, // Z32FS8
 | ||||
| }}; | ||||
| 
 | ||||
| static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) { | ||||
|     switch (target) { | ||||
|     case SurfaceParams::SurfaceTarget::Texture1D: | ||||
|         return GL_TEXTURE_1D; | ||||
|     case SurfaceParams::SurfaceTarget::Texture2D: | ||||
|         return GL_TEXTURE_2D; | ||||
|     case SurfaceParams::SurfaceTarget::Texture3D: | ||||
|         return GL_TEXTURE_3D; | ||||
|     case SurfaceParams::SurfaceTarget::Texture1DArray: | ||||
|         return GL_TEXTURE_1D_ARRAY; | ||||
|     case SurfaceParams::SurfaceTarget::Texture2DArray: | ||||
|         return GL_TEXTURE_2D_ARRAY; | ||||
|     case SurfaceParams::SurfaceTarget::TextureCubemap: | ||||
|         return GL_TEXTURE_CUBE_MAP; | ||||
|     } | ||||
|     LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); | ||||
|     UNREACHABLE(); | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { | ||||
|     ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); | ||||
|     auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)]; | ||||
| @ -220,7 +246,8 @@ static bool IsFormatBCn(PixelFormat format) { | ||||
| } | ||||
| 
 | ||||
| template <bool morton_to_gl, PixelFormat format> | ||||
| void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer, VAddr addr) { | ||||
| void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, size_t gl_buffer_size, | ||||
|                 VAddr addr) { | ||||
|     constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; | ||||
|     constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); | ||||
| 
 | ||||
| @ -230,18 +257,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu | ||||
|         const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; | ||||
|         const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( | ||||
|             addr, tile_size, bytes_per_pixel, stride, height, block_height); | ||||
|         const size_t size_to_copy{std::min(gl_buffer.size(), data.size())}; | ||||
|         gl_buffer.assign(data.begin(), data.begin() + size_to_copy); | ||||
|         const size_t size_to_copy{std::min(gl_buffer_size, data.size())}; | ||||
|         memcpy(gl_buffer, data.data(), size_to_copy); | ||||
|     } else { | ||||
|         // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
 | ||||
|         // check the configuration for this and perform more generic un/swizzle
 | ||||
|         LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); | ||||
|         VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, | ||||
|                                        Memory::GetPointer(addr), gl_buffer.data(), morton_to_gl); | ||||
|                                        Memory::GetPointer(addr), gl_buffer, morton_to_gl); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | ||||
| static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), | ||||
|                             SurfaceParams::MaxPixelFormat> | ||||
|     morton_to_gl_fns = { | ||||
|         // clang-format off
 | ||||
| @ -298,7 +325,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | ||||
|         // clang-format on
 | ||||
| }; | ||||
| 
 | ||||
| static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | ||||
| static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), | ||||
|                             SurfaceParams::MaxPixelFormat> | ||||
|     gl_to_morton_fns = { | ||||
|         // clang-format off
 | ||||
| @ -357,33 +384,6 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), | ||||
|         // clang-format on
 | ||||
| }; | ||||
| 
 | ||||
| // Allocate an uninitialized texture of appropriate size and format for the surface
 | ||||
| static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tuple, u32 width, | ||||
|                                    u32 height) { | ||||
|     OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
| 
 | ||||
|     // Keep track of previous texture bindings
 | ||||
|     GLuint old_tex = cur_state.texture_units[0].texture_2d; | ||||
|     cur_state.texture_units[0].texture_2d = texture; | ||||
|     cur_state.Apply(); | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
| 
 | ||||
|     if (!format_tuple.compressed) { | ||||
|         // Only pre-create the texture for non-compressed textures.
 | ||||
|         glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0, | ||||
|                      format_tuple.format, format_tuple.type, nullptr); | ||||
|     } | ||||
| 
 | ||||
|     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | ||||
|     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
|     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||
|     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||
| 
 | ||||
|     // Restore previous texture bindings
 | ||||
|     cur_state.texture_units[0].texture_2d = old_tex; | ||||
|     cur_state.Apply(); | ||||
| } | ||||
| 
 | ||||
| static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, | ||||
|                          const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, | ||||
|                          GLuint read_fb_handle, GLuint draw_fb_handle) { | ||||
| @ -438,12 +438,56 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) { | ||||
| CachedSurface::CachedSurface(const SurfaceParams& params) | ||||
|     : params(params), gl_target(SurfaceTargetToGL(params.target)) { | ||||
|     texture.Create(); | ||||
|     const auto& rect{params.GetRect()}; | ||||
|     AllocateSurfaceTexture(texture.handle, | ||||
|                            GetFormatTuple(params.pixel_format, params.component_type), | ||||
|                            rect.GetWidth(), rect.GetHeight()); | ||||
| 
 | ||||
|     // Keep track of previous texture bindings
 | ||||
|     OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
|     const auto& old_tex = cur_state.texture_units[0]; | ||||
|     SCOPE_EXIT({ | ||||
|         cur_state.texture_units[0] = old_tex; | ||||
|         cur_state.Apply(); | ||||
|     }); | ||||
| 
 | ||||
|     cur_state.texture_units[0].texture = texture.handle; | ||||
|     cur_state.texture_units[0].target = SurfaceTargetToGL(params.target); | ||||
|     cur_state.Apply(); | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
| 
 | ||||
|     const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type); | ||||
|     if (!format_tuple.compressed) { | ||||
|         // Only pre-create the texture for non-compressed textures.
 | ||||
|         switch (params.target) { | ||||
|         case SurfaceParams::SurfaceTarget::Texture1D: | ||||
|             glTexImage1D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format, | ||||
|                          rect.GetWidth(), 0, format_tuple.format, format_tuple.type, nullptr); | ||||
|             break; | ||||
|         case SurfaceParams::SurfaceTarget::Texture2D: | ||||
|             glTexImage2D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format, | ||||
|                          rect.GetWidth(), rect.GetHeight(), 0, format_tuple.format, | ||||
|                          format_tuple.type, nullptr); | ||||
|             break; | ||||
|         case SurfaceParams::SurfaceTarget::Texture3D: | ||||
|         case SurfaceParams::SurfaceTarget::Texture2DArray: | ||||
|             glTexImage3D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format, | ||||
|                          rect.GetWidth(), rect.GetHeight(), params.depth, 0, format_tuple.format, | ||||
|                          format_tuple.type, nullptr); | ||||
|             break; | ||||
|         default: | ||||
|             LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||||
|                          static_cast<u32>(params.target)); | ||||
|             UNREACHABLE(); | ||||
|             glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, rect.GetWidth(), | ||||
|                          rect.GetHeight(), 0, format_tuple.format, format_tuple.type, nullptr); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL, 0); | ||||
|     glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
|     glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||
|     glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||
| } | ||||
| 
 | ||||
| static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { | ||||
| @ -514,23 +558,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Helper function to perform software conversion (as needed) when flushing a buffer to Switch | ||||
|  * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with | ||||
|  * typical desktop GPUs. | ||||
|  */ | ||||
| static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& /*data*/, PixelFormat pixel_format, | ||||
|                                                 u32 /*width*/, u32 /*height*/) { | ||||
|     switch (pixel_format) { | ||||
|     case PixelFormat::ASTC_2D_4X4: | ||||
|     case PixelFormat::S8Z24: | ||||
|         LOG_CRITICAL(Render_OpenGL, "Unimplemented pixel_format={}", | ||||
|                      static_cast<u32>(pixel_format)); | ||||
|         UNREACHABLE(); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); | ||||
| void CachedSurface::LoadGLBuffer() { | ||||
|     ASSERT(params.type != SurfaceType::Fill); | ||||
| @ -545,13 +572,24 @@ void CachedSurface::LoadGLBuffer() { | ||||
|     MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); | ||||
| 
 | ||||
|     if (params.is_tiled) { | ||||
|         gl_buffer.resize(copy_size); | ||||
|         // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do
 | ||||
|         // this for 3D textures, etc.
 | ||||
|         switch (params.target) { | ||||
|         case SurfaceParams::SurfaceTarget::Texture2D: | ||||
|             // Pass impl. to the fallback code below
 | ||||
|             break; | ||||
|         default: | ||||
|             LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}", | ||||
|                          static_cast<u32>(params.target)); | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
| 
 | ||||
|         gl_buffer.resize(params.depth * copy_size); | ||||
|         morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( | ||||
|             params.width, params.block_height, params.height, gl_buffer, params.addr); | ||||
|             params.width, params.block_height, params.height, gl_buffer.data(), copy_size, | ||||
|             params.addr); | ||||
|     } else { | ||||
|         const u8* const texture_src_data_end = texture_src_data + copy_size; | ||||
| 
 | ||||
|         const u8* const texture_src_data_end{texture_src_data + (params.depth * copy_size)}; | ||||
|         gl_buffer.assign(texture_src_data, texture_src_data_end); | ||||
|     } | ||||
| 
 | ||||
| @ -560,23 +598,7 @@ void CachedSurface::LoadGLBuffer() { | ||||
| 
 | ||||
| MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); | ||||
| void CachedSurface::FlushGLBuffer() { | ||||
|     u8* const dst_buffer = Memory::GetPointer(params.addr); | ||||
| 
 | ||||
|     ASSERT(dst_buffer); | ||||
|     ASSERT(gl_buffer.size() == | ||||
|            params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); | ||||
| 
 | ||||
|     MICROPROFILE_SCOPE(OpenGL_SurfaceFlush); | ||||
| 
 | ||||
|     ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, | ||||
|                                         params.height); | ||||
| 
 | ||||
|     if (!params.is_tiled) { | ||||
|         std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes); | ||||
|     } else { | ||||
|         gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( | ||||
|             params.width, params.block_height, params.height, gl_buffer, params.addr); | ||||
|     } | ||||
|     ASSERT_MSG(false, "Unimplemented"); | ||||
| } | ||||
| 
 | ||||
| MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); | ||||
| @ -587,7 +609,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle | ||||
|     MICROPROFILE_SCOPE(OpenGL_TextureUL); | ||||
| 
 | ||||
|     ASSERT(gl_buffer.size() == | ||||
|            params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); | ||||
|            params.width * params.height * GetGLBytesPerPixel(params.pixel_format) * params.depth); | ||||
| 
 | ||||
|     const auto& rect{params.GetRect()}; | ||||
| 
 | ||||
| @ -600,8 +622,13 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle | ||||
|     GLuint target_tex = texture.handle; | ||||
|     OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
| 
 | ||||
|     GLuint old_tex = cur_state.texture_units[0].texture_2d; | ||||
|     cur_state.texture_units[0].texture_2d = target_tex; | ||||
|     const auto& old_tex = cur_state.texture_units[0]; | ||||
|     SCOPE_EXIT({ | ||||
|         cur_state.texture_units[0] = old_tex; | ||||
|         cur_state.Apply(); | ||||
|     }); | ||||
|     cur_state.texture_units[0].texture = target_tex; | ||||
|     cur_state.texture_units[0].target = SurfaceTargetToGL(params.target); | ||||
|     cur_state.Apply(); | ||||
| 
 | ||||
|     // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
 | ||||
| @ -610,74 +637,68 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle | ||||
| 
 | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
|     if (tuple.compressed) { | ||||
|         glCompressedTexImage2D( | ||||
|             GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), | ||||
|             static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes), | ||||
|             &gl_buffer[buffer_offset]); | ||||
|         switch (params.target) { | ||||
|         case SurfaceParams::SurfaceTarget::Texture2D: | ||||
|             glCompressedTexImage2D( | ||||
|                 SurfaceTargetToGL(params.target), 0, tuple.internal_format, | ||||
|                 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, | ||||
|                 static_cast<GLsizei>(params.size_in_bytes), &gl_buffer[buffer_offset]); | ||||
|             break; | ||||
|         case SurfaceParams::SurfaceTarget::Texture3D: | ||||
|         case SurfaceParams::SurfaceTarget::Texture2DArray: | ||||
|             glCompressedTexImage3D( | ||||
|                 SurfaceTargetToGL(params.target), 0, tuple.internal_format, | ||||
|                 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), | ||||
|                 static_cast<GLsizei>(params.depth), 0, static_cast<GLsizei>(params.size_in_bytes), | ||||
|                 &gl_buffer[buffer_offset]); | ||||
|             break; | ||||
|         default: | ||||
|             LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||||
|                          static_cast<u32>(params.target)); | ||||
|             UNREACHABLE(); | ||||
|             glCompressedTexImage2D( | ||||
|                 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), | ||||
|                 static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes), | ||||
|                 &gl_buffer[buffer_offset]); | ||||
|         } | ||||
|     } else { | ||||
|         glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), | ||||
|                         static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, | ||||
|                         &gl_buffer[buffer_offset]); | ||||
| 
 | ||||
|         switch (params.target) { | ||||
|         case SurfaceParams::SurfaceTarget::Texture1D: | ||||
|             glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0, | ||||
|                             static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type, | ||||
|                             &gl_buffer[buffer_offset]); | ||||
|             break; | ||||
|         case SurfaceParams::SurfaceTarget::Texture2D: | ||||
|             glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0, | ||||
|                             static_cast<GLsizei>(rect.GetWidth()), | ||||
|                             static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, | ||||
|                             &gl_buffer[buffer_offset]); | ||||
|             break; | ||||
|         case SurfaceParams::SurfaceTarget::Texture3D: | ||||
|         case SurfaceParams::SurfaceTarget::Texture2DArray: | ||||
|             glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0, | ||||
|                             static_cast<GLsizei>(rect.GetWidth()), | ||||
|                             static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, | ||||
|                             tuple.type, &gl_buffer[buffer_offset]); | ||||
|             break; | ||||
|         default: | ||||
|             LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||||
|                          static_cast<u32>(params.target)); | ||||
|             UNREACHABLE(); | ||||
|             glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), | ||||
|                             static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, | ||||
|                             &gl_buffer[buffer_offset]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||
| 
 | ||||
|     cur_state.texture_units[0].texture_2d = old_tex; | ||||
|     cur_state.Apply(); | ||||
| } | ||||
| 
 | ||||
| MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64)); | ||||
| void CachedSurface::DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { | ||||
|     if (params.type == SurfaceType::Fill) | ||||
|         return; | ||||
| 
 | ||||
|     MICROPROFILE_SCOPE(OpenGL_TextureDL); | ||||
| 
 | ||||
|     gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); | ||||
| 
 | ||||
|     OpenGLState state = OpenGLState::GetCurState(); | ||||
|     OpenGLState prev_state = state; | ||||
|     SCOPE_EXIT({ prev_state.Apply(); }); | ||||
| 
 | ||||
|     const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); | ||||
| 
 | ||||
|     // Ensure no bad interactions with GL_PACK_ALIGNMENT
 | ||||
|     ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); | ||||
|     glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); | ||||
| 
 | ||||
|     const auto& rect{params.GetRect()}; | ||||
|     size_t buffer_offset = | ||||
|         (rect.bottom * params.width + rect.left) * GetGLBytesPerPixel(params.pixel_format); | ||||
| 
 | ||||
|     state.UnbindTexture(texture.handle); | ||||
|     state.draw.read_framebuffer = read_fb_handle; | ||||
|     state.Apply(); | ||||
| 
 | ||||
|     if (params.type == SurfaceType::ColorTexture) { | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||
|                                texture.handle, 0); | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||||
|                                0); | ||||
|     } else if (params.type == SurfaceType::Depth) { | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, | ||||
|                                texture.handle, 0); | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||
|     } else { | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||
|                                texture.handle, 0); | ||||
|     } | ||||
|     glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom), | ||||
|                  static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), | ||||
|                  tuple.format, tuple.type, &gl_buffer[buffer_offset]); | ||||
| 
 | ||||
|     glPixelStorei(GL_PACK_ROW_LENGTH, 0); | ||||
| } | ||||
| 
 | ||||
| RasterizerCacheOpenGL::RasterizerCacheOpenGL() { | ||||
|     read_framebuffer.Create(); | ||||
|     draw_framebuffer.Create(); | ||||
|     copy_pbo.Create(); | ||||
| } | ||||
| 
 | ||||
| Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { | ||||
| @ -748,7 +769,6 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { | ||||
| } | ||||
| 
 | ||||
| void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) { | ||||
|     surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); | ||||
|     surface->FlushGLBuffer(); | ||||
| } | ||||
| 
 | ||||
| @ -809,8 +829,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | ||||
|     // If format is unchanged, we can do a faster blit without reinterpreting pixel data
 | ||||
|     if (params.pixel_format == new_params.pixel_format) { | ||||
|         BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle, | ||||
|                      new_surface->GetSurfaceParams().GetRect(), params.type, | ||||
|                      read_framebuffer.handle, draw_framebuffer.handle); | ||||
|                      params.GetRect(), params.type, read_framebuffer.handle, | ||||
|                      draw_framebuffer.handle); | ||||
|         return new_surface; | ||||
|     } | ||||
| 
 | ||||
| @ -821,12 +841,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | ||||
| 
 | ||||
|         size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes()); | ||||
| 
 | ||||
|         // Use a Pixel Buffer Object to download the previous texture and then upload it to the new
 | ||||
|         // one using the new format.
 | ||||
|         OGLBuffer pbo; | ||||
|         pbo.Create(); | ||||
| 
 | ||||
|         glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle); | ||||
|         glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle); | ||||
|         glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); | ||||
|         if (source_format.compressed) { | ||||
|             glGetCompressedTextureImage(surface->Texture().handle, 0, | ||||
| @ -845,8 +860,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | ||||
|                 // of the data in this case. Games like Super Mario Odyssey seem to hit this case
 | ||||
|                 // when drawing, it re-uses the memory of a previous texture as a bigger framebuffer
 | ||||
|                 // but it doesn't clear it beforehand, the texture is already full of zeros.
 | ||||
|                 LOG_CRITICAL(HW_GPU, "Trying to upload extra texture data from the CPU during " | ||||
|                                      "reinterpretation but the texture is tiled."); | ||||
|                 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " | ||||
|                                   "reinterpretation but the texture is tiled."); | ||||
|             } | ||||
|             size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes(); | ||||
|             std::vector<u8> data(remaining_size); | ||||
| @ -859,21 +874,38 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | ||||
| 
 | ||||
|         const auto& dest_rect{new_params.GetRect()}; | ||||
| 
 | ||||
|         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle); | ||||
|         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo.handle); | ||||
|         if (dest_format.compressed) { | ||||
|             glCompressedTexSubImage2D( | ||||
|                 GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()), | ||||
|                 static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, | ||||
|                 static_cast<GLsizei>(new_params.SizeInBytes()), nullptr); | ||||
|             LOG_CRITICAL(HW_GPU, "Compressed copy is unimplemented!"); | ||||
|             UNREACHABLE(); | ||||
|         } else { | ||||
|             glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0, | ||||
|                                 static_cast<GLsizei>(dest_rect.GetWidth()), | ||||
|                                 static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, | ||||
|                                 dest_format.type, nullptr); | ||||
|             switch (new_params.target) { | ||||
|             case SurfaceParams::SurfaceTarget::Texture1D: | ||||
|                 glTextureSubImage1D(new_surface->Texture().handle, 0, 0, | ||||
|                                     static_cast<GLsizei>(dest_rect.GetWidth()), dest_format.format, | ||||
|                                     dest_format.type, nullptr); | ||||
|                 break; | ||||
|             case SurfaceParams::SurfaceTarget::Texture2D: | ||||
|                 glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0, | ||||
|                                     static_cast<GLsizei>(dest_rect.GetWidth()), | ||||
|                                     static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, | ||||
|                                     dest_format.type, nullptr); | ||||
|                 break; | ||||
|             case SurfaceParams::SurfaceTarget::Texture3D: | ||||
|             case SurfaceParams::SurfaceTarget::Texture2DArray: | ||||
|                 glTextureSubImage3D(new_surface->Texture().handle, 0, 0, 0, 0, | ||||
|                                     static_cast<GLsizei>(dest_rect.GetWidth()), | ||||
|                                     static_cast<GLsizei>(dest_rect.GetHeight()), | ||||
|                                     static_cast<GLsizei>(new_params.depth), dest_format.format, | ||||
|                                     dest_format.type, nullptr); | ||||
|                 break; | ||||
|             default: | ||||
|                 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||||
|                              static_cast<u32>(params.target)); | ||||
|                 UNREACHABLE(); | ||||
|             } | ||||
|         } | ||||
|         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | ||||
| 
 | ||||
|         pbo.Release(); | ||||
|     } | ||||
| 
 | ||||
|     return new_surface; | ||||
|  | ||||
| @ -109,6 +109,33 @@ struct SurfaceParams { | ||||
|         Invalid = 4, | ||||
|     }; | ||||
| 
 | ||||
|     enum class SurfaceTarget { | ||||
|         Texture1D, | ||||
|         Texture2D, | ||||
|         Texture3D, | ||||
|         Texture1DArray, | ||||
|         Texture2DArray, | ||||
|         TextureCubemap, | ||||
|     }; | ||||
| 
 | ||||
|     static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) { | ||||
|         switch (texture_type) { | ||||
|         case Tegra::Texture::TextureType::Texture1D: | ||||
|             return SurfaceTarget::Texture1D; | ||||
|         case Tegra::Texture::TextureType::Texture2D: | ||||
|         case Tegra::Texture::TextureType::Texture2DNoMipmap: | ||||
|             return SurfaceTarget::Texture2D; | ||||
|         case Tegra::Texture::TextureType::Texture1DArray: | ||||
|             return SurfaceTarget::Texture1DArray; | ||||
|         case Tegra::Texture::TextureType::Texture2DArray: | ||||
|             return SurfaceTarget::Texture2DArray; | ||||
|         default: | ||||
|             LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type)); | ||||
|             UNREACHABLE(); | ||||
|             return SurfaceTarget::Texture2D; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the compression factor for the specified PixelFormat. This applies to just the | ||||
|      * "compressed width" and "compressed height", not the overall compression factor of a | ||||
| @ -635,7 +662,7 @@ struct SurfaceParams { | ||||
|         ASSERT(width % compression_factor == 0); | ||||
|         ASSERT(height % compression_factor == 0); | ||||
|         return (width / compression_factor) * (height / compression_factor) * | ||||
|                GetFormatBpp(pixel_format) / CHAR_BIT; | ||||
|                GetFormatBpp(pixel_format) * depth / CHAR_BIT; | ||||
|     } | ||||
| 
 | ||||
|     /// Creates SurfaceParams from a texture configuration
 | ||||
| @ -664,8 +691,10 @@ struct SurfaceParams { | ||||
|     SurfaceType type; | ||||
|     u32 width; | ||||
|     u32 height; | ||||
|     u32 depth; | ||||
|     u32 unaligned_height; | ||||
|     size_t size_in_bytes; | ||||
|     SurfaceTarget target; | ||||
| 
 | ||||
|     // Parameters used for caching only
 | ||||
|     u32 cache_width; | ||||
| @ -709,6 +738,10 @@ public: | ||||
|         return texture; | ||||
|     } | ||||
| 
 | ||||
|     GLenum Target() const { | ||||
|         return gl_target; | ||||
|     } | ||||
| 
 | ||||
|     static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) { | ||||
|         if (format == SurfaceParams::PixelFormat::Invalid) | ||||
|             return 0; | ||||
| @ -724,14 +757,14 @@ public: | ||||
|     void LoadGLBuffer(); | ||||
|     void FlushGLBuffer(); | ||||
| 
 | ||||
|     // Upload/Download data in gl_buffer in/to this surface's texture
 | ||||
|     // Upload data in gl_buffer to this surface's texture
 | ||||
|     void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); | ||||
|     void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); | ||||
| 
 | ||||
| private: | ||||
|     OGLTexture texture; | ||||
|     std::vector<u8> gl_buffer; | ||||
|     SurfaceParams params; | ||||
|     GLenum gl_target; | ||||
| }; | ||||
| 
 | ||||
| class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { | ||||
| @ -774,6 +807,10 @@ private: | ||||
| 
 | ||||
|     OGLFramebuffer read_framebuffer; | ||||
|     OGLFramebuffer draw_framebuffer; | ||||
| 
 | ||||
|     /// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
 | ||||
|     /// using the new format.
 | ||||
|     OGLBuffer copy_pbo; | ||||
| }; | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
|  | ||||
| @ -443,13 +443,12 @@ public: | ||||
|         } | ||||
|         declarations.AddNewLine(); | ||||
| 
 | ||||
|         // Append the sampler2D array for the used textures.
 | ||||
|         const size_t num_samplers = used_samplers.size(); | ||||
|         if (num_samplers > 0) { | ||||
|             declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' + | ||||
|                                  std::to_string(num_samplers) + "];"); | ||||
|             declarations.AddNewLine(); | ||||
|         const auto& samplers = GetSamplers(); | ||||
|         for (const auto& sampler : samplers) { | ||||
|             declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + | ||||
|                                  ';'); | ||||
|         } | ||||
|         declarations.AddNewLine(); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a list of constant buffer declarations
 | ||||
| @ -461,13 +460,14 @@ public: | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a list of samplers used in the shader
 | ||||
|     std::vector<SamplerEntry> GetSamplers() const { | ||||
|     const std::vector<SamplerEntry>& GetSamplers() const { | ||||
|         return used_samplers; | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
 | ||||
|     /// necessary.
 | ||||
|     std::string AccessSampler(const Sampler& sampler) { | ||||
|     std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, | ||||
|                               bool is_array) { | ||||
|         size_t offset = static_cast<size_t>(sampler.index.Value()); | ||||
| 
 | ||||
|         // If this sampler has already been used, return the existing mapping.
 | ||||
| @ -476,12 +476,13 @@ public: | ||||
|                          [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); | ||||
| 
 | ||||
|         if (itr != used_samplers.end()) { | ||||
|             ASSERT(itr->GetType() == type && itr->IsArray() == is_array); | ||||
|             return itr->GetName(); | ||||
|         } | ||||
| 
 | ||||
|         // Otherwise create a new mapping for this sampler
 | ||||
|         size_t next_index = used_samplers.size(); | ||||
|         SamplerEntry entry{stage, offset, next_index}; | ||||
|         SamplerEntry entry{stage, offset, next_index, type, is_array}; | ||||
|         used_samplers.emplace_back(entry); | ||||
|         return entry.GetName(); | ||||
|     } | ||||
| @ -722,8 +723,8 @@ private: | ||||
|     } | ||||
| 
 | ||||
|     /// Generates code representing a texture sampler.
 | ||||
|     std::string GetSampler(const Sampler& sampler) { | ||||
|         return regs.AccessSampler(sampler); | ||||
|     std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) { | ||||
|         return regs.AccessSampler(sampler, type, is_array); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
| @ -1753,10 +1754,35 @@ private: | ||||
|                 break; | ||||
|             } | ||||
|             case OpCode::Id::TEX: { | ||||
|                 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | ||||
|                 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||||
|                 const std::string sampler = GetSampler(instr.sampler); | ||||
|                 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; | ||||
|                 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented"); | ||||
|                 Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; | ||||
|                 std::string coord; | ||||
| 
 | ||||
|                 switch (texture_type) { | ||||
|                 case Tegra::Shader::TextureType::Texture1D: { | ||||
|                     std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||||
|                     coord = "float coords = " + x + ';'; | ||||
|                     break; | ||||
|                 } | ||||
|                 case Tegra::Shader::TextureType::Texture2D: { | ||||
|                     std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||||
|                     std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||||
|                     coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||||
|                     break; | ||||
|                 } | ||||
|                 default: | ||||
|                     LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | ||||
|                                  static_cast<u32>(texture_type)); | ||||
|                     UNREACHABLE(); | ||||
| 
 | ||||
|                     // Fallback to interpreting as a 2D texture for now
 | ||||
|                     std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||||
|                     std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||||
|                     coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||||
|                     texture_type = Tegra::Shader::TextureType::Texture2D; | ||||
|                 } | ||||
| 
 | ||||
|                 const std::string sampler = GetSampler(instr.sampler, texture_type, false); | ||||
|                 // 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("{"); | ||||
| @ -1778,20 +1804,65 @@ private: | ||||
|                 break; | ||||
|             } | ||||
|             case OpCode::Id::TEXS: { | ||||
|                 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | ||||
|                 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); | ||||
|                 const std::string sampler = GetSampler(instr.sampler); | ||||
|                 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; | ||||
|                 std::string coord; | ||||
|                 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; | ||||
|                 bool is_array{instr.texs.IsArrayTexture()}; | ||||
| 
 | ||||
|                 switch (texture_type) { | ||||
|                 case Tegra::Shader::TextureType::Texture2D: { | ||||
|                     if (is_array) { | ||||
|                         std::string index = regs.GetRegisterAsInteger(instr.gpr8); | ||||
|                         std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||||
|                         std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||||
|                         coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; | ||||
|                     } else { | ||||
|                         std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||||
|                         std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||||
|                         coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||||
|                     } | ||||
|                     break; | ||||
|                 } | ||||
|                 default: | ||||
|                     LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | ||||
|                                  static_cast<u32>(texture_type)); | ||||
|                     UNREACHABLE(); | ||||
| 
 | ||||
|                     // Fallback to interpreting as a 2D texture for now
 | ||||
|                     std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||||
|                     std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||||
|                     coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | ||||
|                     texture_type = Tegra::Shader::TextureType::Texture2D; | ||||
|                     is_array = false; | ||||
|                 } | ||||
|                 const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); | ||||
|                 const std::string texture = "texture(" + sampler + ", coords)"; | ||||
|                 WriteTexsInstruction(instr, coord, texture); | ||||
|                 break; | ||||
|             } | ||||
|             case OpCode::Id::TLDS: { | ||||
|                 const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); | ||||
|                 const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20); | ||||
|                 const std::string sampler = GetSampler(instr.sampler); | ||||
|                 const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");"; | ||||
|                 ASSERT(instr.tlds.GetTextureType() == Tegra::Shader::TextureType::Texture2D); | ||||
|                 ASSERT(instr.tlds.IsArrayTexture() == false); | ||||
|                 std::string coord; | ||||
| 
 | ||||
|                 switch (instr.tlds.GetTextureType()) { | ||||
|                 case Tegra::Shader::TextureType::Texture2D: { | ||||
|                     if (instr.tlds.IsArrayTexture()) { | ||||
|                         LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture"); | ||||
|                         UNREACHABLE(); | ||||
|                     } else { | ||||
|                         std::string x = regs.GetRegisterAsInteger(instr.gpr8); | ||||
|                         std::string y = regs.GetRegisterAsInteger(instr.gpr20); | ||||
|                         coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; | ||||
|                     } | ||||
|                     break; | ||||
|                 } | ||||
|                 default: | ||||
|                     LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", | ||||
|                                  static_cast<u32>(instr.tlds.GetTextureType())); | ||||
|                     UNREACHABLE(); | ||||
|                 } | ||||
|                 const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(), | ||||
|                                                        instr.tlds.IsArrayTexture()); | ||||
|                 const std::string texture = "texelFetch(" + sampler + ", coords, 0)"; | ||||
|                 WriteTexsInstruction(instr, coord, texture); | ||||
|                 break; | ||||
| @ -1799,7 +1870,7 @@ private: | ||||
|             case OpCode::Id::TLD4: { | ||||
|                 ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D); | ||||
|                 ASSERT(instr.tld4.array == 0); | ||||
|                 std::string coord{}; | ||||
|                 std::string coord; | ||||
| 
 | ||||
|                 switch (instr.tld4.texture_type) { | ||||
|                 case Tegra::Shader::TextureType::Texture2D: { | ||||
| @ -1814,7 +1885,8 @@ private: | ||||
|                     UNREACHABLE(); | ||||
|                 } | ||||
| 
 | ||||
|                 const std::string sampler = GetSampler(instr.sampler); | ||||
|                 const std::string sampler = | ||||
|                     GetSampler(instr.sampler, instr.tld4.texture_type, false); | ||||
|                 // 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("{"); | ||||
| @ -1840,7 +1912,8 @@ private: | ||||
|                 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | ||||
|                 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); | ||||
|                 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
 | ||||
|                 const std::string sampler = GetSampler(instr.sampler); | ||||
|                 const std::string sampler = | ||||
|                     GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false); | ||||
|                 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; | ||||
|                 const std::string texture = "textureGather(" + sampler + ", coords, " + | ||||
|                                             std::to_string(instr.tld4s.component) + ')'; | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/engines/shader_bytecode.h" | ||||
| 
 | ||||
| namespace OpenGL::GLShader { | ||||
| 
 | ||||
| @ -73,8 +74,9 @@ class SamplerEntry { | ||||
|     using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||||
| 
 | ||||
| public: | ||||
|     SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index) | ||||
|         : offset(offset), stage(stage), sampler_index(index) {} | ||||
|     SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index, | ||||
|                  Tegra::Shader::TextureType type, bool is_array) | ||||
|         : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {} | ||||
| 
 | ||||
|     size_t GetOffset() const { | ||||
|         return offset; | ||||
| @ -89,8 +91,41 @@ public: | ||||
|     } | ||||
| 
 | ||||
|     std::string GetName() const { | ||||
|         return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' + | ||||
|                std::to_string(sampler_index) + ']'; | ||||
|         return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '_' + | ||||
|                std::to_string(sampler_index); | ||||
|     } | ||||
| 
 | ||||
|     std::string GetTypeString() const { | ||||
|         using Tegra::Shader::TextureType; | ||||
|         std::string glsl_type; | ||||
| 
 | ||||
|         switch (type) { | ||||
|         case TextureType::Texture1D: | ||||
|             glsl_type = "sampler1D"; | ||||
|             break; | ||||
|         case TextureType::Texture2D: | ||||
|             glsl_type = "sampler2D"; | ||||
|             break; | ||||
|         case TextureType::Texture3D: | ||||
|             glsl_type = "sampler3D"; | ||||
|             break; | ||||
|         case TextureType::TextureCube: | ||||
|             glsl_type = "samplerCube"; | ||||
|             break; | ||||
|         default: | ||||
|             UNIMPLEMENTED(); | ||||
|         } | ||||
|         if (is_array) | ||||
|             glsl_type += "Array"; | ||||
|         return glsl_type; | ||||
|     } | ||||
| 
 | ||||
|     Tegra::Shader::TextureType GetType() const { | ||||
|         return type; | ||||
|     } | ||||
| 
 | ||||
|     bool IsArray() const { | ||||
|         return is_array; | ||||
|     } | ||||
| 
 | ||||
|     u32 GetHash() const { | ||||
| @ -105,11 +140,14 @@ private: | ||||
|     static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = { | ||||
|         "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs", | ||||
|     }; | ||||
| 
 | ||||
|     /// Offset in TSC memory from which to read the sampler object, as specified by the sampling
 | ||||
|     /// instruction.
 | ||||
|     size_t offset; | ||||
|     Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
 | ||||
|     size_t sampler_index;       ///< Value used to index into the generated GLSL sampler array.
 | ||||
|     Maxwell::ShaderStage stage;      ///< Shader stage where this sampler was used.
 | ||||
|     size_t sampler_index;            ///< Value used to index into the generated GLSL sampler array.
 | ||||
|     Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
 | ||||
|     bool is_array; ///< Whether the texture is being sampled as an array texture or not.
 | ||||
| }; | ||||
| 
 | ||||
| struct ShaderEntries { | ||||
|  | ||||
| @ -200,9 +200,9 @@ void OpenGLState::Apply() const { | ||||
|         const auto& texture_unit = texture_units[i]; | ||||
|         const auto& cur_state_texture_unit = cur_state.texture_units[i]; | ||||
| 
 | ||||
|         if (texture_unit.texture_2d != cur_state_texture_unit.texture_2d) { | ||||
|         if (texture_unit.texture != cur_state_texture_unit.texture) { | ||||
|             glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); | ||||
|             glBindTexture(GL_TEXTURE_2D, texture_unit.texture_2d); | ||||
|             glBindTexture(texture_unit.target, texture_unit.texture); | ||||
|         } | ||||
|         if (texture_unit.sampler != cur_state_texture_unit.sampler) { | ||||
|             glBindSampler(static_cast<GLuint>(i), texture_unit.sampler); | ||||
| @ -214,7 +214,7 @@ void OpenGLState::Apply() const { | ||||
|             texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) { | ||||
|             std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g, | ||||
|                                          texture_unit.swizzle.b, texture_unit.swizzle.a}; | ||||
|             glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); | ||||
|             glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -287,7 +287,7 @@ void OpenGLState::Apply() const { | ||||
| 
 | ||||
| OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { | ||||
|     for (auto& unit : texture_units) { | ||||
|         if (unit.texture_2d == handle) { | ||||
|         if (unit.texture == handle) { | ||||
|             unit.Unbind(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -94,8 +94,9 @@ public: | ||||
| 
 | ||||
|     // 3 texture units - one for each that is used in PICA fragment shader emulation
 | ||||
|     struct TextureUnit { | ||||
|         GLuint texture_2d; // GL_TEXTURE_BINDING_2D
 | ||||
|         GLuint sampler;    // GL_SAMPLER_BINDING
 | ||||
|         GLuint texture; // GL_TEXTURE_BINDING_2D
 | ||||
|         GLuint sampler; // GL_SAMPLER_BINDING
 | ||||
|         GLenum target; | ||||
|         struct { | ||||
|             GLint r; // GL_TEXTURE_SWIZZLE_R
 | ||||
|             GLint g; // GL_TEXTURE_SWIZZLE_G
 | ||||
| @ -104,7 +105,7 @@ public: | ||||
|         } swizzle; | ||||
| 
 | ||||
|         void Unbind() { | ||||
|             texture_2d = 0; | ||||
|             texture = 0; | ||||
|             swizzle.r = GL_RED; | ||||
|             swizzle.g = GL_GREEN; | ||||
|             swizzle.b = GL_BLUE; | ||||
| @ -114,6 +115,7 @@ public: | ||||
|         void Reset() { | ||||
|             Unbind(); | ||||
|             sampler = 0; | ||||
|             target = GL_TEXTURE_2D; | ||||
|         } | ||||
|     }; | ||||
|     std::array<TextureUnit, 32> texture_units; | ||||
|  | ||||
| @ -177,7 +177,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | ||||
|                                        Memory::GetPointer(framebuffer_addr), | ||||
|                                        gl_framebuffer_data.data(), true); | ||||
| 
 | ||||
|         state.texture_units[0].texture_2d = screen_info.texture.resource.handle; | ||||
|         state.texture_units[0].texture = screen_info.texture.resource.handle; | ||||
|         state.Apply(); | ||||
| 
 | ||||
|         glActiveTexture(GL_TEXTURE0); | ||||
| @ -194,7 +194,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | ||||
| 
 | ||||
|         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||
| 
 | ||||
|         state.texture_units[0].texture_2d = 0; | ||||
|         state.texture_units[0].texture = 0; | ||||
|         state.Apply(); | ||||
|     } | ||||
| } | ||||
| @ -205,7 +205,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | ||||
|  */ | ||||
| void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | ||||
|                                                 const TextureInfo& texture) { | ||||
|     state.texture_units[0].texture_2d = texture.resource.handle; | ||||
|     state.texture_units[0].texture = texture.resource.handle; | ||||
|     state.Apply(); | ||||
| 
 | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
| @ -214,7 +214,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | ||||
|     // Update existing texture
 | ||||
|     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); | ||||
| 
 | ||||
|     state.texture_units[0].texture_2d = 0; | ||||
|     state.texture_units[0].texture = 0; | ||||
|     state.Apply(); | ||||
| } | ||||
| 
 | ||||
| @ -260,7 +260,7 @@ void RendererOpenGL::InitOpenGLObjects() { | ||||
|     // Allocation of storage is deferred until the first frame, when we
 | ||||
|     // know the framebuffer size.
 | ||||
| 
 | ||||
|     state.texture_units[0].texture_2d = screen_info.texture.resource.handle; | ||||
|     state.texture_units[0].texture = screen_info.texture.resource.handle; | ||||
|     state.Apply(); | ||||
| 
 | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
| @ -272,7 +272,7 @@ void RendererOpenGL::InitOpenGLObjects() { | ||||
| 
 | ||||
|     screen_info.display_texture = screen_info.texture.resource.handle; | ||||
| 
 | ||||
|     state.texture_units[0].texture_2d = 0; | ||||
|     state.texture_units[0].texture = 0; | ||||
|     state.Apply(); | ||||
| 
 | ||||
|     // Clear screen to black
 | ||||
| @ -305,14 +305,14 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
| 
 | ||||
|     state.texture_units[0].texture_2d = texture.resource.handle; | ||||
|     state.texture_units[0].texture = texture.resource.handle; | ||||
|     state.Apply(); | ||||
| 
 | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
|     glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, | ||||
|                  texture.gl_format, texture.gl_type, nullptr); | ||||
| 
 | ||||
|     state.texture_units[0].texture_2d = 0; | ||||
|     state.texture_units[0].texture = 0; | ||||
|     state.Apply(); | ||||
| } | ||||
| 
 | ||||
| @ -354,14 +354,14 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, | ||||
|         ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v), | ||||
|     }}; | ||||
| 
 | ||||
|     state.texture_units[0].texture_2d = screen_info.display_texture; | ||||
|     state.texture_units[0].texture = screen_info.display_texture; | ||||
|     state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; | ||||
|     state.Apply(); | ||||
| 
 | ||||
|     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); | ||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
| 
 | ||||
|     state.texture_units[0].texture_2d = 0; | ||||
|     state.texture_units[0].texture = 0; | ||||
|     state.Apply(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -170,8 +170,12 @@ struct TICEntry { | ||||
|         BitField<0, 16, u32> width_minus_1; | ||||
|         BitField<23, 4, TextureType> texture_type; | ||||
|     }; | ||||
|     u16 height_minus_1; | ||||
|     INSERT_PADDING_BYTES(10); | ||||
|     union { | ||||
|         BitField<0, 16, u32> height_minus_1; | ||||
|         BitField<16, 15, u32> depth_minus_1; | ||||
|     }; | ||||
| 
 | ||||
|     INSERT_PADDING_BYTES(8); | ||||
| 
 | ||||
|     GPUVAddr Address() const { | ||||
|         return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); | ||||
| @ -192,6 +196,10 @@ struct TICEntry { | ||||
|         return height_minus_1 + 1; | ||||
|     } | ||||
| 
 | ||||
|     u32 Depth() const { | ||||
|         return depth_minus_1 + 1; | ||||
|     } | ||||
| 
 | ||||
|     u32 BlockHeight() const { | ||||
|         ASSERT(header_version == TICHeaderVersion::BlockLinear || | ||||
|                header_version == TICHeaderVersion::BlockLinearColorKey); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei