From ad1810e895b49f47eb5932e4db7ad1ff2719f5ca Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:40:30 -0500 Subject: [PATCH 01/14] Textures: Added a helper function to know if a texture is blocklinear or pitch. --- src/video_core/textures/texture.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 07936f8a3..1145e39f8 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -77,6 +77,11 @@ struct TICEntry { u32 Height() const { return height_minus_1 + 1; } + + bool IsTiled() const { + return header_version == TICHeaderVersion::BlockLinear || + header_version == TICHeaderVersion::BlockLinearColorKey; + } }; static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size"); From f73a280eebbd729f70b6e3b807440f1b6ea3340a Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:41:05 -0500 Subject: [PATCH 02/14] GL: Added functions to convert Maxwell tex filters and wrap modes to OpenGL. --- .../renderer_opengl/maxwell_to_gl.h | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index d847317ac..48ee80125 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -47,4 +47,27 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { return {}; } +inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) { + switch (filter_mode) { + case Tegra::Texture::TextureFilter::Linear: + return GL_LINEAR; + case Tegra::Texture::TextureFilter::Nearest: + return GL_NEAREST; + } + LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode=%u", + static_cast(filter_mode)); + UNREACHABLE(); + return {}; +} + +inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { + switch (wrap_mode) { + case Tegra::Texture::WrapMode::ClampToEdge: + return GL_CLAMP_TO_EDGE; + } + LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode=%u", static_cast(wrap_mode)); + UNREACHABLE(); + return {}; +} + } // namespace MaxwellToGL From 0171ec606bd9d6d66f7c566838aebf525d8e6dee Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:41:47 -0500 Subject: [PATCH 03/14] GL: Rename PicaTexture to MaxwellTexture. --- src/video_core/renderer_opengl/gl_state.cpp | 2 +- src/video_core/renderer_opengl/gl_state.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 1d396728b..6da3a7781 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -194,7 +194,7 @@ void OpenGLState::Apply() const { // Textures for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) { if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { - glActiveTexture(TextureUnits::PicaTexture(i).Enum()); + glActiveTexture(TextureUnits::MaxwellTexture(i).Enum()); glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d); } if (texture_units[i].sampler != cur_state.texture_units[i].sampler) { diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index c1f4efc8c..b18af14bb 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -16,7 +16,7 @@ struct TextureUnit { } }; -constexpr TextureUnit PicaTexture(int unit) { +constexpr TextureUnit MaxwellTexture(int unit) { return TextureUnit{unit}; } From ca96b04a0c524a1a6c3bc6952aab7d059da52c3d Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:42:54 -0500 Subject: [PATCH 04/14] GL: Ported the SamplerInfo struct from citra. --- .../renderer_opengl/gl_rasterizer.cpp | 39 +++++++++++++++++++ .../renderer_opengl/gl_rasterizer.h | 21 +++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 911890f16..307c5bcc1 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -452,6 +452,45 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebu return true; } +void RasterizerOpenGL::SamplerInfo::Create() { + sampler.Create(); + mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear; + wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap; + border_color_r = border_color_g = border_color_b = border_color_a = 0; + + // default is GL_LINEAR_MIPMAP_LINEAR + glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // Other attributes have correct defaults +} + +void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { + + GLuint s = sampler.handle; + + if (mag_filter != config.mag_filter) { + mag_filter = config.mag_filter; + glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, MaxwellToGL::TextureFilterMode(mag_filter)); + } + if (min_filter != config.min_filter) { + min_filter = config.min_filter; + glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, MaxwellToGL::TextureFilterMode(min_filter)); + } + + if (wrap_u != config.wrap_u) { + wrap_u = config.wrap_u; + glSamplerParameteri(s, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(wrap_u)); + } + if (wrap_v != config.wrap_v) { + wrap_v = config.wrap_v; + glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v)); + } + + if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) { + // TODO(Subv): Implement border color + ASSERT(false); + } +} + void RasterizerOpenGL::SetShader() { // TODO(bunnei): The below sets up a static test shader for passing untransformed vertices to // OpenGL for rendering. This should be removed/replaced when we start emulating Maxwell diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index fd53e94cd..ba2113921 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -85,7 +85,26 @@ public: "FSUniformData structure must be less than 16kb as per the OpenGL spec"); private: - struct SamplerInfo {}; + class SamplerInfo { + public: + OGLSampler sampler; + + /// Creates the sampler object, initializing its state so that it's in sync with the + /// SamplerInfo struct. + void Create(); + /// Syncs the sampler object with the config, updating any necessary state. + void SyncWithConfig(const Tegra::Texture::TSCEntry& config); + + private: + Tegra::Texture::TextureFilter mag_filter; + Tegra::Texture::TextureFilter min_filter; + Tegra::Texture::WrapMode wrap_u; + Tegra::Texture::WrapMode wrap_v; + u32 border_color_r; + u32 border_color_g; + u32 border_color_b; + u32 border_color_a; + }; /// Binds the framebuffer color and depth surface void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, From c28ed858750507339153edc94a579b138c74eaca Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:44:03 -0500 Subject: [PATCH 05/14] GL: Create the sampler objects when starting up the GL rasterizer. --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 307c5bcc1..09cd4859b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -66,6 +66,12 @@ RasterizerOpenGL::RasterizerOpenGL() { has_ARB_separate_shader_objects = false; has_ARB_vertex_attrib_binding = false; + // Create sampler objects + for (size_t i = 0; i < texture_samplers.size(); ++i) { + texture_samplers[i].Create(); + state.texture_units[i].sampler = texture_samplers[i].sampler.handle; + } + GLint ext_num; glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num); for (GLint i = 0; i < ext_num; i++) { From b305646c44385833edccb067456e3a0ba4fd3161 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:46:11 -0500 Subject: [PATCH 06/14] RasterizerCache: Remove 3DS-specific pixel formats. We're only left with RGB8 and DXT1 for now. More will be added as they are needed. --- .../renderer_opengl/gl_rasterizer_cache.cpp | 14 +-- .../renderer_opengl/gl_rasterizer_cache.h | 89 +++++++------------ 2 files changed, 32 insertions(+), 71 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 4fd7cdf6a..cfe06391a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -92,19 +92,9 @@ static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) { u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel; u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * gl_bytes_per_pixel; if (morton_to_gl) { - if (format == PixelFormat::D24S8) { - gl_ptr[0] = tile_ptr[3]; - std::memcpy(gl_ptr + 1, tile_ptr, 3); - } else { - std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel); - } + std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel); } else { - if (format == PixelFormat::D24S8) { - std::memcpy(tile_ptr, gl_ptr + 1, 3); - tile_ptr[3] = gl_ptr[0]; - } else { - std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel); - } + std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel); } } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 37b1dae80..06524fc59 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -24,6 +24,7 @@ #include "common/math_util.h" #include "video_core/gpu.h" #include "video_core/renderer_opengl/gl_resource_manager.h" +#include "video_core/textures/texture.h" struct CachedSurface; using Surface = std::shared_ptr; @@ -51,30 +52,8 @@ enum class ScaleMatch { struct SurfaceParams { enum class PixelFormat { - // First 5 formats are shared between textures and color buffers RGBA8 = 0, - RGB8 = 1, - RGB5A1 = 2, - RGB565 = 3, - RGBA4 = 4, - - // Texture-only formats - IA8 = 5, - RG8 = 6, - I8 = 7, - A8 = 8, - IA4 = 9, - I4 = 10, - A4 = 11, - ETC1 = 12, - ETC1A4 = 13, - - // Depth buffer-only formats - D16 = 14, - // gap - D24 = 16, - D24S8 = 17, - + DXT1 = 1, Invalid = 255, }; @@ -88,28 +67,15 @@ struct SurfaceParams { }; static constexpr unsigned int GetFormatBpp(PixelFormat format) { - constexpr std::array bpp_table = { + if (format == PixelFormat::Invalid) + return 0; + + constexpr std::array bpp_table = { 32, // RGBA8 - 24, // RGB8 - 16, // RGB5A1 - 16, // RGB565 - 16, // RGBA4 - 16, // IA8 - 16, // RG8 - 8, // I8 - 8, // A8 - 8, // IA4 - 4, // I4 - 4, // A4 - 4, // ETC1 - 8, // ETC1A4 - 16, // D16 - 0, - 24, // D24 - 32, // D24S8 + 64, // DXT1 }; - assert(static_cast(format) < bpp_table.size()); + ASSERT(static_cast(format) < bpp_table.size()); return bpp_table[static_cast(format)]; } unsigned int GetFormatBpp() const { @@ -134,6 +100,18 @@ struct SurfaceParams { } } + static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format) { + // TODO(Subv): Properly implement this + switch (format) { + case Tegra::Texture::TextureFormat::A8R8G8B8: + return PixelFormat::RGBA8; + case Tegra::Texture::TextureFormat::DXT1: + return PixelFormat::DXT1; + default: + UNREACHABLE(); + } + } + static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { SurfaceType a_type = GetFormatType(pixel_format_a); SurfaceType b_type = GetFormatType(pixel_format_b); @@ -154,22 +132,17 @@ struct SurfaceParams { return false; } - static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) { - if ((unsigned int)pixel_format < 5) { + static SurfaceType GetFormatType(PixelFormat pixel_format) { + if ((unsigned int)pixel_format <= static_cast(PixelFormat::RGBA8)) { return SurfaceType::Color; } - if ((unsigned int)pixel_format < 14) { + if ((unsigned int)pixel_format <= static_cast(PixelFormat::DXT1)) { return SurfaceType::Texture; } - if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) { - return SurfaceType::Depth; - } - - if (pixel_format == PixelFormat::D24S8) { - return SurfaceType::DepthStencil; - } + // TODO(Subv): Implement the other formats + ASSERT(false); return SurfaceType::Invalid; } @@ -265,12 +238,10 @@ struct CachedSurface : SurfaceParams { OGLTexture texture; static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) { - // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type - return format == PixelFormat::Invalid - ? 0 - : (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture) - ? 4 - : SurfaceParams::GetFormatBpp(format) / 8; + if (format == PixelFormat::Invalid) + return 0; + + return SurfaceParams::GetFormatBpp(format) / 8; } std::unique_ptr gl_buffer; @@ -313,7 +284,7 @@ public: bool load_if_create); /// Get a surface based on the texture configuration - Surface GetTextureSurface(const void* config); + Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config); /// Get the color and depth surfaces based on the framebuffer configuration SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, From 73eaef9c05891fe2d1d6af184d3256b9027d1158 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:48:05 -0500 Subject: [PATCH 07/14] GL: Remove remaining references to 3DS-specific pixel formats --- .../renderer_opengl/gl_rasterizer_cache.cpp | 105 ++++-------------- 1 file changed, 22 insertions(+), 83 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index cfe06391a..fe9c76917 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -30,6 +30,7 @@ #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_state.h" +#include "video_core/textures/decoders.h" #include "video_core/utils.h" #include "video_core/video_core.h" @@ -40,36 +41,36 @@ struct FormatTuple { GLint internal_format; GLenum format; GLenum type; + bool compressed; + // How many pixels in the original texture are equivalent to one pixel in the compressed + // texture. + u32 compression_factor; }; -static constexpr std::array fb_format_tuples = {{ - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // RGBA8 - {GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE}, // RGB8 - {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1 - {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 - {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4 +static constexpr std::array fb_format_tuples = {{ + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8 }}; -static constexpr std::array depth_format_tuples = {{ - {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16 - {}, - {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24 - {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8 +static constexpr std::array tex_format_tuples = {{ + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8 + {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1 }}; -static constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; - static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) { const SurfaceType type = SurfaceParams::GetFormatType(pixel_format); if (type == SurfaceType::Color) { ASSERT(static_cast(pixel_format) < fb_format_tuples.size()); return fb_format_tuples[static_cast(pixel_format)]; } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { - size_t tuple_idx = static_cast(pixel_format) - 14; - ASSERT(tuple_idx < depth_format_tuples.size()); - return depth_format_tuples[tuple_idx]; + // TODO(Subv): Implement depth formats + ASSERT_MSG(false, "Unimplemented"); + } else if (type == SurfaceType::Texture) { + ASSERT(static_cast(pixel_format) < tex_format_tuples.size()); + return tex_format_tuples[static_cast(pixel_format)]; } - return tex_tuple; + + UNREACHABLE(); + return {}; } template @@ -112,46 +113,14 @@ static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr Memory::GetPointer(base), gl_buffer, morton_to_gl); } -static constexpr std::array morton_to_gl_fns = { +static constexpr std::array morton_to_gl_fns = { MortonCopy, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, + MortonCopy, }; -static constexpr std::array gl_to_morton_fns = { +static constexpr std::array gl_to_morton_fns = { MortonCopy, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, + MortonCopy, }; // Allocate an uninitialized texture of appropriate size and format for the surface @@ -944,15 +913,6 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc if (expandable != nullptr && expandable->res_scale > target_res_scale) { target_res_scale = expandable->res_scale; } - // Keep res_scale when reinterpreting d24s8 -> rgba8 - if (params.pixel_format == PixelFormat::RGBA8) { - find_params.pixel_format = PixelFormat::D24S8; - expandable = FindMatch( - surface_cache, find_params, match_res_scale); - if (expandable != nullptr && expandable->res_scale > target_res_scale) { - target_res_scale = expandable->res_scale; - } - } } SurfaceParams new_params = params; new_params.res_scale = target_res_scale; @@ -1230,27 +1190,6 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, VAddr addr, continue; } - // D24S8 to RGBA8 - if (surface->pixel_format == PixelFormat::RGBA8) { - params.pixel_format = PixelFormat::D24S8; - Surface reinterpret_surface = - FindMatch(surface_cache, params, ScaleMatch::Ignore, interval); - if (reinterpret_surface != nullptr) { - ASSERT(reinterpret_surface->pixel_format == PixelFormat::D24S8); - - SurfaceInterval convert_interval = params.GetCopyableInterval(reinterpret_surface); - SurfaceParams convert_params = surface->FromInterval(convert_interval); - auto src_rect = reinterpret_surface->GetScaledSubRect(convert_params); - auto dest_rect = surface->GetScaledSubRect(convert_params); - - ConvertD24S8toABGR(reinterpret_surface->texture.handle, src_rect, - surface->texture.handle, dest_rect); - - surface->invalid_regions.erase(convert_interval); - continue; - } - } - // Load data from Switch memory FlushRegion(params.addr, params.size); surface->LoadGLBuffer(params.addr, params.end); From 65ea52394b7b9d8356ee5e9b0e1fce62ce99712f Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:49:05 -0500 Subject: [PATCH 08/14] GLCache: Support uploading compressed textures to the GPU. Compressed texture formats like DXT1, DXT2, DXT3, etc will use this to ease the load on the CPU. --- .../renderer_opengl/gl_rasterizer_cache.cpp | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index fe9c76917..942b12d70 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -134,8 +134,11 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup cur_state.Apply(); glActiveTexture(GL_TEXTURE0); - glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0, - format_tuple.format, format_tuple.type, nullptr); + 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); @@ -565,9 +568,18 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle& rect, GLuint glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(stride)); glActiveTexture(GL_TEXTURE0); - glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast(rect.GetWidth()), - static_cast(rect.GetHeight()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); + if (tuple.compressed) { + glCompressedTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, + static_cast(rect.GetWidth()), + static_cast(rect.GetHeight()), 0, + rect.GetWidth() * rect.GetHeight() * + GetGLBytesPerPixel(pixel_format) / tuple.compression_factor, + &gl_buffer[buffer_offset]); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast(rect.GetWidth()), + static_cast(rect.GetHeight()), tuple.format, tuple.type, + &gl_buffer[buffer_offset]); + } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); From b258403f0d2fb224f32c0ea81ecb6842d783c658 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:51:04 -0500 Subject: [PATCH 09/14] GLCache: Implemented GetTextureSurface. --- .../renderer_opengl/gl_rasterizer_cache.cpp | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 942b12d70..5495cea45 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1018,9 +1018,34 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& return std::make_tuple(surface, surface->GetScaledSubRect(params)); } -Surface RasterizerCacheOpenGL::GetTextureSurface(const void* config) { - UNREACHABLE(); - return {}; +Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { + auto& gpu = Core::System::GetInstance().GPU(); + + SurfaceParams params; + params.addr = gpu.memory_manager->PhysicalToVirtualAddress(config.tic.Address()); + params.width = config.tic.Width(); + params.height = config.tic.Height(); + params.is_tiled = config.tic.IsTiled(); + params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format); + params.UpdateParams(); + + if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0) { + Surface src_surface; + MathUtil::Rectangle rect; + std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); + + params.res_scale = src_surface->res_scale; + Surface tmp_surface = CreateSurface(params); + BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, + tmp_surface->GetScaledRect(), + SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle, + draw_framebuffer.handle); + + remove_surfaces.emplace(tmp_surface); + return tmp_surface; + } + + return GetSurface(params, ScaleMatch::Ignore, true); } SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( From 65faeb9b2a984268f248250424443902cdfa0fbd Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:51:56 -0500 Subject: [PATCH 10/14] GLCache: Specialize the MortonCopy function for the DXT1 texture format. It will now use the UnswizzleTexture function instead of the MortonCopyPixels128, which doesn't seem to work for textures. --- .../renderer_opengl/gl_rasterizer_cache.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 5495cea45..a7f49c18b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -102,7 +102,7 @@ static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) { } template -static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) { +void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) { constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8; constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); @@ -113,6 +113,20 @@ static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr Memory::GetPointer(base), gl_buffer, morton_to_gl); } +template <> +void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, + VAddr start, VAddr end) { + constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(PixelFormat::DXT1) / 8; + constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(PixelFormat::DXT1); + + // 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!"); + auto data = + Tegra::Texture::UnswizzleTexture(base, Tegra::Texture::TextureFormat::DXT1, stride, height); + std::memcpy(gl_buffer, data.data(), data.size()); +} + static constexpr std::array morton_to_gl_fns = { MortonCopy, MortonCopy, From cb3183212d72836a7b6b196fde9896de66db62e6 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:52:43 -0500 Subject: [PATCH 11/14] GL: Bind the textures to the shaders used for drawing. --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 09cd4859b..77e2fd039 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -524,10 +524,10 @@ void main() { in vec2 frag_tex_coord; out vec4 color; -uniform sampler2D color_texture; +uniform sampler2D tex[32]; void main() { - color = vec4(1.0, 0.0, 1.0, 0.0); + color = texture(tex[0], frag_tex_coord); } )"; @@ -548,6 +548,15 @@ void main() { state.draw.shader_program = test_shader.shader.handle; state.Apply(); + for (u32 texture = 0; texture < texture_samplers.size(); ++texture) { + // Set the texture samplers to correspond to different texture units + std::string uniform_name = "tex[" + std::to_string(texture) + "]"; + GLint uniform_tex = glGetUniformLocation(test_shader.shader.handle, uniform_name.c_str()); + if (uniform_tex != -1) { + glUniform1i(uniform_tex, TextureUnits::MaxwellTexture(texture).id); + } + } + if (has_ARB_separate_shader_objects) { state.draw.shader_program = 0; state.Apply(); From b0ca330e149b5f1609980a8167faa0c363066fd2 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 26 Mar 2018 21:54:16 -0500 Subject: [PATCH 12/14] GL: Set up the textures used for each draw call. Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a certain number in OpenGL. We try to only use the minimum amount of host textures by not keeping a 1:1 relation between guest texture ids and host texture ids, ie, guest texture id 8 can be host texture id 0 if it's the only texture used in the guest shader program. This mapping will have to be passed to the shader decompiler so it can rewrite the texture accesses. --- .../renderer_opengl/gl_rasterizer.cpp | 38 ++++++++++++++++++- .../renderer_opengl/gl_rasterizer.h | 3 ++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 77e2fd039..f217a265b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -276,7 +276,9 @@ void RasterizerOpenGL::DrawArrays() { // TODO(bunnei): Sync framebuffer_scale uniform here // TODO(bunnei): Sync scissorbox uniform(s) here - // TODO(bunnei): Sync and bind the texture surfaces + + // Sync and bind the texture surfaces + BindTextures(); // Sync and bind the shader if (shader_dirty) { @@ -380,6 +382,39 @@ void RasterizerOpenGL::DrawArrays() { } } +void RasterizerOpenGL::BindTextures() { + using Regs = Tegra::Engines::Maxwell3D::Regs; + auto maxwell3d = Core::System::GetInstance().GPU().Get3DEngine(); + + // Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a + // certain number in OpenGL. We try to only use the minimum amount of host textures by not + // keeping a 1:1 relation between guest texture ids and host texture ids, ie, guest texture id 8 + // can be host texture id 0 if it's the only texture used in the guest shader program. + u32 host_texture_index = 0; + for (u32 stage = 0; stage < Regs::MaxShaderStage; ++stage) { + ASSERT(host_texture_index < texture_samplers.size()); + const auto textures = maxwell3d.GetStageTextures(static_cast(stage)); + for (unsigned texture_index = 0; texture_index < textures.size(); ++texture_index) { + const auto& texture = textures[texture_index]; + + if (texture.enabled) { + texture_samplers[host_texture_index].SyncWithConfig(texture.tsc); + Surface surface = res_cache.GetTextureSurface(texture); + if (surface != nullptr) { + state.texture_units[host_texture_index].texture_2d = surface->texture.handle; + } else { + // Can occur when texture addr is null or its memory is unmapped/invalid + state.texture_units[texture_index].texture_2d = 0; + } + + ++host_texture_index; + } else { + state.texture_units[texture_index].texture_2d = 0; + } + } + } +} + void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {} void RasterizerOpenGL::FlushAll() { @@ -470,7 +505,6 @@ void RasterizerOpenGL::SamplerInfo::Create() { } void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { - GLuint s = sampler.handle; if (mag_filter != config.mag_filter) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index ba2113921..d868bf421 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -110,6 +110,9 @@ private: void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, bool has_stencil); + /// Binds the required textures to OpenGL before drawing a batch. + void BindTextures(); + /// Syncs the viewport to match the guest state void SyncViewport(const MathUtil::Rectangle& surfaces_rect, u16 res_scale); From dcc27d6dc1153cf0d9ed33a210d46e4111e13e8b Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 29 Mar 2018 13:12:53 -0500 Subject: [PATCH 13/14] GPU: Assert when finding a texture with a format type other than UNORM. --- src/video_core/engines/maxwell_3d.cpp | 2 ++ src/video_core/textures/texture.h | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 124753032..2d7c3152f 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -231,6 +231,8 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { // TODO(Subv): Different data types for separate components are not supported ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); + // TODO(Subv): Only UNORM formats are supported for now. + ASSERT(r_type == Texture::ComponentType::UNORM); return tic_entry; } diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 1145e39f8..c12ed6e1d 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -37,6 +37,16 @@ enum class TICHeaderVersion : u32 { BlockLinearColorKey = 4, }; +enum class ComponentType : u32 { + SNORM = 1, + UNORM = 2, + SINT = 3, + UINT = 4, + SNORM_FORCE_FP16 = 5, + UNORM_FORCE_FP16 = 6, + FLOAT = 7 +}; + union TextureHandle { u32 raw; BitField<0, 20, u32> tic_id; @@ -48,10 +58,10 @@ struct TICEntry { union { u32 raw; BitField<0, 7, TextureFormat> format; - BitField<7, 3, u32> r_type; - BitField<10, 3, u32> g_type; - BitField<13, 3, u32> b_type; - BitField<16, 3, u32> a_type; + BitField<7, 3, ComponentType> r_type; + BitField<10, 3, ComponentType> g_type; + BitField<13, 3, ComponentType> b_type; + BitField<16, 3, ComponentType> a_type; }; u32 address_low; union { From 0b855f1c21aceeda50a115fa80bf06ad6707cf66 Mon Sep 17 00:00:00 2001 From: James Rowe Date: Fri, 6 Apr 2018 22:00:48 -0600 Subject: [PATCH 14/14] Fix clang format issues --- src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index a7f49c18b..5cbafa2e7 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -115,7 +115,7 @@ void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, template <> void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, - VAddr start, VAddr end) { + VAddr start, VAddr end) { constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(PixelFormat::DXT1) / 8; constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(PixelFormat::DXT1);