summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/OGL/Render.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoBackends/OGL/Render.cpp')
-rw-r--r--Source/Core/VideoBackends/OGL/Render.cpp780
1 files changed, 260 insertions, 520 deletions
diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp
index b72941583e..baeb9f877c 100644
--- a/Source/Core/VideoBackends/OGL/Render.cpp
+++ b/Source/Core/VideoBackends/OGL/Render.cpp
@@ -27,22 +27,21 @@
#include "Core/Core.h"
#include "VideoBackends/OGL/BoundingBox.h"
-#include "VideoBackends/OGL/FramebufferManager.h"
#include "VideoBackends/OGL/OGLPipeline.h"
#include "VideoBackends/OGL/OGLShader.h"
#include "VideoBackends/OGL/OGLTexture.h"
-#include "VideoBackends/OGL/PostProcessing.h"
#include "VideoBackends/OGL/ProgramShaderCache.h"
#include "VideoBackends/OGL/SamplerCache.h"
#include "VideoBackends/OGL/StreamBuffer.h"
-#include "VideoBackends/OGL/TextureCache.h"
#include "VideoBackends/OGL/VertexManager.h"
#include "VideoCommon/BPFunctions.h"
#include "VideoCommon/DriverDetails.h"
+#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/IndexGenerator.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PixelEngine.h"
+#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/VertexShaderManager.h"
@@ -54,22 +53,6 @@ namespace OGL
{
VideoConfig g_ogl_config;
-// Declarations and definitions
-// ----------------------------
-
-// 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA.
-static int s_MSAASamples = 1;
-
-// EFB cache related
-static const u32 EFB_CACHE_RECT_SIZE = 64; // Cache 64x64 blocks.
-static const u32 EFB_CACHE_WIDTH =
- (EFB_WIDTH + EFB_CACHE_RECT_SIZE - 1) / EFB_CACHE_RECT_SIZE; // round up
-static const u32 EFB_CACHE_HEIGHT = (EFB_HEIGHT + EFB_CACHE_RECT_SIZE - 1) / EFB_CACHE_RECT_SIZE;
-static bool s_efbCacheValid[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT];
-static bool s_efbCacheIsCleared = false;
-static std::vector<u32>
- s_efbCache[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT]; // 2 for PeekZ and PeekColor
-
static void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const char* message, const void* userParam)
{
@@ -356,6 +339,16 @@ Renderer::Renderer(std::unique_ptr<GLContext> main_gl_context, float backbuffer_
m_current_depth_state(RenderState::GetInvalidDepthState()),
m_current_blend_state(RenderState::GetInvalidBlendingState())
{
+ // Create the window framebuffer.
+ if (!m_main_gl_context->IsHeadless())
+ {
+ m_system_framebuffer = std::make_unique<OGLFramebuffer>(
+ nullptr, nullptr, AbstractTextureFormat::RGBA8, AbstractTextureFormat::Undefined,
+ std::max(m_main_gl_context->GetBackBufferWidth(), 1u),
+ std::max(m_main_gl_context->GetBackBufferHeight(), 1u), 1, 1, 0);
+ m_current_framebuffer = m_system_framebuffer.get();
+ }
+
bool bSuccess = true;
g_ogl_config.gl_vendor = (const char*)glGetString(GL_VENDOR);
@@ -437,9 +430,9 @@ Renderer::Renderer(std::unique_ptr<GLContext> main_gl_context, float backbuffer_
g_Config.backend_info.bSupportsPrimitiveRestart =
!DriverDetails::HasBug(DriverDetails::BUG_PRIMITIVE_RESTART) &&
((GLExtensions::Version() >= 310) || GLExtensions::Supports("GL_NV_primitive_restart"));
- g_Config.backend_info.bSupportsBBox = true;
g_Config.backend_info.bSupportsFragmentStoresAndAtomics =
GLExtensions::Supports("GL_ARB_shader_storage_buffer_object");
+ g_Config.backend_info.bSupportsBBox = g_Config.backend_info.bSupportsFragmentStoresAndAtomics;
g_Config.backend_info.bSupportsGSInstancing = GLExtensions::Supports("GL_ARB_gpu_shader5");
g_Config.backend_info.bSupportsSSAA = GLExtensions::Supports("GL_ARB_gpu_shader5") &&
GLExtensions::Supports("GL_ARB_sample_shading");
@@ -692,9 +685,13 @@ Renderer::Renderer(std::unique_ptr<GLContext> main_gl_context, float backbuffer_
glDebugMessageCallbackARB(ErrorCallback, nullptr);
}
if (LogManager::GetInstance()->IsEnabled(LogTypes::HOST_GPU, LogTypes::LERROR))
+ {
glEnable(GL_DEBUG_OUTPUT);
+ }
else
+ {
glDisable(GL_DEBUG_OUTPUT);
+ }
}
int samples;
@@ -753,23 +750,9 @@ Renderer::Renderer(std::unique_ptr<GLContext> main_gl_context, float backbuffer_
if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive);
- // Because of the fixed framebuffer size we need to disable the resolution
- // options while running
-
- // The stencil is used for bounding box emulation when SSBOs are not available
- glDisable(GL_STENCIL_TEST);
- glStencilFunc(GL_ALWAYS, 1, 0xFF);
- glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
-
- // Reset The Current Viewport
- glViewport(0, 0, GetTargetWidth(), GetTargetHeight());
if (g_ActiveConfig.backend_info.bSupportsClipControl)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClearDepthf(1.0f);
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LEQUAL);
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
{
glEnable(GL_CLIP_DISTANCE0);
@@ -779,18 +762,14 @@ Renderer::Renderer(std::unique_ptr<GLContext> main_gl_context, float backbuffer_
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
- glEnable(GL_SCISSOR_TEST);
- glScissor(0, 0, GetTargetWidth(), GetTargetHeight());
- glBlendFunc(GL_ONE, GL_ONE);
- glBlendColor(0, 0, 0, 0.5f);
- glClearDepthf(1.0f);
+ glGenFramebuffers(1, &m_shared_read_framebuffer);
+ glGenFramebuffers(1, &m_shared_draw_framebuffer);
if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart)
GLUtil::EnablePrimitiveRestart(m_main_gl_context.get());
IndexGenerator::Init();
UpdateActiveConfig();
- ClearEFBCache();
}
Renderer::~Renderer() = default;
@@ -805,24 +784,15 @@ bool Renderer::Initialize()
if (!::Renderer::Initialize())
return false;
- // Initialize the FramebufferManager
- g_framebuffer_manager = std::make_unique<FramebufferManager>(
- m_target_width, m_target_height, s_MSAASamples, BoundingBox::NeedsStencilBuffer());
- m_current_framebuffer_width = m_target_width;
- m_current_framebuffer_height = m_target_height;
-
- m_post_processor = std::make_unique<OpenGLPostProcessing>();
return true;
}
void Renderer::Shutdown()
{
::Renderer::Shutdown();
- g_framebuffer_manager.reset();
-
- UpdateActiveConfig();
- m_post_processor.reset();
+ glDeleteFramebuffers(1, &m_shared_draw_framebuffer);
+ glDeleteFramebuffers(1, &m_shared_read_framebuffer);
}
std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
@@ -836,12 +806,11 @@ std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTe
return OGLStagingTexture::Create(type, config);
}
-std::unique_ptr<AbstractFramebuffer>
-Renderer::CreateFramebuffer(const AbstractTexture* color_attachment,
- const AbstractTexture* depth_attachment)
+std::unique_ptr<AbstractFramebuffer> Renderer::CreateFramebuffer(AbstractTexture* color_attachment,
+ AbstractTexture* depth_attachment)
{
- return OGLFramebuffer::Create(static_cast<const OGLTexture*>(color_attachment),
- static_cast<const OGLTexture*>(depth_attachment));
+ return OGLFramebuffer::Create(static_cast<OGLTexture*>(color_attachment),
+ static_cast<OGLTexture*>(depth_attachment));
}
std::unique_ptr<AbstractShader> Renderer::CreateShaderFromSource(ShaderStage stage,
@@ -861,231 +830,9 @@ std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelin
return OGLPipeline::Create(config);
}
-TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
-{
- TargetRectangle result;
- result.left = EFBToScaledX(rc.left);
- result.top = EFBToScaledY(EFB_HEIGHT - rc.top);
- result.right = EFBToScaledX(rc.right);
- result.bottom = EFBToScaledY(EFB_HEIGHT - rc.bottom);
- return result;
-}
-
void Renderer::SetScissorRect(const MathUtil::Rectangle<int>& rc)
{
- glScissor(rc.left, rc.bottom, rc.GetWidth(), rc.GetHeight());
-}
-
-void ClearEFBCache()
-{
- if (!s_efbCacheIsCleared)
- {
- s_efbCacheIsCleared = true;
- memset(s_efbCacheValid, 0, sizeof(s_efbCacheValid));
- }
-}
-
-void Renderer::UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
- const TargetRectangle& targetPixelRc, const void* data)
-{
- const u32 cacheType = (type == EFBAccessType::PeekZ ? 0 : 1);
-
- if (s_efbCache[cacheType][cacheRectIdx].empty())
- s_efbCache[cacheType][cacheRectIdx].resize(EFB_CACHE_RECT_SIZE * EFB_CACHE_RECT_SIZE);
-
- u32 targetPixelRcWidth = targetPixelRc.right - targetPixelRc.left;
- u32 efbPixelRcHeight = efbPixelRc.bottom - efbPixelRc.top;
- u32 efbPixelRcWidth = efbPixelRc.right - efbPixelRc.left;
-
- for (u32 yCache = 0; yCache < efbPixelRcHeight; ++yCache)
- {
- u32 yEFB = efbPixelRc.top + yCache;
- u32 yPixel = (EFBToScaledY(EFB_HEIGHT - yEFB) + EFBToScaledY(EFB_HEIGHT - yEFB - 1)) / 2;
- u32 yData = yPixel - targetPixelRc.bottom;
-
- for (u32 xCache = 0; xCache < efbPixelRcWidth; ++xCache)
- {
- u32 xEFB = efbPixelRc.left + xCache;
- u32 xPixel = (EFBToScaledX(xEFB) + EFBToScaledX(xEFB + 1)) / 2;
- u32 xData = xPixel - targetPixelRc.left;
- u32 value;
- if (type == EFBAccessType::PeekZ)
- {
- float* ptr = (float*)data;
- value = MathUtil::Clamp<u32>((u32)(ptr[yData * targetPixelRcWidth + xData] * 16777216.0f),
- 0, 0xFFFFFF);
- }
- else
- {
- u32* ptr = (u32*)data;
- value = ptr[yData * targetPixelRcWidth + xData];
- }
- s_efbCache[cacheType][cacheRectIdx][yCache * EFB_CACHE_RECT_SIZE + xCache] = value;
- }
- }
-
- s_efbCacheValid[cacheType][cacheRectIdx] = true;
- s_efbCacheIsCleared = false;
-}
-
-// This function allows the CPU to directly access the EFB.
-// There are EFB peeks (which will read the color or depth of a pixel)
-// and EFB pokes (which will change the color or depth of a pixel).
-//
-// The behavior of EFB peeks can only be modified by:
-// - GX_PokeAlphaRead
-// The behavior of EFB pokes can be modified by:
-// - GX_PokeAlphaMode (TODO)
-// - GX_PokeAlphaUpdate (TODO)
-// - GX_PokeBlendMode (TODO)
-// - GX_PokeColorUpdate (TODO)
-// - GX_PokeDither (TODO)
-// - GX_PokeDstAlpha (TODO)
-// - GX_PokeZMode (TODO)
-u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
-{
- u32 cacheRectIdx = (y / EFB_CACHE_RECT_SIZE) * EFB_CACHE_WIDTH + (x / EFB_CACHE_RECT_SIZE);
-
- EFBRectangle efbPixelRc;
-
- if (type == EFBAccessType::PeekColor || type == EFBAccessType::PeekZ)
- {
- // Get the rectangular target region containing the EFB pixel
- efbPixelRc.left = (x / EFB_CACHE_RECT_SIZE) * EFB_CACHE_RECT_SIZE;
- efbPixelRc.top = (y / EFB_CACHE_RECT_SIZE) * EFB_CACHE_RECT_SIZE;
- efbPixelRc.right = std::min(efbPixelRc.left + EFB_CACHE_RECT_SIZE, (u32)EFB_WIDTH);
- efbPixelRc.bottom = std::min(efbPixelRc.top + EFB_CACHE_RECT_SIZE, (u32)EFB_HEIGHT);
- }
- else
- {
- efbPixelRc.left = x;
- efbPixelRc.top = y;
- efbPixelRc.right = x + 1;
- efbPixelRc.bottom = y + 1;
- }
-
- TargetRectangle targetPixelRc = ConvertEFBRectangle(efbPixelRc);
- u32 targetPixelRcWidth = targetPixelRc.right - targetPixelRc.left;
- u32 targetPixelRcHeight = targetPixelRc.top - targetPixelRc.bottom;
-
- // TODO (FIX) : currently, AA path is broken/offset and doesn't return the correct pixel
- switch (type)
- {
- case EFBAccessType::PeekZ:
- {
- if (!s_efbCacheValid[0][cacheRectIdx])
- {
- if (s_MSAASamples > 1)
- {
- ResetAPIState();
-
- // Resolve our rectangle.
- FramebufferManager::GetEFBDepthTexture(efbPixelRc);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetResolvedFramebuffer());
- }
-
- std::unique_ptr<float[]> depthMap(new float[targetPixelRcWidth * targetPixelRcHeight]);
-
- glReadPixels(targetPixelRc.left, targetPixelRc.bottom, targetPixelRcWidth,
- targetPixelRcHeight, GL_DEPTH_COMPONENT, GL_FLOAT, depthMap.get());
-
- UpdateEFBCache(type, cacheRectIdx, efbPixelRc, targetPixelRc, depthMap.get());
-
- if (s_MSAASamples > 1)
- RestoreAPIState();
- }
-
- u32 xRect = x % EFB_CACHE_RECT_SIZE;
- u32 yRect = y % EFB_CACHE_RECT_SIZE;
- u32 z = s_efbCache[0][cacheRectIdx][yRect * EFB_CACHE_RECT_SIZE + xRect];
-
- // if Z is in 16 bit format you must return a 16 bit integer
- if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
- z = z >> 8;
-
- return z;
- }
-
- case EFBAccessType::PeekColor: // GXPeekARGB
- {
- // Although it may sound strange, this really is A8R8G8B8 and not RGBA or 24-bit...
-
- // Tested in Killer 7, the first 8bits represent the alpha value which is used to
- // determine if we're aiming at an enemy (0x80 / 0x88) or not (0x70)
- // Wind Waker is also using it for the pictograph to determine the color of each pixel
- if (!s_efbCacheValid[1][cacheRectIdx])
- {
- if (s_MSAASamples > 1)
- {
- ResetAPIState();
-
- // Resolve our rectangle.
- FramebufferManager::GetEFBColorTexture(efbPixelRc);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetResolvedFramebuffer());
- }
-
- std::unique_ptr<u32[]> colorMap(new u32[targetPixelRcWidth * targetPixelRcHeight]);
-
- if (IsGLES())
- // XXX: Swap colours
- glReadPixels(targetPixelRc.left, targetPixelRc.bottom, targetPixelRcWidth,
- targetPixelRcHeight, GL_RGBA, GL_UNSIGNED_BYTE, colorMap.get());
- else
- glReadPixels(targetPixelRc.left, targetPixelRc.bottom, targetPixelRcWidth,
- targetPixelRcHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, colorMap.get());
-
- UpdateEFBCache(type, cacheRectIdx, efbPixelRc, targetPixelRc, colorMap.get());
-
- if (s_MSAASamples > 1)
- RestoreAPIState();
- }
-
- u32 xRect = x % EFB_CACHE_RECT_SIZE;
- u32 yRect = y % EFB_CACHE_RECT_SIZE;
- u32 color = s_efbCache[1][cacheRectIdx][yRect * EFB_CACHE_RECT_SIZE + xRect];
-
- // check what to do with the alpha channel (GX_PokeAlphaRead)
- PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode();
-
- if (bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24)
- {
- color = RGBA8ToRGBA6ToRGBA8(color);
- }
- else if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
- {
- color = RGBA8ToRGB565ToRGBA8(color);
- }
- if (bpmem.zcontrol.pixel_format != PEControl::RGBA6_Z24)
- {
- color |= 0xFF000000;
- }
- if (alpha_read_mode.ReadMode == 2)
- {
- // GX_READ_NONE
- return color;
- }
- else if (alpha_read_mode.ReadMode == 1)
- {
- // GX_READ_FF
- return (color | 0xFF000000);
- }
- else /*if(alpha_read_mode.ReadMode == 0)*/
- {
- // GX_READ_00
- return (color & 0x00FFFFFF);
- }
- }
-
- default:
- break;
- }
-
- return 0;
-}
-
-void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points)
-{
- FramebufferManager::PokeEFB(type, points, num_points);
+ glScissor(rc.left, rc.top, rc.GetWidth(), rc.GetHeight());
}
u16 Renderer::BBoxRead(int index)
@@ -1138,9 +885,6 @@ void Renderer::BBoxWrite(int index, u16 _value)
void Renderer::SetViewport(float x, float y, float width, float height, float near_depth,
float far_depth)
{
- // The x/y parameters here assume a upper-left origin. glViewport takes an offset from the
- // lower-left of the framebuffer, so we must set y to the distance from the lower-left.
- y = static_cast<float>(m_current_framebuffer_height) - y - height;
if (g_ogl_config.bSupportViewportFloat)
{
glViewportIndexedf(0, x, y, width, height);
@@ -1156,7 +900,7 @@ void Renderer::SetViewport(float x, float y, float width, float height, float ne
void Renderer::Draw(u32 base_vertex, u32 num_vertices)
{
- glDrawArrays(static_cast<const OGLPipeline*>(m_graphics_pipeline)->GetGLPrimitive(), base_vertex,
+ glDrawArrays(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(), base_vertex,
num_vertices);
}
@@ -1164,135 +908,112 @@ void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
{
if (g_ogl_config.bSupportsGLBaseVertex)
{
- glDrawElementsBaseVertex(static_cast<const OGLPipeline*>(m_graphics_pipeline)->GetGLPrimitive(),
+ glDrawElementsBaseVertex(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(),
num_indices, GL_UNSIGNED_SHORT,
static_cast<u16*>(nullptr) + base_index, base_vertex);
}
else
{
- glDrawElements(static_cast<const OGLPipeline*>(m_graphics_pipeline)->GetGLPrimitive(),
+ glDrawElements(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(),
num_indices, GL_UNSIGNED_SHORT, static_cast<u16*>(nullptr) + base_index);
}
}
-void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
- u32 color, u32 z)
+void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y,
+ u32 groups_z)
{
- ResetAPIState();
+ glUseProgram(static_cast<const OGLShader*>(shader)->GetGLComputeProgramID());
+ glDispatchCompute(groups_x, groups_y, groups_z);
- // color
- GLboolean const color_mask = colorEnable ? GL_TRUE : GL_FALSE,
- alpha_mask = alphaEnable ? GL_TRUE : GL_FALSE;
- glColorMask(color_mask, color_mask, color_mask, alpha_mask);
+ // We messed up the program binding, so restore it.
+ ProgramShaderCache::InvalidateLastProgram();
+ if (m_current_pipeline)
+ static_cast<const OGLPipeline*>(m_current_pipeline)->GetProgram()->shader.Bind();
- glClearColor(float((color >> 16) & 0xFF) / 255.0f, float((color >> 8) & 0xFF) / 255.0f,
- float((color >> 0) & 0xFF) / 255.0f, float((color >> 24) & 0xFF) / 255.0f);
+ // Barrier to texture can be used for reads.
+ if (m_bound_image_texture)
+ glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
+}
- // depth
- glDepthMask(zEnable ? GL_TRUE : GL_FALSE);
+void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
+ u32 color, u32 z)
+{
+ g_framebuffer_manager->FlushEFBPokes();
+ g_framebuffer_manager->InvalidatePeekCache();
- glClearDepthf(float(z & 0xFFFFFF) / 16777216.0f);
+ u32 clear_mask = 0;
+ if (colorEnable || alphaEnable)
+ {
+ glColorMask(colorEnable, colorEnable, colorEnable, alphaEnable);
+ glClearColor(float((color >> 16) & 0xFF) / 255.0f, float((color >> 8) & 0xFF) / 255.0f,
+ float((color >> 0) & 0xFF) / 255.0f, float((color >> 24) & 0xFF) / 255.0f);
+ clear_mask = GL_COLOR_BUFFER_BIT;
+ }
+ if (zEnable)
+ {
+ glDepthMask(zEnable ? GL_TRUE : GL_FALSE);
+ glClearDepthf(float(z & 0xFFFFFF) / 16777216.0f);
+ clear_mask |= GL_DEPTH_BUFFER_BIT;
+ }
// Update rect for clearing the picture
- glEnable(GL_SCISSOR_TEST);
-
- TargetRectangle const targetRc = ConvertEFBRectangle(rc);
- glScissor(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight());
-
// glColorMask/glDepthMask/glScissor affect glClear (glViewport does not)
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ const auto converted_target_rc =
+ ConvertFramebufferRectangle(ConvertEFBRectangle(rc), m_current_framebuffer);
+ SetScissorRect(converted_target_rc);
- RestoreAPIState();
+ glClear(clear_mask);
- ClearEFBCache();
+ // Restore color/depth mask.
+ if (colorEnable || alphaEnable)
+ {
+ glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate,
+ m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate);
+ }
+ if (zEnable)
+ glDepthMask(m_current_depth_state.updateenable);
+
+ // Scissor rect must be restored.
+ BPFunctions::SetScissor();
}
void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc)
{
- TargetRectangle source_rc = rc;
- source_rc.top = rc.GetHeight();
- source_rc.bottom = 0;
-
- // Check if we need to render to a new surface.
- TargetRectangle flipped_trc = GetTargetRectangle();
- std::swap(flipped_trc.top, flipped_trc.bottom);
-
- // Copy the framebuffer to screen.
- OpenGLPostProcessing* post_processor = static_cast<OpenGLPostProcessing*>(m_post_processor.get());
- if (g_ActiveConfig.stereo_mode == StereoMode::SBS ||
- g_ActiveConfig.stereo_mode == StereoMode::TAB)
- {
- TargetRectangle left_rc, right_rc;
+ // Quad-buffered stereo is annoying on GL.
+ if (g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer)
+ return ::Renderer::RenderXFBToScreen(texture, rc);
- // Top-and-Bottom mode needs to compensate for inverted vertical screen coordinates.
- if (g_ActiveConfig.stereo_mode == StereoMode::TAB)
- std::tie(right_rc, left_rc) = ConvertStereoRectangle(flipped_trc);
- else
- std::tie(left_rc, right_rc) = ConvertStereoRectangle(flipped_trc);
-
- post_processor->BlitFromTexture(source_rc, left_rc,
- static_cast<const OGLTexture*>(texture)->GetRawTexIdentifier(),
- texture->GetWidth(), texture->GetHeight(), 0);
- post_processor->BlitFromTexture(source_rc, right_rc,
- static_cast<const OGLTexture*>(texture)->GetRawTexIdentifier(),
- texture->GetWidth(), texture->GetHeight(), 1);
- }
- else if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer)
- {
- glDrawBuffer(GL_BACK_LEFT);
- post_processor->BlitFromTexture(source_rc, flipped_trc,
- static_cast<const OGLTexture*>(texture)->GetRawTexIdentifier(),
- texture->GetWidth(), texture->GetHeight(), 0);
+ const auto target_rc = GetTargetRectangle();
- glDrawBuffer(GL_BACK_RIGHT);
- post_processor->BlitFromTexture(source_rc, flipped_trc,
- static_cast<const OGLTexture*>(texture)->GetRawTexIdentifier(),
- texture->GetWidth(), texture->GetHeight(), 1);
+ glDrawBuffer(GL_BACK_LEFT);
+ m_post_processor->BlitFromTexture(target_rc, rc, texture, 0);
- glDrawBuffer(GL_BACK);
- }
- else
- {
- post_processor->BlitFromTexture(source_rc, flipped_trc,
- static_cast<const OGLTexture*>(texture)->GetRawTexIdentifier(),
- texture->GetWidth(), texture->GetHeight(), 0);
- }
-}
+ glDrawBuffer(GL_BACK_RIGHT);
+ m_post_processor->BlitFromTexture(target_rc, rc, texture, 1);
-void Renderer::ReinterpretPixelData(unsigned int convtype)
-{
- if (convtype == 0 || convtype == 2)
- {
- FramebufferManager::ReinterpretPixelData(convtype);
- }
- else
- {
- ERROR_LOG(VIDEO, "Trying to reinterpret pixel data with unsupported conversion type %d",
- convtype);
- }
+ glDrawBuffer(GL_BACK);
}
-void Renderer::SetFramebuffer(const AbstractFramebuffer* framebuffer)
+void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer)
{
- glBindFramebuffer(GL_FRAMEBUFFER, static_cast<const OGLFramebuffer*>(framebuffer)->GetFBO());
+ if (m_current_framebuffer == framebuffer)
+ return;
+
+ glBindFramebuffer(GL_FRAMEBUFFER, static_cast<OGLFramebuffer*>(framebuffer)->GetFBO());
m_current_framebuffer = framebuffer;
- m_current_framebuffer_width = framebuffer->GetWidth();
- m_current_framebuffer_height = framebuffer->GetHeight();
}
-void Renderer::SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer)
+void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
{
// EXT_discard_framebuffer could be used here to save bandwidth on tilers.
SetFramebuffer(framebuffer);
}
-void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
+void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer,
const ClearColor& color_value, float depth_value)
{
SetFramebuffer(framebuffer);
- // NOTE: This disturbs the current scissor/mask setting.
- // This won't be an issue when we implement proper state tracking.
glDisable(GL_SCISSOR_TEST);
GLbitfield clear_mask = 0;
if (framebuffer->HasColorBuffer())
@@ -1304,103 +1025,27 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
if (framebuffer->HasDepthBuffer())
{
glDepthMask(GL_TRUE);
- glClearDepth(depth_value);
+ glClearDepthf(depth_value);
clear_mask |= GL_DEPTH_BUFFER_BIT;
}
glClear(clear_mask);
-}
-
-void Renderer::ApplyBlendingState(const BlendingState state, bool force)
-{
- if (!force && m_current_blend_state == state)
- return;
-
- bool useDualSource =
- state.usedualsrc && g_ActiveConfig.backend_info.bSupportsDualSourceBlend &&
- (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) || state.dstalpha);
- // Only use shader blend if we need to and we don't support dual-source blending directly
- bool useShaderBlend = !useDualSource && state.usedualsrc && state.dstalpha &&
- g_ActiveConfig.backend_info.bSupportsFramebufferFetch;
-
- if (useShaderBlend)
- {
- glDisable(GL_BLEND);
- }
- else
- {
- const GLenum src_factors[8] = {GL_ZERO,
- GL_ONE,
- GL_DST_COLOR,
- GL_ONE_MINUS_DST_COLOR,
- useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
- useDualSource ? GL_ONE_MINUS_SRC1_ALPHA :
- (GLenum)GL_ONE_MINUS_SRC_ALPHA,
- GL_DST_ALPHA,
- GL_ONE_MINUS_DST_ALPHA};
- const GLenum dst_factors[8] = {GL_ZERO,
- GL_ONE,
- GL_SRC_COLOR,
- GL_ONE_MINUS_SRC_COLOR,
- useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
- useDualSource ? GL_ONE_MINUS_SRC1_ALPHA :
- (GLenum)GL_ONE_MINUS_SRC_ALPHA,
- GL_DST_ALPHA,
- GL_ONE_MINUS_DST_ALPHA};
-
- if (state.blendenable)
- {
- glEnable(GL_BLEND);
- }
- else
- {
- glDisable(GL_BLEND);
- }
-
- // Always call glBlendEquationSeparate and glBlendFuncSeparate, even when
- // GL_BLEND is disabled, as a workaround for some bugs (possibly graphics
- // driver issues?). See https://bugs.dolphin-emu.org/issues/10120 : "Sonic
- // Adventure 2 Battle: graphics crash when loading first Dark level"
- GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
- GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
- glBlendEquationSeparate(equation, equationAlpha);
- glBlendFuncSeparate(src_factors[state.srcfactor], dst_factors[state.dstfactor],
- src_factors[state.srcfactoralpha], dst_factors[state.dstfactoralpha]);
- }
-
- const GLenum logic_op_codes[16] = {
- GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP,
- GL_XOR, GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE,
- GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET};
+ glEnable(GL_SCISSOR_TEST);
- if (IsGLES())
- {
- // Logic ops aren't available in GLES3
- }
- else if (state.logicopenable)
- {
- glEnable(GL_COLOR_LOGIC_OP);
- glLogicOp(logic_op_codes[state.logicmode]);
- }
- else
+ // Restore color/depth mask.
+ if (framebuffer->HasColorBuffer())
{
- glDisable(GL_COLOR_LOGIC_OP);
+ glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate,
+ m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate);
}
-
- glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate);
- m_current_blend_state = state;
+ if (framebuffer->HasDepthBuffer())
+ glDepthMask(m_current_depth_state.updateenable);
}
void Renderer::BindBackbuffer(const ClearColor& clear_color)
{
CheckForSurfaceChange();
CheckForSurfaceResize();
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- m_current_framebuffer = nullptr;
- m_current_framebuffer_width = m_backbuffer_width;
- m_current_framebuffer_height = m_backbuffer_height;
+ SetAndClearFramebuffer(m_system_framebuffer.get(), clear_color);
}
void Renderer::PresentBackbuffer()
@@ -1419,25 +1064,6 @@ void Renderer::PresentBackbuffer()
void Renderer::OnConfigChanged(u32 bits)
{
- if (bits & (CONFIG_CHANGE_BIT_TARGET_SIZE | CONFIG_CHANGE_BIT_MULTISAMPLES |
- CONFIG_CHANGE_BIT_STEREO_MODE | CONFIG_CHANGE_BIT_BBOX))
- {
- s_MSAASamples = g_ActiveConfig.iMultisamples;
- if (s_MSAASamples > 1 && s_MSAASamples > g_ogl_config.max_samples)
- {
- s_MSAASamples = g_ogl_config.max_samples;
- OSD::AddMessage(
- StringFromFormat("%d Anti Aliasing samples selected, but only %d supported by your GPU.",
- s_MSAASamples, g_ogl_config.max_samples),
- 10000);
- }
-
- g_framebuffer_manager.reset();
- g_framebuffer_manager = std::make_unique<FramebufferManager>(
- m_target_width, m_target_height, s_MSAASamples, BoundingBox::NeedsStencilBuffer());
- BoundingBox::SetTargetSizeChanged(m_target_width, m_target_height);
- }
-
if (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive);
@@ -1448,10 +1074,15 @@ void Renderer::OnConfigChanged(u32 bits)
void Renderer::Flush()
{
// ensure all commands are sent to the GPU.
- // Otherwise the driver could batch several frames togehter.
+ // Otherwise the driver could batch several frames together.
glFlush();
}
+void Renderer::WaitForGPUIdle()
+{
+ glFinish();
+}
+
void Renderer::CheckForSurfaceChange()
{
if (!m_surface_changed.TestAndClear())
@@ -1463,6 +1094,7 @@ void Renderer::CheckForSurfaceChange()
// With a surface change, the window likely has new dimensions.
m_backbuffer_width = m_main_gl_context->GetBackBufferWidth();
m_backbuffer_height = m_main_gl_context->GetBackBufferHeight();
+ m_system_framebuffer->UpdateDimensions(m_backbuffer_width, m_backbuffer_height);
}
void Renderer::CheckForSurfaceResize()
@@ -1473,52 +1105,36 @@ void Renderer::CheckForSurfaceResize()
m_main_gl_context->Update();
m_backbuffer_width = m_main_gl_context->GetBackBufferWidth();
m_backbuffer_height = m_main_gl_context->GetBackBufferHeight();
+ m_system_framebuffer->UpdateDimensions(m_backbuffer_width, m_backbuffer_height);
}
-// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
-void Renderer::ResetAPIState()
+void Renderer::BeginUtilityDrawing()
{
- // Gets us to a reasonably sane state where it's possible to do things like
- // image copies with textured quads, etc.
- glDisable(GL_SCISSOR_TEST);
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_CULL_FACE);
- glDisable(GL_BLEND);
- if (!IsGLES())
- glDisable(GL_COLOR_LOGIC_OP);
+ ::Renderer::BeginUtilityDrawing();
+
+ glEnable(GL_PROGRAM_POINT_SIZE);
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
{
glDisable(GL_CLIP_DISTANCE0);
glDisable(GL_CLIP_DISTANCE1);
}
- glDepthMask(GL_FALSE);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- m_current_rasterization_state = RenderState::GetInvalidRasterizationState();
- m_current_depth_state = RenderState::GetInvalidDepthState();
- m_current_blend_state = RenderState::GetInvalidBlendingState();
}
-void Renderer::RestoreAPIState()
+void Renderer::EndUtilityDrawing()
{
- m_current_framebuffer = nullptr;
- m_current_framebuffer_width = m_target_width;
- m_current_framebuffer_height = m_target_height;
- FramebufferManager::SetFramebuffer(0);
+ ::Renderer::EndUtilityDrawing();
- // Gets us back into a more game-like state.
- glEnable(GL_SCISSOR_TEST);
+ glDisable(GL_PROGRAM_POINT_SIZE);
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
{
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
}
- BPFunctions::SetScissor();
- BPFunctions::SetViewport();
}
-void Renderer::ApplyRasterizationState(const RasterizationState state, bool force)
+void Renderer::ApplyRasterizationState(const RasterizationState state)
{
- if (!force && m_current_rasterization_state == state)
+ if (m_current_rasterization_state == state)
return;
// none, ccw, cw, ccw
@@ -1536,9 +1152,9 @@ void Renderer::ApplyRasterizationState(const RasterizationState state, bool forc
m_current_rasterization_state = state;
}
-void Renderer::ApplyDepthState(const DepthState state, bool force)
+void Renderer::ApplyDepthState(const DepthState state)
{
- if (!force && m_current_depth_state == state)
+ if (m_current_depth_state == state)
return;
const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
@@ -1562,30 +1178,116 @@ void Renderer::ApplyDepthState(const DepthState state, bool force)
m_current_depth_state = state;
}
+void Renderer::ApplyBlendingState(const BlendingState state)
+{
+ if (m_current_blend_state == state)
+ return;
+
+ bool useDualSource =
+ state.usedualsrc && g_ActiveConfig.backend_info.bSupportsDualSourceBlend &&
+ (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) || state.dstalpha);
+ // Only use shader blend if we need to and we don't support dual-source blending directly
+ bool useShaderBlend = !useDualSource && state.usedualsrc && state.dstalpha &&
+ g_ActiveConfig.backend_info.bSupportsFramebufferFetch;
+
+ if (useShaderBlend)
+ {
+ glDisable(GL_BLEND);
+ }
+ else
+ {
+ const GLenum src_factors[8] = {GL_ZERO,
+ GL_ONE,
+ GL_DST_COLOR,
+ GL_ONE_MINUS_DST_COLOR,
+ useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
+ useDualSource ? GL_ONE_MINUS_SRC1_ALPHA :
+ (GLenum)GL_ONE_MINUS_SRC_ALPHA,
+ GL_DST_ALPHA,
+ GL_ONE_MINUS_DST_ALPHA};
+ const GLenum dst_factors[8] = {GL_ZERO,
+ GL_ONE,
+ GL_SRC_COLOR,
+ GL_ONE_MINUS_SRC_COLOR,
+ useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
+ useDualSource ? GL_ONE_MINUS_SRC1_ALPHA :
+ (GLenum)GL_ONE_MINUS_SRC_ALPHA,
+ GL_DST_ALPHA,
+ GL_ONE_MINUS_DST_ALPHA};
+
+ if (state.blendenable)
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+
+ // Always call glBlendEquationSeparate and glBlendFuncSeparate, even when
+ // GL_BLEND is disabled, as a workaround for some bugs (possibly graphics
+ // driver issues?). See https://bugs.dolphin-emu.org/issues/10120 : "Sonic
+ // Adventure 2 Battle: graphics crash when loading first Dark level"
+ GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
+ GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
+ glBlendEquationSeparate(equation, equationAlpha);
+ glBlendFuncSeparate(src_factors[state.srcfactor], dst_factors[state.dstfactor],
+ src_factors[state.srcfactoralpha], dst_factors[state.dstfactoralpha]);
+ }
+
+ const GLenum logic_op_codes[16] = {
+ GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP,
+ GL_XOR, GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE,
+ GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET};
+
+ // Logic ops aren't available in GLES3
+ if (!IsGLES())
+ {
+ if (state.logicopenable)
+ {
+ glEnable(GL_COLOR_LOGIC_OP);
+ glLogicOp(logic_op_codes[state.logicmode]);
+ }
+ else
+ {
+ glDisable(GL_COLOR_LOGIC_OP);
+ }
+ }
+
+ glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate);
+ m_current_blend_state = state;
+}
+
void Renderer::SetPipeline(const AbstractPipeline* pipeline)
{
- // Not all shader changes currently go through SetPipeline, so we can't
- // test if the pipeline hasn't changed and skip these applications. Yet.
- m_graphics_pipeline = static_cast<const OGLPipeline*>(pipeline);
- if (!m_graphics_pipeline)
+ if (m_current_pipeline == pipeline)
return;
- ApplyRasterizationState(m_graphics_pipeline->GetRasterizationState());
- ApplyDepthState(m_graphics_pipeline->GetDepthState());
- ApplyBlendingState(m_graphics_pipeline->GetBlendingState());
- ProgramShaderCache::BindVertexFormat(m_graphics_pipeline->GetVertexFormat());
- m_graphics_pipeline->GetProgram()->shader.Bind();
+ if (pipeline)
+ {
+ ApplyRasterizationState(static_cast<const OGLPipeline*>(pipeline)->GetRasterizationState());
+ ApplyDepthState(static_cast<const OGLPipeline*>(pipeline)->GetDepthState());
+ ApplyBlendingState(static_cast<const OGLPipeline*>(pipeline)->GetBlendingState());
+ ProgramShaderCache::BindVertexFormat(
+ static_cast<const OGLPipeline*>(pipeline)->GetVertexFormat());
+ static_cast<const OGLPipeline*>(pipeline)->GetProgram()->shader.Bind();
+ }
+ else
+ {
+ ProgramShaderCache::InvalidateLastProgram();
+ glUseProgram(0);
+ }
+ m_current_pipeline = pipeline;
}
void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
{
- if (m_bound_textures[index] == texture)
+ const OGLTexture* gl_texture = static_cast<const OGLTexture*>(texture);
+ if (m_bound_textures[index] == gl_texture)
return;
glActiveTexture(GL_TEXTURE0 + index);
- glBindTexture(GL_TEXTURE_2D_ARRAY,
- texture ? static_cast<const OGLTexture*>(texture)->GetRawTexIdentifier() : 0);
- m_bound_textures[index] = texture;
+ if (gl_texture)
+ glBindTexture(gl_texture->GetGLTarget(), gl_texture->GetGLTextureId());
+ else
+ glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
+ m_bound_textures[index] = gl_texture;
}
void Renderer::SetSamplerState(u32 index, const SamplerState& state)
@@ -1593,6 +1295,25 @@ void Renderer::SetSamplerState(u32 index, const SamplerState& state)
g_sampler_cache->SetSamplerState(index, state);
}
+void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write)
+{
+ if (m_bound_image_texture == texture)
+ return;
+
+ if (texture)
+ {
+ const GLenum access = read ? (write ? GL_READ_WRITE : GL_READ_ONLY) : GL_WRITE_ONLY;
+ glBindImageTexture(0, static_cast<OGLTexture*>(texture)->GetGLTextureId(), 0, GL_TRUE, 0,
+ access, static_cast<OGLTexture*>(texture)->GetGLFormatForImageTexture());
+ }
+ else
+ {
+ glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
+ }
+
+ m_bound_image_texture = texture;
+}
+
void Renderer::UnbindTexture(const AbstractTexture* texture)
{
for (size_t i = 0; i < m_bound_textures.size(); i++)
@@ -1604,15 +1325,34 @@ void Renderer::UnbindTexture(const AbstractTexture* texture)
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
m_bound_textures[i] = nullptr;
}
-}
-void Renderer::SetInterlacingMode()
-{
- // TODO
+ if (m_bound_image_texture == texture)
+ {
+ glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
+ m_bound_image_texture = nullptr;
+ }
}
std::unique_ptr<VideoCommon::AsyncShaderCompiler> Renderer::CreateAsyncShaderCompiler()
{
return std::make_unique<SharedContextAsyncShaderCompiler>();
}
+
+void Renderer::BindSharedReadFramebuffer()
+{
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, m_shared_read_framebuffer);
+}
+
+void Renderer::BindSharedDrawFramebuffer()
+{
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_shared_draw_framebuffer);
+}
+
+void Renderer::RestoreFramebufferBinding()
+{
+ glBindFramebuffer(
+ GL_FRAMEBUFFER,
+ m_current_framebuffer ? static_cast<OGLFramebuffer*>(m_current_framebuffer)->GetFBO() : 0);
+}
+
} // namespace OGL