diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp index 95e3643277..10b37befd4 100644 --- a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp @@ -35,6 +35,10 @@ GLuint FramebufferManager::m_resolvedDepthTexture; // reinterpret pixel format SHADER FramebufferManager::m_pixel_format_shaders[2]; +// EFB pokes +GLuint FramebufferManager::m_EfbColorPokes_VBO; +GLuint FramebufferManager::m_EfbColorPokes_VAO; +SHADER FramebufferManager::m_EfbColorPokes; FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int msaaSamples) { @@ -318,6 +322,51 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms ProgramShaderCache::CompileShader(m_pixel_format_shaders[0], vs, ps_rgb8_to_rgba6.c_str(), (m_EFBLayers > 1) ? gs.c_str() : nullptr); ProgramShaderCache::CompileShader(m_pixel_format_shaders[1], vs, ps_rgba6_to_rgb8.c_str(), (m_EFBLayers > 1) ? gs.c_str() : nullptr); + + ProgramShaderCache::CompileShader(m_EfbColorPokes, + StringFromFormat( + "in vec2 rawpos;\n" + "in vec4 color0;\n" + "out vec4 v_c;\n" + "void main(void) {\n" + " gl_Position = vec4(((rawpos + 0.5) / vec2(640.0, 528.0) * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);\n" + " gl_PointSize = %d.0 / 640.0;\n" + " v_c = color0;\n" + "}\n", m_targetWidth).c_str(), + + StringFromFormat( + "in vec4 %s_c;\n" + "out vec4 ocol0;\n" + "void main(void) {\n" + " ocol0 = %s_c.bgra;\n" + "}\n", m_EFBLayers > 1 ? "g" : "v", m_EFBLayers > 1 ? "g" : "v").c_str(), + + m_EFBLayers > 1 ? StringFromFormat( + "layout(points) in;\n" + "layout(points, max_vertices = %d) out;\n" + "in vec4 v_c[1];\n" + "out vec4 g_c;\n" + "void main()\n" + "{\n" + " for (int j = 0; j < %d; ++j) {\n" + " gl_Layer = j;\n" + " gl_Position = gl_in[0].gl_Position;\n" + " gl_PointSize = %d.0 / 640.0;\n" + " g_c = v_c[0];\n" + " EmitVertex();\n" + " EndPrimitive();\n" + " }\n" + "}\n", m_EFBLayers, m_EFBLayers, m_targetWidth).c_str() : nullptr); + glGenBuffers(1, &m_EfbColorPokes_VBO); + glGenVertexArrays(1, &m_EfbColorPokes_VAO); + glBindBuffer(GL_ARRAY_BUFFER, m_EfbColorPokes_VBO); + glBindVertexArray(m_EfbColorPokes_VAO ); + glEnableVertexAttribArray(SHADER_POSITION_ATTRIB); + glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_UNSIGNED_SHORT, 0, sizeof(EfbPokeData), (void*)offsetof(EfbPokeData, x)); + glEnableVertexAttribArray(SHADER_COLOR0_ATTRIB); + glVertexAttribPointer(SHADER_COLOR0_ATTRIB, 4, GL_UNSIGNED_BYTE, 1, sizeof(EfbPokeData), (void*)offsetof(EfbPokeData, data)); + + glEnable(GL_PROGRAM_POINT_SIZE); } FramebufferManager::~FramebufferManager() @@ -355,6 +404,13 @@ FramebufferManager::~FramebufferManager() // reinterpret pixel format m_pixel_format_shaders[0].Destroy(); m_pixel_format_shaders[1].Destroy(); + + // EFB pokes + glDeleteBuffers(1, &m_EfbColorPokes_VBO); + glDeleteVertexArrays(1, &m_EfbColorPokes_VAO); + m_EfbColorPokes_VBO = 0; + m_EfbColorPokes_VAO = 0; + m_EfbColorPokes.Destroy(); } GLuint FramebufferManager::GetEFBColorTexture(const EFBRectangle& sourceRc) @@ -551,4 +607,31 @@ void FramebufferManager::GetTargetSize(unsigned int *width, unsigned int *height *height = m_targetHeight; } +void FramebufferManager::PokeEFB(EFBAccessType type, const std::vector& data) +{ + switch (type) + { + case POKE_COLOR: + { + g_renderer->ResetAPIState(); + + glBindVertexArray(m_EfbColorPokes_VAO); + glBindBuffer(GL_ARRAY_BUFFER, m_EfbColorPokes_VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(EfbPokeData) * data.size(), data.data(), GL_STREAM_DRAW); + m_EfbColorPokes.Bind(); + glViewport(0, 0, m_targetWidth, m_targetHeight); + glDrawArrays(GL_POINTS, 0, (GLsizei)data.size()); + + g_renderer->RestoreAPIState(); + + // TODO: Could just update the EFB cache with the new value + ClearEFBCache(); + break; + } + + default: + break; + } +} + } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.h b/Source/Core/VideoBackends/OGL/FramebufferManager.h index d0c430a3f3..51f47de072 100644 --- a/Source/Core/VideoBackends/OGL/FramebufferManager.h +++ b/Source/Core/VideoBackends/OGL/FramebufferManager.h @@ -91,6 +91,8 @@ public: // convtype=0 -> rgb8->rgba6, convtype=2 -> rgba6->rgb8 static void ReinterpretPixelData(unsigned int convtype); + static void PokeEFB(EFBAccessType type, const std::vector& data); + private: XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers) override; void GetTargetSize(unsigned int *width, unsigned int *height) override; @@ -115,6 +117,11 @@ private: // For pixel format draw static SHADER m_pixel_format_shaders[2]; + + // For EFB pokes + static GLuint m_EfbColorPokes_VBO; + static GLuint m_EfbColorPokes_VAO; + static SHADER m_EfbColorPokes; }; } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 8f8211b363..745a52a40c 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1108,23 +1108,13 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) case POKE_COLOR: { - ResetAPIState(); - - glClearColor(float((poke_data >> 16) & 0xFF) / 255.0f, - float((poke_data >> 8) & 0xFF) / 255.0f, - float((poke_data >> 0) & 0xFF) / 255.0f, - float((poke_data >> 24) & 0xFF) / 255.0f); - - glEnable(GL_SCISSOR_TEST); - glScissor(targetPixelRc.left, targetPixelRc.bottom, targetPixelRc.GetWidth(), targetPixelRc.GetHeight()); - - glClear(GL_COLOR_BUFFER_BIT); - - RestoreAPIState(); - - // TODO: Could just update the EFB cache with the new value - ClearEFBCache(); - + std::vector vector; + EfbPokeData d; + d.x = x; + d.y = y; + d.data = poke_data; + vector.push_back(d); + PokeEFB(type, vector); break; } @@ -1155,6 +1145,22 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) return 0; } +void Renderer::PokeEFB(EFBAccessType type, const std::vector& data) +{ + switch(type) + { + case POKE_COLOR: + { + FramebufferManager::PokeEFB(type, data); + break; + } + + default: + ::Renderer::PokeEFB(type, data); + break; + } +} + u16 Renderer::BBoxRead(int index) { int swapped_index = index; diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index 25c72d5655..08ca12ba03 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -75,6 +75,7 @@ public: void FlipImageData(u8 *data, int w, int h, int pixel_width = 3); u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; + void PokeEFB(EFBAccessType type, const std::vector& data) override; u16 BBoxRead(int index) override; void BBoxWrite(int index, u16 value) override; diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp index a702bf01f6..993b6bce2c 100644 --- a/Source/Core/VideoCommon/AsyncRequests.cpp +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -20,7 +20,34 @@ void AsyncRequests::PullEventsInternal() while (!m_queue.empty()) { - const Event& e = m_queue.front(); + Event e = m_queue.front(); + + // try to merge as many efb pokes as possible + // it's a bit hacky, but some games render a complete frame in this way + if ((e.type == Event::EFB_POKE_COLOR || e.type == Event::EFB_POKE_Z)) + { + m_merged_efb_pokes.clear(); + Event first_event = m_queue.front(); + EFBAccessType t = first_event.type == Event::EFB_POKE_COLOR ? POKE_COLOR : POKE_Z; + + do + { + e = m_queue.front(); + + EfbPokeData d; + d.data = e.efb_poke.data; + d.x = e.efb_poke.x; + d.y = e.efb_poke.y; + m_merged_efb_pokes.push_back(d); + + m_queue.pop(); + } while(!m_queue.empty() && m_queue.front().type == first_event.type); + + lock.unlock(); + g_renderer->PokeEFB(t, m_merged_efb_pokes); + lock.lock(); + continue; + } lock.unlock(); HandleEvent(e); diff --git a/Source/Core/VideoCommon/AsyncRequests.h b/Source/Core/VideoCommon/AsyncRequests.h index 8149bb7a45..39e79ab8e3 100644 --- a/Source/Core/VideoCommon/AsyncRequests.h +++ b/Source/Core/VideoCommon/AsyncRequests.h @@ -11,6 +11,8 @@ #include "Common/CommonTypes.h" +struct EfbPokeData; + class AsyncRequests { public: @@ -91,4 +93,6 @@ private: bool m_wake_me_up_again; bool m_enable; bool m_passthrough; + + std::vector m_merged_efb_pokes; }; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 18aaef599f..dba0667b64 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -599,3 +599,11 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const Core::Callback_VideoCopiedToXFB(XFBWrited || (g_ActiveConfig.bUseXFB && g_ActiveConfig.bUseRealXFB)); XFBWrited = false; } + +void Renderer::PokeEFB(EFBAccessType type, const std::vector& data) +{ + for (EfbPokeData poke : data) + { + AccessEFB(type, poke.x, poke.y, poke.data); + } +} diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index b8a0ec66a5..3535284d2e 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -26,6 +26,12 @@ class PostProcessingShaderImplementation; +struct EfbPokeData +{ + u16 x,y; + u32 data; +}; + // TODO: Move these out of here. extern int frameCount; extern int OSDChoice; @@ -104,6 +110,7 @@ public: static void RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbWidth, u32 fbHeight, float Gamma = 1.0f); virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) = 0; + virtual void PokeEFB(EFBAccessType type, const std::vector& data); virtual u16 BBoxRead(int index) = 0; virtual void BBoxWrite(int index, u16 value) = 0;