GS/OGL: Fix sampling from cleared targets

And merge GLLoader into GSDeviceOGL while we're at it.
This commit is contained in:
Stenzek 2023-07-08 17:19:13 +10:00 committed by Connor McLaughlin
parent 4290c16997
commit c2786b91ce
10 changed files with 306 additions and 369 deletions

View File

@ -607,7 +607,6 @@ set(pcsx2GSHeaders
if(USE_OPENGL) if(USE_OPENGL)
list(APPEND pcsx2GSSources list(APPEND pcsx2GSSources
GS/Renderers/OpenGL/GLContext.cpp GS/Renderers/OpenGL/GLContext.cpp
GS/Renderers/OpenGL/GLLoader.cpp
GS/Renderers/OpenGL/GLProgram.cpp GS/Renderers/OpenGL/GLProgram.cpp
GS/Renderers/OpenGL/GLShaderCache.cpp GS/Renderers/OpenGL/GLShaderCache.cpp
GS/Renderers/OpenGL/GLState.cpp GS/Renderers/OpenGL/GLState.cpp
@ -617,7 +616,6 @@ if(USE_OPENGL)
) )
list(APPEND pcsx2GSHeaders list(APPEND pcsx2GSHeaders
GS/Renderers/OpenGL/GLContext.h GS/Renderers/OpenGL/GLContext.h
GS/Renderers/OpenGL/GLLoader.h
GS/Renderers/OpenGL/GLProgram.h GS/Renderers/OpenGL/GLProgram.h
GS/Renderers/OpenGL/GLShaderCache.h GS/Renderers/OpenGL/GLShaderCache.h
GS/Renderers/OpenGL/GLState.h GS/Renderers/OpenGL/GLState.h

View File

@ -1,198 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "GS/Renderers/OpenGL/GLLoader.h"
#include "GS/GS.h"
#include "Host.h"
#include "glad.h"
namespace ReplaceGL
{
void APIENTRY ScissorIndexed(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height)
{
glScissor(left, bottom, width, height);
}
void APIENTRY ViewportIndexedf(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h)
{
glViewport(GLint(x), GLint(y), GLsizei(w), GLsizei(h));
}
void APIENTRY TextureBarrier()
{
}
} // namespace ReplaceGL
namespace Emulate_DSA
{
// Texture entry point
void APIENTRY BindTextureUnit(GLuint unit, GLuint texture)
{
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, texture);
}
void APIENTRY CreateTexture(GLenum target, GLsizei n, GLuint* textures)
{
glGenTextures(1, textures);
}
void APIENTRY TextureStorage(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
{
BindTextureUnit(7, texture);
glTexStorage2D(GL_TEXTURE_2D, levels, internalformat, width, height);
}
void APIENTRY TextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels)
{
BindTextureUnit(7, texture);
glTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset, width, height, format, type, pixels);
}
void APIENTRY CompressedTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)
{
BindTextureUnit(7, texture);
glCompressedTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset, width, height, format, imageSize, data);
}
void APIENTRY GetTexureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels)
{
BindTextureUnit(7, texture);
glGetTexImage(GL_TEXTURE_2D, level, format, type, pixels);
}
void APIENTRY TextureParameteri(GLuint texture, GLenum pname, GLint param)
{
BindTextureUnit(7, texture);
glTexParameteri(GL_TEXTURE_2D, pname, param);
}
void APIENTRY GenerateTextureMipmap(GLuint texture)
{
BindTextureUnit(7, texture);
glGenerateMipmap(GL_TEXTURE_2D);
}
// Misc entry point
void APIENTRY CreateSamplers(GLsizei n, GLuint* samplers)
{
glGenSamplers(n, samplers);
}
// Replace function pointer to emulate DSA behavior
void Init()
{
glBindTextureUnit = BindTextureUnit;
glCreateTextures = CreateTexture;
glTextureStorage2D = TextureStorage;
glTextureSubImage2D = TextureSubImage;
glCompressedTextureSubImage2D = CompressedTextureSubImage;
glGetTextureImage = GetTexureImage;
glTextureParameteri = TextureParameteri;
glGenerateTextureMipmap = GenerateTextureMipmap;
glCreateSamplers = CreateSamplers;
}
} // namespace Emulate_DSA
namespace GLLoader
{
bool vendor_id_amd = false;
bool vendor_id_nvidia = false;
bool vendor_id_intel = false;
bool buggy_pbo = false;
bool disable_download_pbo = false;
static bool check_gl_version()
{
const char* vendor = (const char*)glGetString(GL_VENDOR);
if (strstr(vendor, "Advanced Micro Devices") || strstr(vendor, "ATI Technologies Inc.") || strstr(vendor, "ATI"))
vendor_id_amd = true;
else if (strstr(vendor, "NVIDIA Corporation"))
vendor_id_nvidia = true;
else if (strstr(vendor, "Intel"))
vendor_id_intel = true;
GLint major_gl = 0;
GLint minor_gl = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major_gl);
glGetIntegerv(GL_MINOR_VERSION, &minor_gl);
if (!GLAD_GL_VERSION_3_3 && !GLAD_GL_ES_VERSION_3_1)
{
Host::ReportFormattedErrorAsync("GS", "OpenGL is not supported. Only OpenGL %d.%d\n was found", major_gl, minor_gl);
return false;
}
return true;
}
static bool check_gl_supported_extension()
{
if (!GLAD_GL_ARB_shading_language_420pack)
{
Host::ReportFormattedErrorAsync("GS",
"GL_ARB_shading_language_420pack is not supported, this is required for the OpenGL renderer.");
return false;
}
if (!GLAD_GL_ARB_viewport_array)
{
glScissorIndexed = ReplaceGL::ScissorIndexed;
glViewportIndexedf = ReplaceGL::ViewportIndexedf;
Console.Warning("GL_ARB_viewport_array is not supported! Function pointer will be replaced.");
}
if (!GLAD_GL_ARB_texture_barrier)
{
glTextureBarrier = ReplaceGL::TextureBarrier;
Host::AddOSDMessage("GL_ARB_texture_barrier is not supported, blending will not be accurate.",
Host::OSD_ERROR_DURATION);
}
if (!GLAD_GL_ARB_direct_state_access)
{
Console.Warning("GL_ARB_direct_state_access is not supported, this will reduce performance.");
Emulate_DSA::Init();
}
// Don't use PBOs when we don't have ARB_buffer_storage, orphaning buffers probably ends up worse than just
// using the normal texture update routines and letting the driver take care of it.
buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
if (buggy_pbo)
Console.Warning("Not using PBOs for texture uploads because buffer_storage is unavailable.");
// Give the user the option to disable PBO usage for downloads.
// Most drivers seem to be faster with PBO.
disable_download_pbo = Host::GetBoolSettingValue("EmuCore/GS", "DisableGLDownloadPBO", false);
if (disable_download_pbo)
Console.Warning("Not using PBOs for texture downloads, this may reduce performance.");
return true;
}
bool check_gl_requirements()
{
if (!check_gl_version())
return false;
if (!check_gl_supported_extension())
return false;
return true;
}
} // namespace GLLoader

View File

@ -1,27 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace GLLoader
{
bool check_gl_requirements();
extern bool vendor_id_amd;
extern bool vendor_id_nvidia;
extern bool vendor_id_intel;
extern bool buggy_pbo;
extern bool disable_download_pbo;
} // namespace GLLoader

View File

@ -15,7 +15,6 @@
#pragma once #pragma once
#include "GS/Renderers/OpenGL/GLLoader.h"
#include "GS/GSVector.h" #include "GS/GSVector.h"
#include "glad.h" #include "glad.h"

View File

@ -41,7 +41,98 @@ static constexpr u32 VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
static constexpr u32 FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024; static constexpr u32 FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
static constexpr u32 TEXTURE_UPLOAD_BUFFER_SIZE = 128 * 1024 * 1024; static constexpr u32 TEXTURE_UPLOAD_BUFFER_SIZE = 128 * 1024 * 1024;
static std::unique_ptr<GLStreamBuffer> s_texture_upload_buffer; namespace ReplaceGL
{
static void APIENTRY ScissorIndexed(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height)
{
glScissor(left, bottom, width, height);
}
static void APIENTRY ViewportIndexedf(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h)
{
glViewport(GLint(x), GLint(y), GLsizei(w), GLsizei(h));
}
static void APIENTRY TextureBarrier()
{
}
} // namespace ReplaceGL
namespace Emulate_DSA
{
// Texture entry point
static void APIENTRY BindTextureUnit(GLuint unit, GLuint texture)
{
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, texture);
}
static void APIENTRY CreateTexture(GLenum target, GLsizei n, GLuint* textures)
{
glGenTextures(1, textures);
}
static void APIENTRY TextureStorage(
GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
{
BindTextureUnit(7, texture);
glTexStorage2D(GL_TEXTURE_2D, levels, internalformat, width, height);
}
static void APIENTRY TextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
GLsizei height, GLenum format, GLenum type, const void* pixels)
{
BindTextureUnit(7, texture);
glTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset, width, height, format, type, pixels);
}
static void APIENTRY CompressedTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)
{
BindTextureUnit(7, texture);
glCompressedTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset, width, height, format, imageSize, data);
}
static void APIENTRY GetTexureImage(
GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels)
{
BindTextureUnit(7, texture);
glGetTexImage(GL_TEXTURE_2D, level, format, type, pixels);
}
static void APIENTRY TextureParameteri(GLuint texture, GLenum pname, GLint param)
{
BindTextureUnit(7, texture);
glTexParameteri(GL_TEXTURE_2D, pname, param);
}
static void APIENTRY GenerateTextureMipmap(GLuint texture)
{
BindTextureUnit(7, texture);
glGenerateMipmap(GL_TEXTURE_2D);
}
// Misc entry point
static void APIENTRY CreateSamplers(GLsizei n, GLuint* samplers)
{
glGenSamplers(n, samplers);
}
// Replace function pointer to emulate DSA behavior
static void Init()
{
glBindTextureUnit = BindTextureUnit;
glCreateTextures = CreateTexture;
glTextureStorage2D = TextureStorage;
glTextureSubImage2D = TextureSubImage;
glCompressedTextureSubImage2D = CompressedTextureSubImage;
glGetTextureImage = GetTexureImage;
glTextureParameteri = TextureParameteri;
glGenerateTextureMipmap = GenerateTextureMipmap;
glCreateSamplers = CreateSamplers;
}
} // namespace Emulate_DSA
GSDeviceOGL::GSDeviceOGL() = default; GSDeviceOGL::GSDeviceOGL() = default;
@ -96,7 +187,6 @@ bool GSDeviceOGL::Create()
if (!m_gl_context) if (!m_gl_context)
{ {
Console.Error("Failed to create any GL context"); Console.Error("Failed to create any GL context");
m_gl_context.reset();
return false; return false;
} }
@ -106,15 +196,16 @@ bool GSDeviceOGL::Create()
return false; return false;
} }
// Render a frame as soon as possible to clear out whatever was previously being displayed. bool buggy_pbo;
if (m_window_info.type != WindowInfo::Type::Surfaceless) if (!CheckFeatures(buggy_pbo))
RenderBlankFrame();
if (!GLLoader::check_gl_requirements())
return false; return false;
SetSwapInterval(); SetSwapInterval();
// Render a frame as soon as possible to clear out whatever was previously being displayed.
if (m_window_info.type != WindowInfo::Type::Surfaceless)
RenderBlankFrame();
if (!GSConfig.DisableShaderCache) if (!GSConfig.DisableShaderCache)
{ {
if (!m_shader_cache.Open()) if (!m_shader_cache.Open())
@ -125,68 +216,6 @@ bool GSDeviceOGL::Create()
Console.WriteLn("Not using shader cache."); Console.WriteLn("Not using shader cache.");
} }
// optional features based on context
m_features.broken_point_sampler = GLLoader::vendor_id_amd;
m_features.primitive_id = true;
m_features.framebuffer_fetch = GLAD_GL_EXT_shader_framebuffer_fetch;
if (m_features.framebuffer_fetch && GSConfig.DisableFramebufferFetch)
{
Host::AddOSDMessage("Framebuffer fetch was found but is disabled. This will reduce performance.", Host::OSD_ERROR_DURATION);
m_features.framebuffer_fetch = false;
}
if (GSConfig.OverrideTextureBarriers == 0)
m_features.texture_barrier = m_features.framebuffer_fetch; // Force Disabled
else if (GSConfig.OverrideTextureBarriers == 1)
m_features.texture_barrier = true; // Force Enabled
else
m_features.texture_barrier = m_features.framebuffer_fetch || GLAD_GL_ARB_texture_barrier;
if (!m_features.texture_barrier)
{
Host::AddOSDMessage(
"GL_ARB_texture_barrier is not supported, blending will not be accurate.", Host::OSD_ERROR_DURATION);
}
m_features.provoking_vertex_last = true;
m_features.dxt_textures = GLAD_GL_EXT_texture_compression_s3tc;
m_features.bptc_textures = GLAD_GL_VERSION_4_2 || GLAD_GL_ARB_texture_compression_bptc || GLAD_GL_EXT_texture_compression_bptc;
m_features.prefer_new_textures = false;
m_features.dual_source_blend = !GSConfig.DisableDualSourceBlend;
m_features.clip_control = GLAD_GL_ARB_clip_control;
if (!m_features.clip_control)
Host::AddOSDMessage("GL_ARB_clip_control is not supported, this will cause rendering issues.", Host::OSD_ERROR_DURATION);
m_features.stencil_buffer = true;
m_features.test_and_sample_depth = m_features.texture_barrier;
// NVIDIA GPUs prior to Kepler appear to have broken vertex shader buffer loading.
// Use bindless textures (introduced in Kepler) to differentiate.
const bool buggy_vs_expand =
GLLoader::vendor_id_nvidia && (!GLAD_GL_ARB_bindless_texture && !GLAD_GL_NV_bindless_texture);
if (buggy_vs_expand)
Console.Warning("Disabling vertex shader expand due to broken NVIDIA driver.");
if (GLAD_GL_ARB_shader_storage_buffer_object)
{
GLint max_vertex_ssbos = 0;
glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &max_vertex_ssbos);
DevCon.WriteLn("GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: %d", max_vertex_ssbos);
m_features.vs_expand = (!GSConfig.DisableVertexShaderExpand && !buggy_vs_expand && max_vertex_ssbos > 0 &&
GLAD_GL_ARB_gpu_shader5);
}
if (!m_features.vs_expand)
Console.Warning("Vertex expansion is not supported. This will reduce performance.");
GLint point_range[2] = {};
glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_range);
m_features.point_expand = (point_range[0] <= GSConfig.UpscaleMultiplier && point_range[1] >= GSConfig.UpscaleMultiplier);
m_features.line_expand = false;
Console.WriteLn("Using %s for point expansion, %s for line expansion and %s for sprite expansion.",
m_features.point_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"),
m_features.line_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"),
m_features.vs_expand ? "vertex expanding" : "CPU");
// because of fbo bindings below... // because of fbo bindings below...
GLState::Clear(); GLState::Clear();
@ -512,19 +541,18 @@ bool GSDeviceOGL::Create()
// **************************************************************** // ****************************************************************
// Pbo Pool allocation // Pbo Pool allocation
// **************************************************************** // ****************************************************************
if (!GLLoader::buggy_pbo) if (!buggy_pbo)
{ {
s_texture_upload_buffer = GLStreamBuffer::Create(GL_PIXEL_UNPACK_BUFFER, TEXTURE_UPLOAD_BUFFER_SIZE); m_texture_upload_buffer = GLStreamBuffer::Create(GL_PIXEL_UNPACK_BUFFER, TEXTURE_UPLOAD_BUFFER_SIZE);
if (s_texture_upload_buffer) if (m_texture_upload_buffer)
{ {
// Don't keep it bound, we'll re-bind when we need it. // Don't keep it bound, we'll re-bind when we need it.
// Otherwise non-PBO texture uploads break. Yay for global state. // Otherwise non-PBO texture uploads break. Yay for global state.
s_texture_upload_buffer->Unbind(); m_texture_upload_buffer->Unbind();
} }
else else
{ {
Console.Error("Failed to create texture upload buffer. Using slow path."); Console.Error("Failed to create texture upload buffer. Using slow path.");
GLLoader::buggy_pbo = true;
} }
} }
@ -587,6 +615,176 @@ bool GSDeviceOGL::CreateTextureFX()
return true; return true;
} }
bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo)
{
bool vendor_id_amd = false;
bool vendor_id_nvidia = false;
bool vendor_id_intel = false;
const char* vendor = (const char*)glGetString(GL_VENDOR);
if (std::strstr(vendor, "Advanced Micro Devices") || std::strstr(vendor, "ATI Technologies Inc.") ||
std::strstr(vendor, "ATI"))
{
Console.WriteLn(Color_StrongRed, "OGL: AMD GPU detected.");
vendor_id_amd = true;
}
else if (std::strstr(vendor, "NVIDIA Corporation"))
{
Console.WriteLn(Color_StrongGreen, "OGL: NVIDIA GPU detected.");
vendor_id_nvidia = true;
}
else if (std::strstr(vendor, "Intel"))
{
Console.WriteLn(Color_StrongBlue, "OGL: Intel GPU detected.");
vendor_id_intel = true;
}
GLint major_gl = 0;
GLint minor_gl = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major_gl);
glGetIntegerv(GL_MINOR_VERSION, &minor_gl);
if (!GLAD_GL_VERSION_3_3)
{
Host::ReportErrorAsync(
"GS", fmt::format("OpenGL renderer is not supported. Only OpenGL {}.{}\n was found", major_gl, minor_gl));
return false;
}
// Log extension string for debugging purposes.
Console.WriteLn(fmt::format("GL_VENDOR: {}", reinterpret_cast<const char*>(glGetString(GL_VENDOR))));
Console.WriteLn(fmt::format("GL_VERSION: {}", reinterpret_cast<const char*>(glGetString(GL_VERSION))));
Console.WriteLn(fmt::format("GL_RENDERER: {}", reinterpret_cast<const char*>(glGetString(GL_RENDERER))));
Console.WriteLn(fmt::format(
"GL_SHADING_LANGUAGE_VERSION: {}", reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION))));
std::string extensions = "GL_EXTENSIONS:";
GLint num_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
for (GLint i = 0; i < num_extensions; i++)
{
const char* ext = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
if (ext)
{
extensions += ' ';
extensions.append(ext);
}
}
Console.WriteLn(std::move(extensions));
if (!GLAD_GL_ARB_shading_language_420pack)
{
Host::ReportFormattedErrorAsync(
"GS", "GL_ARB_shading_language_420pack is not supported, this is required for the OpenGL renderer.");
return false;
}
if (!GLAD_GL_VERSION_4_3 && !GLAD_GL_ARB_copy_image && !GLAD_GL_EXT_copy_image)
{
Host::ReportFormattedErrorAsync(
"GS", "GL_ARB_copy_image is not supported, this is required for the OpenGL renderer.");
return false;
}
if (!GLAD_GL_ARB_viewport_array)
{
glScissorIndexed = ReplaceGL::ScissorIndexed;
glViewportIndexedf = ReplaceGL::ViewportIndexedf;
Console.Warning("GL_ARB_viewport_array is not supported! Function pointer will be replaced.");
}
if (!GLAD_GL_ARB_texture_barrier)
{
glTextureBarrier = ReplaceGL::TextureBarrier;
Host::AddOSDMessage(
"GL_ARB_texture_barrier is not supported, blending will not be accurate.", Host::OSD_ERROR_DURATION);
}
if (!GLAD_GL_ARB_direct_state_access)
{
Console.Warning("GL_ARB_direct_state_access is not supported, this will reduce performance.");
Emulate_DSA::Init();
}
// Don't use PBOs when we don't have ARB_buffer_storage, orphaning buffers probably ends up worse than just
// using the normal texture update routines and letting the driver take care of it.
buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
if (buggy_pbo)
Console.Warning("Not using PBOs for texture uploads because buffer_storage is unavailable.");
// Give the user the option to disable PBO usage for downloads.
// Most drivers seem to be faster with PBO.
m_disable_download_pbo = Host::GetBoolSettingValue("EmuCore/GS", "DisableGLDownloadPBO", false);
if (m_disable_download_pbo)
Console.Warning("Not using PBOs for texture downloads, this may reduce performance.");
// optional features based on context
m_features.broken_point_sampler = vendor_id_amd;
m_features.primitive_id = true;
m_features.framebuffer_fetch = GLAD_GL_EXT_shader_framebuffer_fetch;
if (m_features.framebuffer_fetch && GSConfig.DisableFramebufferFetch)
{
Host::AddOSDMessage(
"Framebuffer fetch was found but is disabled. This will reduce performance.", Host::OSD_ERROR_DURATION);
m_features.framebuffer_fetch = false;
}
if (GSConfig.OverrideTextureBarriers == 0)
m_features.texture_barrier = m_features.framebuffer_fetch; // Force Disabled
else if (GSConfig.OverrideTextureBarriers == 1)
m_features.texture_barrier = true; // Force Enabled
else
m_features.texture_barrier = m_features.framebuffer_fetch || GLAD_GL_ARB_texture_barrier;
if (!m_features.texture_barrier)
{
Host::AddOSDMessage(
"GL_ARB_texture_barrier is not supported, blending will not be accurate.", Host::OSD_ERROR_DURATION);
}
m_features.provoking_vertex_last = true;
m_features.dxt_textures = GLAD_GL_EXT_texture_compression_s3tc;
m_features.bptc_textures =
GLAD_GL_VERSION_4_2 || GLAD_GL_ARB_texture_compression_bptc || GLAD_GL_EXT_texture_compression_bptc;
m_features.prefer_new_textures = false;
m_features.dual_source_blend = !GSConfig.DisableDualSourceBlend;
m_features.clip_control = GLAD_GL_ARB_clip_control;
if (!m_features.clip_control)
Host::AddOSDMessage(
"GL_ARB_clip_control is not supported, this will cause rendering issues.", Host::OSD_ERROR_DURATION);
m_features.stencil_buffer = true;
m_features.test_and_sample_depth = m_features.texture_barrier;
// NVIDIA GPUs prior to Kepler appear to have broken vertex shader buffer loading.
// Use bindless textures (introduced in Kepler) to differentiate.
const bool buggy_vs_expand =
vendor_id_nvidia && (!GLAD_GL_ARB_bindless_texture && !GLAD_GL_NV_bindless_texture);
if (buggy_vs_expand)
Console.Warning("Disabling vertex shader expand due to broken NVIDIA driver.");
if (GLAD_GL_ARB_shader_storage_buffer_object)
{
GLint max_vertex_ssbos = 0;
glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &max_vertex_ssbos);
DevCon.WriteLn("GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: %d", max_vertex_ssbos);
m_features.vs_expand = (!GSConfig.DisableVertexShaderExpand && !buggy_vs_expand && max_vertex_ssbos > 0 &&
GLAD_GL_ARB_gpu_shader5);
}
if (!m_features.vs_expand)
Console.Warning("Vertex expansion is not supported. This will reduce performance.");
GLint point_range[2] = {};
glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_range);
m_features.point_expand =
(point_range[0] <= GSConfig.UpscaleMultiplier && point_range[1] >= GSConfig.UpscaleMultiplier);
m_features.line_expand = false;
Console.WriteLn("Using %s for point expansion, %s for line expansion and %s for sprite expansion.",
m_features.point_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"),
m_features.line_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"),
m_features.vs_expand ? "vertex expanding" : "CPU");
return true;
}
void GSDeviceOGL::SetSwapInterval() void GSDeviceOGL::SetSwapInterval()
{ {
const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0)); const int interval = ((m_vsync_mode == VsyncMode::Adaptive) ? -1 : ((m_vsync_mode == VsyncMode::On) ? 1 : 0));
@ -648,7 +846,7 @@ void GSDeviceOGL::DestroyResources()
m_index_stream_buffer.reset(); m_index_stream_buffer.reset();
m_vertex_stream_buffer.reset(); m_vertex_stream_buffer.reset();
s_texture_upload_buffer.reset(); m_texture_upload_buffer.reset();
if (m_expand_ibo) if (m_expand_ibo)
glDeleteBuffers(1, &m_expand_ibo); glDeleteBuffers(1, &m_expand_ibo);
@ -1230,10 +1428,6 @@ void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2
// Copy a sub part of a texture into another // Copy a sub part of a texture into another
void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY)
{ {
ASSERT(sTex && dTex);
if (!(sTex && dTex))
return;
const GLuint& sid = static_cast<GSTextureOGL*>(sTex)->GetID(); const GLuint& sid = static_cast<GSTextureOGL*>(sTex)->GetID();
const GLuint& did = static_cast<GSTextureOGL*>(dTex)->GetID(); const GLuint& did = static_cast<GSTextureOGL*>(dTex)->GetID();
CommitClear(sTex, false); CommitClear(sTex, false);
@ -1253,26 +1447,6 @@ void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
glCopyImageSubDataEXT(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D, glCopyImageSubDataEXT(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
0, destX, destY, 0, r.width(), r.height(), 1); 0, destX, destY, 0, r.width(), r.height(), 1);
} }
else if (GLAD_GL_OES_copy_image)
{
glCopyImageSubDataOES(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
0, destX, destY, 0, r.width(), r.height(), 1);
}
else
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo_write);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sid, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, did, 0);
const int w = r.width(), h = r.height();
glDisable(GL_SCISSOR_TEST);
glBlitFramebuffer(r.x, r.y, r.x + w, r.y + h, destX + r.x, destY + r.y, destX + r.x + w, destY + r.y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glEnable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
} }
void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear) void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear)
@ -1774,23 +1948,14 @@ void GSDeviceOGL::IASetPrimitiveTopology(GLenum topology)
void GSDeviceOGL::PSSetShaderResource(int i, GSTexture* sr) void GSDeviceOGL::PSSetShaderResource(int i, GSTexture* sr)
{ {
ASSERT(i < static_cast<int>(std::size(GLState::tex_unit))); pxAssert(i < static_cast<int>(std::size(GLState::tex_unit)));
// Note: Nvidia debgger doesn't support the id 0 (ie the NULL texture)
if (sr)
{
const GLuint id = static_cast<GSTextureOGL*>(sr)->GetID();
if (GLState::tex_unit[i] != id)
{
GLState::tex_unit[i] = id;
glBindTextureUnit(i, id);
}
}
}
void GSDeviceOGL::PSSetShaderResources(GSTexture* sr0, GSTexture* sr1) const GLuint id = static_cast<GSTextureOGL*>(sr)->GetID();
{ if (GLState::tex_unit[i] != id)
PSSetShaderResource(0, sr0); {
PSSetShaderResource(1, sr1); GLState::tex_unit[i] = id;
glBindTextureUnit(i, id);
}
} }
void GSDeviceOGL::PSSetSamplerState(GLuint ss) void GSDeviceOGL::PSSetSamplerState(GLuint ss)
@ -2216,6 +2381,11 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
GLState::scissor = config.scissor; GLState::scissor = config.scissor;
} }
if (config.tex)
CommitClear(config.tex, true);
if (config.pal)
CommitClear(config.pal, true);
GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize(); GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* primid_texture = nullptr; GSTexture* primid_texture = nullptr;
@ -2308,7 +2478,10 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
} }
IASetPrimitiveTopology(topology); IASetPrimitiveTopology(topology);
PSSetShaderResources(config.tex, config.pal); if (config.tex)
PSSetShaderResource(0, config.tex);
if (config.pal)
PSSetShaderResource(1, config.pal);
if (draw_rt_clone) if (draw_rt_clone)
PSSetShaderResource(2, draw_rt_clone); PSSetShaderResource(2, draw_rt_clone);
else if (config.require_one_barrier || config.require_full_barrier) else if (config.require_one_barrier || config.require_full_barrier)
@ -2584,11 +2757,6 @@ void GSDeviceOGL::DebugMessageCallback(GLenum gl_source, GLenum gl_type, GLuint
} }
} }
GLStreamBuffer* GSDeviceOGL::GetTextureUploadBuffer()
{
return s_texture_upload_buffer.get();
}
void GSDeviceOGL::PushDebugGroup(const char* fmt, ...) void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
{ {
#ifdef ENABLE_OGL_DEBUG #ifdef ENABLE_OGL_DEBUG

View File

@ -16,7 +16,6 @@
#pragma once #pragma once
#include "GS/Renderers/Common/GSDevice.h" #include "GS/Renderers/Common/GSDevice.h"
#include "GS/Renderers/OpenGL/GLLoader.h"
#include "GS/Renderers/OpenGL/GLProgram.h" #include "GS/Renderers/OpenGL/GLProgram.h"
#include "GS/Renderers/OpenGL/GLShaderCache.h" #include "GS/Renderers/OpenGL/GLShaderCache.h"
#include "GS/Renderers/OpenGL/GLState.h" #include "GS/Renderers/OpenGL/GLState.h"
@ -154,10 +153,14 @@ private:
std::unique_ptr<GLContext> m_gl_context; std::unique_ptr<GLContext> m_gl_context;
bool m_disable_download_pbo = false;
GLuint m_fbo = 0; // frame buffer container GLuint m_fbo = 0; // frame buffer container
GLuint m_fbo_read = 0; // frame buffer container only for reading GLuint m_fbo_read = 0; // frame buffer container only for reading
GLuint m_fbo_write = 0; // frame buffer container only for writing GLuint m_fbo_write = 0; // frame buffer container only for writing
std::unique_ptr<GLStreamBuffer> m_texture_upload_buffer;
std::unique_ptr<GLStreamBuffer> m_vertex_stream_buffer; std::unique_ptr<GLStreamBuffer> m_vertex_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_index_stream_buffer; std::unique_ptr<GLStreamBuffer> m_index_stream_buffer;
GLuint m_expand_ibo = 0; GLuint m_expand_ibo = 0;
@ -240,6 +243,8 @@ private:
std::string m_shader_tfx_vgs; std::string m_shader_tfx_vgs;
std::string m_shader_tfx_fs; std::string m_shader_tfx_fs;
bool CheckFeatures(bool& buggy_pbo);
void SetSwapInterval(); void SetSwapInterval();
void DestroyResources(); void DestroyResources();
@ -281,10 +286,10 @@ public:
// Used by OpenGL, so the same calling convention is required. // Used by OpenGL, so the same calling convention is required.
static void APIENTRY DebugMessageCallback(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam); static void APIENTRY DebugMessageCallback(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam);
static GLStreamBuffer* GetTextureUploadBuffer(); __fi bool IsDownloadPBODisabled() const { return m_disable_download_pbo; }
__fi u32 GetFBORead() const { return m_fbo_read; } __fi u32 GetFBORead() const { return m_fbo_read; }
__fi u32 GetFBOWrite() const { return m_fbo_write; } __fi u32 GetFBOWrite() const { return m_fbo_write; }
__fi GLStreamBuffer* GetTextureUploadBuffer() const { return m_texture_upload_buffer.get(); }
void CommitClear(GSTexture* t, bool use_write_fbo); void CommitClear(GSTexture* t, bool use_write_fbo);
RenderAPI GetRenderAPI() const override; RenderAPI GetRenderAPI() const override;
@ -346,7 +351,6 @@ public:
void IASetIndexBuffer(const void* index, size_t count); void IASetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr); void PSSetShaderResource(int i, GSTexture* sr);
void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1);
void PSSetSamplerState(GLuint ss); void PSSetSamplerState(GLuint ss);
void ClearSamplerCache() override; void ClearSamplerCache() override;

View File

@ -214,6 +214,7 @@ bool GSTextureOGL::Update(const GSVector4i& r, const void* data, int pitch, int
// Don't use PBOs for huge texture uploads, let the driver sort it out. // Don't use PBOs for huge texture uploads, let the driver sort it out.
// Otherwise we'll just be syncing, or worse, crashing because the PBO routine above isn't great. // Otherwise we'll just be syncing, or worse, crashing because the PBO routine above isn't great.
GLStreamBuffer* const sb = GSDeviceOGL::GetInstance()->GetTextureUploadBuffer();
if (IsCompressedFormat()) if (IsCompressedFormat())
{ {
const u32 row_length = CalcUploadRowLengthFromPitch(pitch); const u32 row_length = CalcUploadRowLengthFromPitch(pitch);
@ -222,7 +223,7 @@ bool GSTextureOGL::Update(const GSVector4i& r, const void* data, int pitch, int
glCompressedTextureSubImage2D(m_texture_id, layer, r.x, r.y, r.width(), r.height(), m_int_format, upload_size, data); glCompressedTextureSubImage2D(m_texture_id, layer, r.x, r.y, r.width(), r.height(), m_int_format, upload_size, data);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
} }
else if (GLLoader::buggy_pbo || map_size > GSDeviceOGL::GetTextureUploadBuffer()->GetChunkSize()) else if (!sb || map_size > sb->GetChunkSize())
{ {
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch >> m_int_shift); glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch >> m_int_shift);
glTextureSubImage2D(m_texture_id, layer, r.x, r.y, r.width(), r.height(), m_int_format, m_int_type, data); glTextureSubImage2D(m_texture_id, layer, r.x, r.y, r.width(), r.height(), m_int_format, m_int_type, data);
@ -230,8 +231,6 @@ bool GSTextureOGL::Update(const GSVector4i& r, const void* data, int pitch, int
} }
else else
{ {
GLStreamBuffer* const sb = GSDeviceOGL::GetTextureUploadBuffer();
const auto map = sb->Map(TEXTURE_UPLOAD_ALIGNMENT, map_size); const auto map = sb->Map(TEXTURE_UPLOAD_ALIGNMENT, map_size);
StringUtil::StrideMemCpy(map.pointer, preferred_pitch, data, pitch, r.width() << m_int_shift, r.height()); StringUtil::StrideMemCpy(map.pointer, preferred_pitch, data, pitch, r.width() << m_int_shift, r.height());
sb->Unmap(map_size); sb->Unmap(map_size);
@ -271,13 +270,14 @@ bool GSTextureOGL::Map(GSMap& m, const GSVector4i* _r, int layer)
if (m_type == Type::Texture || m_type == Type::RenderTarget) if (m_type == Type::Texture || m_type == Type::RenderTarget)
{ {
const u32 upload_size = CalcUploadSize(r.height(), pitch); const u32 upload_size = CalcUploadSize(r.height(), pitch);
if (GLLoader::buggy_pbo || upload_size > GSDeviceOGL::GetTextureUploadBuffer()->GetChunkSize()) GLStreamBuffer* sb = GSDeviceOGL::GetInstance()->GetTextureUploadBuffer();
if (!sb || upload_size > sb->GetChunkSize())
return false; return false;
GL_PUSH_("Upload Texture %d", m_texture_id); // POP is in Unmap GL_PUSH_("Upload Texture %d", m_texture_id); // POP is in Unmap
g_perfmon.Put(GSPerfMon::TextureUploads, 1); g_perfmon.Put(GSPerfMon::TextureUploads, 1);
const auto map = GSDeviceOGL::GetTextureUploadBuffer()->Map(TEXTURE_UPLOAD_ALIGNMENT, upload_size); const auto map = sb->Map(TEXTURE_UPLOAD_ALIGNMENT, upload_size);
m.bits = static_cast<u8*>(map.pointer); m.bits = static_cast<u8*>(map.pointer);
// Save the area for the unmap // Save the area for the unmap
@ -302,7 +302,7 @@ void GSTextureOGL::Unmap()
const u32 pitch = Common::AlignUpPow2(m_r_w << m_int_shift, TEXTURE_UPLOAD_PITCH_ALIGNMENT); const u32 pitch = Common::AlignUpPow2(m_r_w << m_int_shift, TEXTURE_UPLOAD_PITCH_ALIGNMENT);
const u32 upload_size = pitch * m_r_h; const u32 upload_size = pitch * m_r_h;
GLStreamBuffer* sb = GSDeviceOGL::GetTextureUploadBuffer(); GLStreamBuffer* sb = GSDeviceOGL::GetInstance()->GetTextureUploadBuffer();
sb->Unmap(upload_size); sb->Unmap(upload_size);
sb->Bind(); sb->Bind();
@ -435,7 +435,7 @@ std::unique_ptr<GSDownloadTextureOGL> GSDownloadTextureOGL::Create(u32 width, u3
const u32 buffer_size = GetBufferSize(width, height, format, TEXTURE_UPLOAD_PITCH_ALIGNMENT); const u32 buffer_size = GetBufferSize(width, height, format, TEXTURE_UPLOAD_PITCH_ALIGNMENT);
const bool use_buffer_storage = (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage) && const bool use_buffer_storage = (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage) &&
!GLLoader::disable_download_pbo; !GSDeviceOGL::GetInstance()->IsDownloadPBODisabled();
if (use_buffer_storage) if (use_buffer_storage)
{ {
GLuint buffer_id; GLuint buffer_id;

View File

@ -16,7 +16,8 @@
#pragma once #pragma once
#include "GS/Renderers/Common/GSTexture.h" #include "GS/Renderers/Common/GSTexture.h"
#include "GS/Renderers/OpenGL/GLLoader.h"
#include "glad.h"
class GSTextureOGL final : public GSTexture class GSTextureOGL final : public GSTexture
{ {

View File

@ -267,7 +267,6 @@
<ClCompile Include="Dmac.cpp" /> <ClCompile Include="Dmac.cpp" />
<ClCompile Include="ShiftJisToUnicode.cpp" /> <ClCompile Include="ShiftJisToUnicode.cpp" />
<ClCompile Include="sif2.cpp" /> <ClCompile Include="sif2.cpp" />
<ClCompile Include="GS\Renderers\OpenGL\GLLoader.cpp" />
<ClCompile Include="GS\Renderers\OpenGL\GLState.cpp" /> <ClCompile Include="GS\Renderers\OpenGL\GLState.cpp" />
<ClCompile Include="GS\GS.cpp" /> <ClCompile Include="GS\GS.cpp" />
<ClCompile Include="GS\GSAlignedClass.cpp" /> <ClCompile Include="GS\GSAlignedClass.cpp" />
@ -607,7 +606,6 @@
<ClInclude Include="SPU2\regs.h" /> <ClInclude Include="SPU2\regs.h" />
<ClInclude Include="SPU2\Mixer.h" /> <ClInclude Include="SPU2\Mixer.h" />
<ClInclude Include="SPU2\spu2.h" /> <ClInclude Include="SPU2\spu2.h" />
<ClInclude Include="GS\Renderers\OpenGL\GLLoader.h" />
<ClInclude Include="GS\Renderers\OpenGL\GLState.h" /> <ClInclude Include="GS\Renderers\OpenGL\GLState.h" />
<ClInclude Include="GS\GS.h" /> <ClInclude Include="GS\GS.h" />
<ClInclude Include="GS\GSExtra.h" /> <ClInclude Include="GS\GSExtra.h" />

View File

@ -1034,9 +1034,6 @@
<ClCompile Include="GS\Renderers\DX11\GSTextureFX11.cpp"> <ClCompile Include="GS\Renderers\DX11\GSTextureFX11.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D11</Filter> <Filter>System\Ps2\GS\Renderers\Direct3D11</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="GS\Renderers\OpenGL\GLLoader.cpp">
<Filter>System\Ps2\GS\Renderers\OpenGL</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\OpenGL\GSDeviceOGL.cpp"> <ClCompile Include="GS\Renderers\OpenGL\GSDeviceOGL.cpp">
<Filter>System\Ps2\GS\Renderers\OpenGL</Filter> <Filter>System\Ps2\GS\Renderers\OpenGL</Filter>
</ClCompile> </ClCompile>
@ -1981,9 +1978,6 @@
<ClInclude Include="GS\Renderers\DX11\GSTexture11.h"> <ClInclude Include="GS\Renderers\DX11\GSTexture11.h">
<Filter>System\Ps2\GS\Renderers\Direct3D11</Filter> <Filter>System\Ps2\GS\Renderers\Direct3D11</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="GS\Renderers\OpenGL\GLLoader.h">
<Filter>System\Ps2\GS\Renderers\OpenGL</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\OpenGL\GSDeviceOGL.h"> <ClInclude Include="GS\Renderers\OpenGL\GSDeviceOGL.h">
<Filter>System\Ps2\GS\Renderers\OpenGL</Filter> <Filter>System\Ps2\GS\Renderers\OpenGL</Filter>
</ClInclude> </ClInclude>