diff options
Diffstat (limited to 'Source/Core/VideoBackends/OGL/Render.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/OGL/Render.cpp | 780 |
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 |
