diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 0b826fd90..93332b7a2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -61,11 +61,6 @@ RasterizerOpenGL::RasterizerOpenGL() "Shadow might not be able to render because of unsupported OpenGL extensions."); } - if (!GLAD_GL_ARB_texture_barrier) { - LOG_WARNING(Render_OpenGL, - "ARB_texture_barrier not supported. Some games might produce artifacts."); - } - // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 state.clip_distance[0] = true; @@ -643,10 +638,10 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { uniform_block_data.dirty = true; } - bool need_texture_barrier = false; - auto CheckBarrier = [&need_texture_barrier, &color_surface](GLuint handle) { + bool need_duplicate_texture = false; + auto CheckBarrier = [&need_duplicate_texture, &color_surface](GLuint handle) { if (color_surface && color_surface->texture.handle == handle) { - need_texture_barrier = true; + need_duplicate_texture = true; } }; @@ -776,6 +771,43 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { } } + OGLTexture temp_tex; + if (need_duplicate_texture) { + // The game is trying to use a surface as a texture and framebuffer at the same time + // which causes unpredictable behavior on the host. + // Making a copy to sample from eliminates this issue and seems to be fairly cheap. + temp_tex.Create(); + glBindTexture(GL_TEXTURE_2D, temp_tex.handle); + auto [internal_format, format, type] = GetFormatTuple(color_surface->pixel_format); + OGLTexture::Allocate(GL_TEXTURE_2D, color_surface->max_level + 1, internal_format, format, + type, color_surface->GetScaledWidth(), + color_surface->GetScaledHeight()); + 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); + glBindTexture(GL_TEXTURE_2D, state.texture_units[0].texture_2d); + + for (std::size_t level{0}; level <= color_surface->max_level; ++level) { + glCopyImageSubData(color_surface->texture.handle, GL_TEXTURE_2D, level, 0, 0, 0, + temp_tex.handle, GL_TEXTURE_2D, level, 0, 0, 0, + color_surface->GetScaledWidth() >> level, + color_surface->GetScaledHeight() >> level, 1); + } + + for (auto& unit : state.texture_units) { + if (unit.texture_2d == color_surface->texture.handle) { + unit.texture_2d = temp_tex.handle; + } + } + for (auto shadow_unit : {&state.image_shadow_texture_nx, &state.image_shadow_texture_ny, + &state.image_shadow_texture_nz, &state.image_shadow_texture_px, + &state.image_shadow_texture_py, &state.image_shadow_texture_pz}) { + if (*shadow_unit == color_surface->texture.handle) { + *shadow_unit = temp_tex.handle; + } + } + } + // Sync and bind the shader if (shader_dirty) { SetShader(); @@ -850,10 +882,6 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { GL_TEXTURE_UPDATE_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT); } - if (need_texture_barrier && GLAD_GL_ARB_texture_barrier) { - glTextureBarrier(); - } - // Mark framebuffer surfaces as dirty Common::Rectangle draw_rect_unscaled{draw_rect.left / res_scale, draw_rect.top / res_scale, draw_rect.right / res_scale, diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index 836cbb90c..f185b2355 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp @@ -9,6 +9,7 @@ #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_state.h" +#include "video_core/renderer_opengl/gl_vars.h" MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192)); @@ -51,6 +52,61 @@ void OGLTexture::Release() { handle = 0; } +void OGLTexture::Allocate(GLenum target, GLsizei levels, GLenum internalformat, GLenum format, + GLenum type, GLsizei width, GLsizei height, GLsizei depth) { + const bool tex_storage = GLAD_GL_ARB_texture_storage || GLES; + + switch (target) { + case GL_TEXTURE_1D: + case GL_TEXTURE: + if (tex_storage) { + glTexStorage1D(target, levels, internalformat, width); + } else { + for (GLsizei level{0}; level < levels; ++level) { + glTexImage1D(target, level, internalformat, width, 0, format, type, nullptr); + width >>= 1; + } + } + break; + case GL_TEXTURE_2D: + case GL_TEXTURE_1D_ARRAY: + case GL_TEXTURE_RECTANGLE: + case GL_TEXTURE_CUBE_MAP: + if (tex_storage) { + glTexStorage2D(target, levels, internalformat, width, height); + } else { + for (GLsizei level{0}; level < levels; ++level) { + glTexImage2D(target, level, internalformat, width, height, 0, format, type, + nullptr); + width >>= 1; + if (target != GL_TEXTURE_1D_ARRAY) + height >>= 1; + } + } + break; + case GL_TEXTURE_3D: + case GL_TEXTURE_2D_ARRAY: + case GL_TEXTURE_CUBE_MAP_ARRAY: + if (tex_storage) { + glTexStorage3D(target, levels, internalformat, width, height, depth); + } else { + for (GLsizei level{0}; level < levels; ++level) { + glTexImage3D(target, level, internalformat, width, height, depth, 0, format, type, + nullptr); + } + width >>= 1; + height >>= 1; + if (target == GL_TEXTURE_3D) + depth >>= 1; + } + break; + } + + if (!tex_storage) { + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels - 1); + } +} + void OGLSampler::Create() { if (handle != 0) return; diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 55b433858..004e1db01 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -59,6 +59,9 @@ public: /// Deletes the internal OpenGL resource void Release(); + static void Allocate(GLenum target, GLsizei levels, GLenum internalformat, GLenum format, + GLenum type, GLsizei width, GLsizei height = 1, GLsizei depth = 1); + GLuint handle = 0; };