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 #2071 from ReinUsesLisp/dsa-texture
gl_rasterizer: Use DSA for textures and move swizzling to texture state
This commit is contained in:
		
						commit
						40ac058557
					
				| @ -1019,11 +1019,8 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s | |||||||
|         if (surface != nullptr) { |         if (surface != nullptr) { | ||||||
|             unit.texture = |             unit.texture = | ||||||
|                 entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle; |                 entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle; | ||||||
|             unit.target = entry.IsArray() ? surface->TargetLayer() : surface->Target(); |             surface->UpdateSwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source, | ||||||
|             unit.swizzle.r = MaxwellToGL::SwizzleSource(texture.tic.x_source); |                                    texture.tic.w_source); | ||||||
|             unit.swizzle.g = MaxwellToGL::SwizzleSource(texture.tic.y_source); |  | ||||||
|             unit.swizzle.b = MaxwellToGL::SwizzleSource(texture.tic.z_source); |  | ||||||
|             unit.swizzle.a = MaxwellToGL::SwizzleSource(texture.tic.w_source); |  | ||||||
|         } else { |         } else { | ||||||
|             // Can occur when texture addr is null or its memory is unmapped/invalid
 |             // Can occur when texture addr is null or its memory is unmapped/invalid
 | ||||||
|             unit.texture = 0; |             unit.texture = 0; | ||||||
|  | |||||||
| @ -18,7 +18,6 @@ | |||||||
| #include "video_core/morton.h" | #include "video_core/morton.h" | ||||||
| #include "video_core/renderer_opengl/gl_rasterizer.h" | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||||
| #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | ||||||
| #include "video_core/renderer_opengl/gl_state.h" |  | ||||||
| #include "video_core/renderer_opengl/utils.h" | #include "video_core/renderer_opengl/utils.h" | ||||||
| #include "video_core/surface.h" | #include "video_core/surface.h" | ||||||
| #include "video_core/textures/astc.h" | #include "video_core/textures/astc.h" | ||||||
| @ -44,14 +43,14 @@ struct FormatTuple { | |||||||
|     bool compressed; |     bool compressed; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void ApplyTextureDefaults(GLenum target, u32 max_mip_level) { | static void ApplyTextureDefaults(GLuint texture, u32 max_mip_level) { | ||||||
|     glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |     glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||||
|     glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |     glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||||
|     glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |     glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||||
|     glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |     glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||||
|     glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, max_mip_level - 1); |     glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, max_mip_level - 1); | ||||||
|     if (max_mip_level == 1) { |     if (max_mip_level == 1) { | ||||||
|         glTexParameterf(target, GL_TEXTURE_LOD_BIAS, 1000.0); |         glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -529,55 +528,41 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface, | |||||||
| CachedSurface::CachedSurface(const SurfaceParams& params) | CachedSurface::CachedSurface(const SurfaceParams& params) | ||||||
|     : params(params), gl_target(SurfaceTargetToGL(params.target)), |     : params(params), gl_target(SurfaceTargetToGL(params.target)), | ||||||
|       cached_size_in_bytes(params.size_in_bytes) { |       cached_size_in_bytes(params.size_in_bytes) { | ||||||
|     texture.Create(); |     texture.Create(gl_target); | ||||||
|     const auto& rect{params.GetRect()}; |  | ||||||
| 
 | 
 | ||||||
|     // Keep track of previous texture bindings
 |     // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0)
 | ||||||
|     OpenGLState cur_state = OpenGLState::GetCurState(); |     // alternatives. This signals a bug on those functions.
 | ||||||
|     const auto& old_tex = cur_state.texture_units[0]; |     const auto width = static_cast<GLsizei>(params.MipWidth(0)); | ||||||
|     SCOPE_EXIT({ |     const auto height = static_cast<GLsizei>(params.MipHeight(0)); | ||||||
|         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); |     const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type); | ||||||
|     gl_internal_format = format_tuple.internal_format; |     gl_internal_format = format_tuple.internal_format; | ||||||
|     gl_is_compressed = format_tuple.compressed; |  | ||||||
| 
 | 
 | ||||||
|     if (!format_tuple.compressed) { |  | ||||||
|         // Only pre-create the texture for non-compressed textures.
 |  | ||||||
|     switch (params.target) { |     switch (params.target) { | ||||||
|     case SurfaceTarget::Texture1D: |     case SurfaceTarget::Texture1D: | ||||||
|             glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level, |         glTextureStorage1D(texture.handle, params.max_mip_level, format_tuple.internal_format, | ||||||
|                            format_tuple.internal_format, rect.GetWidth()); |                            width); | ||||||
|         break; |         break; | ||||||
|     case SurfaceTarget::Texture2D: |     case SurfaceTarget::Texture2D: | ||||||
|     case SurfaceTarget::TextureCubemap: |     case SurfaceTarget::TextureCubemap: | ||||||
|             glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level, |         glTextureStorage2D(texture.handle, params.max_mip_level, format_tuple.internal_format, | ||||||
|                            format_tuple.internal_format, rect.GetWidth(), rect.GetHeight()); |                            width, height); | ||||||
|         break; |         break; | ||||||
|     case SurfaceTarget::Texture3D: |     case SurfaceTarget::Texture3D: | ||||||
|     case SurfaceTarget::Texture2DArray: |     case SurfaceTarget::Texture2DArray: | ||||||
|     case SurfaceTarget::TextureCubeArray: |     case SurfaceTarget::TextureCubeArray: | ||||||
|             glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level, |         glTextureStorage3D(texture.handle, params.max_mip_level, format_tuple.internal_format, | ||||||
|                            format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(), |                            width, height, params.depth); | ||||||
|                            params.depth); |  | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", |         LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||||||
|                      static_cast<u32>(params.target)); |                      static_cast<u32>(params.target)); | ||||||
|         UNREACHABLE(); |         UNREACHABLE(); | ||||||
|             glTexStorage2D(GL_TEXTURE_2D, params.max_mip_level, format_tuple.internal_format, |         glTextureStorage2D(texture.handle, params.max_mip_level, format_tuple.internal_format, | ||||||
|                            rect.GetWidth(), rect.GetHeight()); |                            width, height); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ApplyTextureDefaults(SurfaceTargetToGL(params.target), params.max_mip_level); |     ApplyTextureDefaults(texture.handle, params.max_mip_level); | ||||||
| 
 | 
 | ||||||
|     OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, params.IdentityString()); |     OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, params.IdentityString()); | ||||||
| 
 | 
 | ||||||
| @ -751,62 +736,49 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, | |||||||
|     const auto& rect{params.GetRect(mip_map)}; |     const auto& rect{params.GetRect(mip_map)}; | ||||||
| 
 | 
 | ||||||
|     // Load data from memory to the surface
 |     // Load data from memory to the surface
 | ||||||
|     const GLint x0 = static_cast<GLint>(rect.left); |     const auto x0 = static_cast<GLint>(rect.left); | ||||||
|     const GLint y0 = static_cast<GLint>(rect.bottom); |     const auto y0 = static_cast<GLint>(rect.bottom); | ||||||
|     std::size_t buffer_offset = |     auto buffer_offset = | ||||||
|         static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) + |         static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) + | ||||||
|                                  static_cast<std::size_t>(x0)) * |                                  static_cast<std::size_t>(x0)) * | ||||||
|         GetBytesPerPixel(params.pixel_format); |         GetBytesPerPixel(params.pixel_format); | ||||||
| 
 | 
 | ||||||
|     const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); |     const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); | ||||||
|     const GLuint target_tex = texture.handle; |  | ||||||
|     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 = target_tex; |  | ||||||
|     cur_state.texture_units[0].target = SurfaceTargetToGL(params.target); |  | ||||||
|     cur_state.Apply(); |  | ||||||
| 
 | 
 | ||||||
|     // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
 |     // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
 | ||||||
|     ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0); |     ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0); | ||||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map))); |     glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map))); | ||||||
| 
 | 
 | ||||||
|     GLsizei image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false)); |     const auto image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false)); | ||||||
|     glActiveTexture(GL_TEXTURE0); |  | ||||||
|     if (tuple.compressed) { |     if (tuple.compressed) { | ||||||
|         switch (params.target) { |         switch (params.target) { | ||||||
|         case SurfaceTarget::Texture2D: |         case SurfaceTarget::Texture2D: | ||||||
|             glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, |             glCompressedTextureSubImage2D( | ||||||
|                                    static_cast<GLsizei>(params.MipWidth(mip_map)), |                 texture.handle, mip_map, 0, 0, static_cast<GLsizei>(params.MipWidth(mip_map)), | ||||||
|                                    static_cast<GLsizei>(params.MipHeight(mip_map)), 0, image_size, |                 static_cast<GLsizei>(params.MipHeight(mip_map)), tuple.internal_format, image_size, | ||||||
|                 &gl_buffer[mip_map][buffer_offset]); |                 &gl_buffer[mip_map][buffer_offset]); | ||||||
|             break; |             break; | ||||||
|         case SurfaceTarget::Texture3D: |         case SurfaceTarget::Texture3D: | ||||||
|             glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, |             glCompressedTextureSubImage3D( | ||||||
|                                    static_cast<GLsizei>(params.MipWidth(mip_map)), |                 texture.handle, mip_map, 0, 0, 0, static_cast<GLsizei>(params.MipWidth(mip_map)), | ||||||
|                 static_cast<GLsizei>(params.MipHeight(mip_map)), |                 static_cast<GLsizei>(params.MipHeight(mip_map)), | ||||||
|                                    static_cast<GLsizei>(params.MipDepth(mip_map)), 0, image_size, |                 static_cast<GLsizei>(params.MipDepth(mip_map)), tuple.internal_format, image_size, | ||||||
|                 &gl_buffer[mip_map][buffer_offset]); |                 &gl_buffer[mip_map][buffer_offset]); | ||||||
|             break; |             break; | ||||||
|         case SurfaceTarget::Texture2DArray: |         case SurfaceTarget::Texture2DArray: | ||||||
|         case SurfaceTarget::TextureCubeArray: |         case SurfaceTarget::TextureCubeArray: | ||||||
|             glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, |             glCompressedTextureSubImage3D( | ||||||
|                                    static_cast<GLsizei>(params.MipWidth(mip_map)), |                 texture.handle, mip_map, 0, 0, 0, static_cast<GLsizei>(params.MipWidth(mip_map)), | ||||||
|                                    static_cast<GLsizei>(params.MipHeight(mip_map)), |                 static_cast<GLsizei>(params.MipHeight(mip_map)), static_cast<GLsizei>(params.depth), | ||||||
|                                    static_cast<GLsizei>(params.depth), 0, image_size, |                 tuple.internal_format, image_size, &gl_buffer[mip_map][buffer_offset]); | ||||||
|                                    &gl_buffer[mip_map][buffer_offset]); |  | ||||||
|             break; |             break; | ||||||
|         case SurfaceTarget::TextureCubemap: { |         case SurfaceTarget::TextureCubemap: { | ||||||
|             GLsizei layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map)); |             const auto layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map)); | ||||||
|             for (std::size_t face = 0; face < params.depth; ++face) { |             for (std::size_t face = 0; face < params.depth; ++face) { | ||||||
|                 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), |                 glCompressedTextureSubImage3D( | ||||||
|                                        mip_map, tuple.internal_format, |                     texture.handle, mip_map, 0, 0, static_cast<GLint>(face), | ||||||
|                     static_cast<GLsizei>(params.MipWidth(mip_map)), |                     static_cast<GLsizei>(params.MipWidth(mip_map)), | ||||||
|                                        static_cast<GLsizei>(params.MipHeight(mip_map)), 0, |                     static_cast<GLsizei>(params.MipHeight(mip_map)), 1, tuple.internal_format, | ||||||
|                     layer_size, &gl_buffer[mip_map][buffer_offset]); |                     layer_size, &gl_buffer[mip_map][buffer_offset]); | ||||||
|                 buffer_offset += layer_size; |                 buffer_offset += layer_size; | ||||||
|             } |             } | ||||||
| @ -816,35 +788,32 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, | |||||||
|             LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", |             LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||||||
|                          static_cast<u32>(params.target)); |                          static_cast<u32>(params.target)); | ||||||
|             UNREACHABLE(); |             UNREACHABLE(); | ||||||
|             glCompressedTexImage2D(GL_TEXTURE_2D, mip_map, tuple.internal_format, |             glCompressedTextureSubImage2D( | ||||||
|                                    static_cast<GLsizei>(params.MipWidth(mip_map)), |                 texture.handle, mip_map, 0, 0, static_cast<GLsizei>(params.MipWidth(mip_map)), | ||||||
|                                    static_cast<GLsizei>(params.MipHeight(mip_map)), 0, |                 static_cast<GLsizei>(params.MipHeight(mip_map)), tuple.internal_format, | ||||||
|                                    static_cast<GLsizei>(params.size_in_bytes_gl), |                 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[mip_map][buffer_offset]); | ||||||
|                                    &gl_buffer[mip_map][buffer_offset]); |  | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
| 
 |  | ||||||
|         switch (params.target) { |         switch (params.target) { | ||||||
|         case SurfaceTarget::Texture1D: |         case SurfaceTarget::Texture1D: | ||||||
|             glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0, |             glTextureSubImage1D(texture.handle, mip_map, x0, static_cast<GLsizei>(rect.GetWidth()), | ||||||
|                             static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type, |                                 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]); | ||||||
|                             &gl_buffer[mip_map][buffer_offset]); |  | ||||||
|             break; |             break; | ||||||
|         case SurfaceTarget::Texture2D: |         case SurfaceTarget::Texture2D: | ||||||
|             glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0, |             glTextureSubImage2D(texture.handle, mip_map, x0, y0, | ||||||
|                                 static_cast<GLsizei>(rect.GetWidth()), |                                 static_cast<GLsizei>(rect.GetWidth()), | ||||||
|                                 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, |                                 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, | ||||||
|                                 &gl_buffer[mip_map][buffer_offset]); |                                 &gl_buffer[mip_map][buffer_offset]); | ||||||
|             break; |             break; | ||||||
|         case SurfaceTarget::Texture3D: |         case SurfaceTarget::Texture3D: | ||||||
|             glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, |             glTextureSubImage3D(texture.handle, mip_map, x0, y0, 0, | ||||||
|                                 static_cast<GLsizei>(rect.GetWidth()), |                                 static_cast<GLsizei>(rect.GetWidth()), | ||||||
|                                 static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map), |                                 static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map), | ||||||
|                                 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]); |                                 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]); | ||||||
|             break; |             break; | ||||||
|         case SurfaceTarget::Texture2DArray: |         case SurfaceTarget::Texture2DArray: | ||||||
|         case SurfaceTarget::TextureCubeArray: |         case SurfaceTarget::TextureCubeArray: | ||||||
|             glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, |             glTextureSubImage3D(texture.handle, mip_map, x0, y0, 0, | ||||||
|                                 static_cast<GLsizei>(rect.GetWidth()), |                                 static_cast<GLsizei>(rect.GetWidth()), | ||||||
|                                 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, |                                 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, | ||||||
|                                 tuple.type, &gl_buffer[mip_map][buffer_offset]); |                                 tuple.type, &gl_buffer[mip_map][buffer_offset]); | ||||||
| @ -852,10 +821,10 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, | |||||||
|         case SurfaceTarget::TextureCubemap: { |         case SurfaceTarget::TextureCubemap: { | ||||||
|             std::size_t start = buffer_offset; |             std::size_t start = buffer_offset; | ||||||
|             for (std::size_t face = 0; face < params.depth; ++face) { |             for (std::size_t face = 0; face < params.depth; ++face) { | ||||||
|                 glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map, |                 glTextureSubImage3D(texture.handle, mip_map, x0, y0, static_cast<GLint>(face), | ||||||
|                                 x0, y0, static_cast<GLsizei>(rect.GetWidth()), |                                     static_cast<GLsizei>(rect.GetWidth()), | ||||||
|                                 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, |                                     static_cast<GLsizei>(rect.GetHeight()), 1, tuple.format, | ||||||
|                                 &gl_buffer[mip_map][buffer_offset]); |                                     tuple.type, &gl_buffer[mip_map][buffer_offset]); | ||||||
|                 buffer_offset += params.LayerSizeGL(mip_map); |                 buffer_offset += params.LayerSizeGL(mip_map); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
| @ -864,7 +833,8 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, | |||||||
|             LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", |             LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | ||||||
|                          static_cast<u32>(params.target)); |                          static_cast<u32>(params.target)); | ||||||
|             UNREACHABLE(); |             UNREACHABLE(); | ||||||
|             glTexSubImage2D(GL_TEXTURE_2D, mip_map, x0, y0, static_cast<GLsizei>(rect.GetWidth()), |             glTextureSubImage2D(texture.handle, mip_map, x0, y0, | ||||||
|  |                                 static_cast<GLsizei>(rect.GetWidth()), | ||||||
|                                 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, |                                 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, | ||||||
|                                 &gl_buffer[mip_map][buffer_offset]); |                                 &gl_buffer[mip_map][buffer_offset]); | ||||||
|         } |         } | ||||||
| @ -876,29 +846,18 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, | |||||||
| void CachedSurface::EnsureTextureView() { | void CachedSurface::EnsureTextureView() { | ||||||
|     if (texture_view.handle != 0) |     if (texture_view.handle != 0) | ||||||
|         return; |         return; | ||||||
|     // Compressed texture are not being created with immutable storage
 |  | ||||||
|     UNIMPLEMENTED_IF(gl_is_compressed); |  | ||||||
| 
 | 
 | ||||||
|     const GLenum target{TargetLayer()}; |     const GLenum target{TargetLayer()}; | ||||||
|     const GLuint num_layers{target == GL_TEXTURE_CUBE_MAP_ARRAY ? 6u : 1u}; |     const GLuint num_layers{target == GL_TEXTURE_CUBE_MAP_ARRAY ? 6u : 1u}; | ||||||
|     constexpr GLuint min_layer = 0; |     constexpr GLuint min_layer = 0; | ||||||
|     constexpr GLuint min_level = 0; |     constexpr GLuint min_level = 0; | ||||||
| 
 | 
 | ||||||
|     texture_view.Create(); |     glGenTextures(1, &texture_view.handle); | ||||||
|     glTextureView(texture_view.handle, target, texture.handle, gl_internal_format, min_level, |     glTextureView(texture_view.handle, target, texture.handle, gl_internal_format, 0, | ||||||
|                   params.max_mip_level, min_layer, num_layers); |                   params.max_mip_level, 0, 1); | ||||||
| 
 |     ApplyTextureDefaults(texture_view.handle, params.max_mip_level); | ||||||
|     OpenGLState cur_state = OpenGLState::GetCurState(); |     glTextureParameteriv(texture_view.handle, GL_TEXTURE_SWIZZLE_RGBA, | ||||||
|     const auto& old_tex = cur_state.texture_units[0]; |                          reinterpret_cast<const GLint*>(swizzle.data())); | ||||||
|     SCOPE_EXIT({ |  | ||||||
|         cur_state.texture_units[0] = old_tex; |  | ||||||
|         cur_state.Apply(); |  | ||||||
|     }); |  | ||||||
|     cur_state.texture_units[0].texture = texture_view.handle; |  | ||||||
|     cur_state.texture_units[0].target = target; |  | ||||||
|     cur_state.Apply(); |  | ||||||
| 
 |  | ||||||
|     ApplyTextureDefaults(target, params.max_mip_level); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64)); | MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64)); | ||||||
| @ -909,6 +868,25 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle | |||||||
|         UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle); |         UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void CachedSurface::UpdateSwizzle(Tegra::Texture::SwizzleSource swizzle_x, | ||||||
|  |                                   Tegra::Texture::SwizzleSource swizzle_y, | ||||||
|  |                                   Tegra::Texture::SwizzleSource swizzle_z, | ||||||
|  |                                   Tegra::Texture::SwizzleSource swizzle_w) { | ||||||
|  |     const GLenum new_x = MaxwellToGL::SwizzleSource(swizzle_x); | ||||||
|  |     const GLenum new_y = MaxwellToGL::SwizzleSource(swizzle_y); | ||||||
|  |     const GLenum new_z = MaxwellToGL::SwizzleSource(swizzle_z); | ||||||
|  |     const GLenum new_w = MaxwellToGL::SwizzleSource(swizzle_w); | ||||||
|  |     if (swizzle[0] == new_x && swizzle[1] == new_y && swizzle[2] == new_z && swizzle[3] == new_w) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     swizzle = {new_x, new_y, new_z, new_w}; | ||||||
|  |     const auto swizzle_data = reinterpret_cast<const GLint*>(swizzle.data()); | ||||||
|  |     glTextureParameteriv(texture.handle, GL_TEXTURE_SWIZZLE_RGBA, swizzle_data); | ||||||
|  |     if (texture_view.handle != 0) { | ||||||
|  |         glTextureParameteriv(texture_view.handle, GL_TEXTURE_SWIZZLE_RGBA, swizzle_data); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| RasterizerCacheOpenGL::RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer) | RasterizerCacheOpenGL::RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer) | ||||||
|     : RasterizerCache{rasterizer} { |     : RasterizerCache{rasterizer} { | ||||||
|     read_framebuffer.Create(); |     read_framebuffer.Create(); | ||||||
|  | |||||||
| @ -382,6 +382,11 @@ public: | |||||||
|     // Upload data in gl_buffer 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 UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); | ||||||
| 
 | 
 | ||||||
|  |     void UpdateSwizzle(Tegra::Texture::SwizzleSource swizzle_x, | ||||||
|  |                        Tegra::Texture::SwizzleSource swizzle_y, | ||||||
|  |                        Tegra::Texture::SwizzleSource swizzle_z, | ||||||
|  |                        Tegra::Texture::SwizzleSource swizzle_w); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle); |     void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle); | ||||||
| 
 | 
 | ||||||
| @ -393,8 +398,8 @@ private: | |||||||
|     SurfaceParams params{}; |     SurfaceParams params{}; | ||||||
|     GLenum gl_target{}; |     GLenum gl_target{}; | ||||||
|     GLenum gl_internal_format{}; |     GLenum gl_internal_format{}; | ||||||
|     bool gl_is_compressed{}; |  | ||||||
|     std::size_t cached_size_in_bytes{}; |     std::size_t cached_size_in_bytes{}; | ||||||
|  |     std::array<GLenum, 4> swizzle{GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { | class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { | ||||||
|  | |||||||
| @ -15,12 +15,12 @@ MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_R | |||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| void OGLTexture::Create() { | void OGLTexture::Create(GLenum target) { | ||||||
|     if (handle != 0) |     if (handle != 0) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     MICROPROFILE_SCOPE(OpenGL_ResourceCreation); |     MICROPROFILE_SCOPE(OpenGL_ResourceCreation); | ||||||
|     glGenTextures(1, &handle); |     glCreateTextures(target, 1, &handle); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OGLTexture::Release() { | void OGLTexture::Release() { | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ public: | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Creates a new internal OpenGL resource and stores the handle
 |     /// Creates a new internal OpenGL resource and stores the handle
 | ||||||
|     void Create(); |     void Create(GLenum target); | ||||||
| 
 | 
 | ||||||
|     /// Deletes the internal OpenGL resource
 |     /// Deletes the internal OpenGL resource
 | ||||||
|     void Release(); |     void Release(); | ||||||
|  | |||||||
| @ -462,29 +462,35 @@ void OpenGLState::ApplyPolygonOffset() const { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OpenGLState::ApplyTextures() const { | void OpenGLState::ApplyTextures() const { | ||||||
|  |     bool has_delta{}; | ||||||
|  |     std::size_t first{}; | ||||||
|  |     std::size_t last{}; | ||||||
|  |     std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> textures; | ||||||
|  | 
 | ||||||
|     for (std::size_t i = 0; i < std::size(texture_units); ++i) { |     for (std::size_t i = 0; i < std::size(texture_units); ++i) { | ||||||
|         const auto& texture_unit = texture_units[i]; |         const auto& texture_unit = texture_units[i]; | ||||||
|         const auto& cur_state_texture_unit = cur_state.texture_units[i]; |         const auto& cur_state_texture_unit = cur_state.texture_units[i]; | ||||||
|  |         textures[i] = texture_unit.texture; | ||||||
| 
 | 
 | ||||||
|         if (texture_unit.texture != cur_state_texture_unit.texture) { |         if (textures[i] != cur_state_texture_unit.texture) { | ||||||
|             glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); |             if (!has_delta) { | ||||||
|             glBindTexture(texture_unit.target, texture_unit.texture); |                 first = i; | ||||||
|  |                 has_delta = true; | ||||||
|             } |             } | ||||||
|         // Update the texture swizzle
 |             last = i; | ||||||
|         if (texture_unit.swizzle.r != cur_state_texture_unit.swizzle.r || |  | ||||||
|             texture_unit.swizzle.g != cur_state_texture_unit.swizzle.g || |  | ||||||
|             texture_unit.swizzle.b != cur_state_texture_unit.swizzle.b || |  | ||||||
|             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(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if (has_delta) { | ||||||
|  |         glBindTextures(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), | ||||||
|  |                        textures.data()); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OpenGLState::ApplySamplers() const { | void OpenGLState::ApplySamplers() const { | ||||||
|     bool has_delta{}; |     bool has_delta{}; | ||||||
|     std::size_t first{}, last{}; |     std::size_t first{}; | ||||||
|  |     std::size_t last{}; | ||||||
|     std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; |     std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; | ||||||
|     for (std::size_t i = 0; i < std::size(samplers); ++i) { |     for (std::size_t i = 0; i < std::size(samplers); ++i) { | ||||||
|         samplers[i] = texture_units[i].sampler; |         samplers[i] = texture_units[i].sampler; | ||||||
|  | |||||||
| @ -126,26 +126,14 @@ public: | |||||||
|     struct TextureUnit { |     struct TextureUnit { | ||||||
|         GLuint texture; // GL_TEXTURE_BINDING_2D
 |         GLuint texture; // GL_TEXTURE_BINDING_2D
 | ||||||
|         GLuint sampler; // GL_SAMPLER_BINDING
 |         GLuint sampler; // GL_SAMPLER_BINDING
 | ||||||
|         GLenum target; |  | ||||||
|         struct { |  | ||||||
|             GLint r; // GL_TEXTURE_SWIZZLE_R
 |  | ||||||
|             GLint g; // GL_TEXTURE_SWIZZLE_G
 |  | ||||||
|             GLint b; // GL_TEXTURE_SWIZZLE_B
 |  | ||||||
|             GLint a; // GL_TEXTURE_SWIZZLE_A
 |  | ||||||
|         } swizzle; |  | ||||||
| 
 | 
 | ||||||
|         void Unbind() { |         void Unbind() { | ||||||
|             texture = 0; |             texture = 0; | ||||||
|             swizzle.r = GL_RED; |  | ||||||
|             swizzle.g = GL_GREEN; |  | ||||||
|             swizzle.b = GL_BLUE; |  | ||||||
|             swizzle.a = GL_ALPHA; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         void Reset() { |         void Reset() { | ||||||
|             Unbind(); |             Unbind(); | ||||||
|             sampler = 0; |             sampler = 0; | ||||||
|             target = GL_TEXTURE_2D; |  | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     std::array<TextureUnit, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_units; |     std::array<TextureUnit, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_units; | ||||||
|  | |||||||
| @ -171,10 +171,6 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | |||||||
|                                        Memory::GetPointer(framebuffer_addr), |                                        Memory::GetPointer(framebuffer_addr), | ||||||
|                                        gl_framebuffer_data.data(), true); |                                        gl_framebuffer_data.data(), true); | ||||||
| 
 | 
 | ||||||
|         state.texture_units[0].texture = screen_info.texture.resource.handle; |  | ||||||
|         state.Apply(); |  | ||||||
| 
 |  | ||||||
|         glActiveTexture(GL_TEXTURE0); |  | ||||||
|         glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); |         glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); | ||||||
| 
 | 
 | ||||||
|         // Update existing texture
 |         // Update existing texture
 | ||||||
| @ -182,14 +178,11 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | |||||||
|         //       they differ from the LCD resolution.
 |         //       they differ from the LCD resolution.
 | ||||||
|         // TODO: Applications could theoretically crash yuzu here by specifying too large
 |         // TODO: Applications could theoretically crash yuzu here by specifying too large
 | ||||||
|         //       framebuffer sizes. We should make sure that this cannot happen.
 |         //       framebuffer sizes. We should make sure that this cannot happen.
 | ||||||
|         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, |         glTextureSubImage2D(screen_info.texture.resource.handle, 0, 0, 0, framebuffer.width, | ||||||
|                         screen_info.texture.gl_format, screen_info.texture.gl_type, |                             framebuffer.height, screen_info.texture.gl_format, | ||||||
|                         gl_framebuffer_data.data()); |                             screen_info.texture.gl_type, gl_framebuffer_data.data()); | ||||||
| 
 | 
 | ||||||
|         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||||
| 
 |  | ||||||
|         state.texture_units[0].texture = 0; |  | ||||||
|         state.Apply(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -199,17 +192,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf | |||||||
|  */ |  */ | ||||||
| void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | ||||||
|                                                 const TextureInfo& texture) { |                                                 const TextureInfo& texture) { | ||||||
|     state.texture_units[0].texture = texture.resource.handle; |     const u8 framebuffer_data[4] = {color_a, color_b, color_g, color_r}; | ||||||
|     state.Apply(); |     glClearTexImage(texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); | ||||||
| 
 |  | ||||||
|     glActiveTexture(GL_TEXTURE0); |  | ||||||
|     u8 framebuffer_data[4] = {color_a, color_b, color_g, color_r}; |  | ||||||
| 
 |  | ||||||
|     // 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 = 0; |  | ||||||
|     state.Apply(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -249,26 +233,13 @@ void RendererOpenGL::InitOpenGLObjects() { | |||||||
|                               sizeof(ScreenRectVertex)); |                               sizeof(ScreenRectVertex)); | ||||||
| 
 | 
 | ||||||
|     // Allocate textures for the screen
 |     // Allocate textures for the screen
 | ||||||
|     screen_info.texture.resource.Create(); |     screen_info.texture.resource.Create(GL_TEXTURE_2D); | ||||||
| 
 | 
 | ||||||
|     // Allocation of storage is deferred until the first frame, when we
 |     const GLuint texture = screen_info.texture.resource.handle; | ||||||
|     // know the framebuffer size.
 |     glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); | ||||||
| 
 |  | ||||||
|     state.texture_units[0].texture = screen_info.texture.resource.handle; |  | ||||||
|     state.Apply(); |  | ||||||
| 
 |  | ||||||
|     glActiveTexture(GL_TEXTURE0); |  | ||||||
|     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); |  | ||||||
|     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |  | ||||||
|     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_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); |  | ||||||
| 
 | 
 | ||||||
|     screen_info.display_texture = screen_info.texture.resource.handle; |     screen_info.display_texture = screen_info.texture.resource.handle; | ||||||
| 
 | 
 | ||||||
|     state.texture_units[0].texture = 0; |  | ||||||
|     state.Apply(); |  | ||||||
| 
 |  | ||||||
|     // Clear screen to black
 |     // Clear screen to black
 | ||||||
|     LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); |     LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); | ||||||
| } | } | ||||||
| @ -284,20 +255,19 @@ void RendererOpenGL::CreateRasterizer() { | |||||||
| 
 | 
 | ||||||
| void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | ||||||
|                                                  const Tegra::FramebufferConfig& framebuffer) { |                                                  const Tegra::FramebufferConfig& framebuffer) { | ||||||
| 
 |  | ||||||
|     texture.width = framebuffer.width; |     texture.width = framebuffer.width; | ||||||
|     texture.height = framebuffer.height; |     texture.height = framebuffer.height; | ||||||
| 
 | 
 | ||||||
|     GLint internal_format; |     GLint internal_format; | ||||||
|     switch (framebuffer.pixel_format) { |     switch (framebuffer.pixel_format) { | ||||||
|     case Tegra::FramebufferConfig::PixelFormat::ABGR8: |     case Tegra::FramebufferConfig::PixelFormat::ABGR8: | ||||||
|         internal_format = GL_RGBA; |         internal_format = GL_RGBA8; | ||||||
|         texture.gl_format = GL_RGBA; |         texture.gl_format = GL_RGBA; | ||||||
|         texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; |         texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||||||
|         gl_framebuffer_data.resize(texture.width * texture.height * 4); |         gl_framebuffer_data.resize(texture.width * texture.height * 4); | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         internal_format = GL_RGBA; |         internal_format = GL_RGBA8; | ||||||
|         texture.gl_format = GL_RGBA; |         texture.gl_format = GL_RGBA; | ||||||
|         texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; |         texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||||||
|         gl_framebuffer_data.resize(texture.width * texture.height * 4); |         gl_framebuffer_data.resize(texture.width * texture.height * 4); | ||||||
| @ -306,15 +276,9 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | |||||||
|         UNREACHABLE(); |         UNREACHABLE(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     state.texture_units[0].texture = texture.resource.handle; |     texture.resource.Release(); | ||||||
|     state.Apply(); |     texture.resource.Create(GL_TEXTURE_2D); | ||||||
| 
 |     glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height); | ||||||
|     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 = 0; |  | ||||||
|     state.Apply(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, | void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, | ||||||
| @ -356,7 +320,6 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, | |||||||
|     }}; |     }}; | ||||||
| 
 | 
 | ||||||
|     state.texture_units[0].texture = 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}; |  | ||||||
|     // Workaround brigthness problems in SMO by enabling sRGB in the final output
 |     // Workaround brigthness problems in SMO by enabling sRGB in the final output
 | ||||||
|     // if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987
 |     // if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987
 | ||||||
|     state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed(); |     state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed(); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei