我正在尝试以 60Fps 而不是 30Fps 运行游戏。游戏锁定为 30fps,因此我可以达到 60fps 的唯一方法是帧插值。
对于插值部分,我将使用 OpenCV,我找到了 this pretty good article .
此游戏使用 OpenGL。经过一番研究,我发现最好的抓屏方式是hook SwapBuffer函数。所以我选择 Hook 游戏来抓取屏幕并将它们发送到另一个应用程序,这将实时播放游戏并顺便添加插值帧以达到 60 FPS(如果你有更好的主意,我完全打开!)
我开始编写我的新 dll。我使用 C# 编写代码,并选择 EasyHook 来注入(inject)我的 DLL。
Hooking 运行良好,现在非常酷 =D。
但是,我现在卡住了,因为我完全不知道如何从游戏中抓取屏幕。
我尝试使用 OpenGL.Net和 SharpGL ,但我无法弄清楚我应该如何获取实际的 OpenGL 上下文以使用 glReadPixel!
这是我的实际代码:
using System;
using System.Runtime.InteropServices;
namespace Hook
{
public class ServerInterface : MarshalByRefObject
{
public void IsInstalled(int clientPID)
{
Console.WriteLine("This programm has injected dll into process {0}.\r\n", clientPID);
}
public void ReportMessage(int clientPID, string message)
{
Console.WriteLine(message);
}
public void ReportException(Exception e)
{
Console.WriteLine("The target process has reported an error:\r\n" + e.ToString());
}
public void Ping()
{
Console.WriteLine("Ping !");
}
}
//Point d'entrée
public class InjectionEntryPoint : EasyHook.IEntryPoint
{
//Serveur :
ServerInterface server = null;
public InjectionEntryPoint(EasyHook.RemoteHooking.IContext context, string channelName)
{
//Constructeur
//Objectif : vérifier si la communication entre les deux programmes peut etre établit
server = EasyHook.RemoteHooking.IpcConnectClient<ServerInterface>(channelName);
server.Ping();
}
public void Run(EasyHook.RemoteHooking.IContext context, string channelName)
{
try
{
var SwapBuffersHook = EasyHook.LocalHook.Create(EasyHook.LocalHook.GetProcAddress("opengl32.dll", "wglSwapBuffers"), new SwapBuffers_Delegate(SwapBuffers_Hook), this);
SwapBuffersHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
server.ReportMessage(EasyHook.RemoteHooking.GetCurrentProcessId(), "SwapBuffers Hooked");
}
catch (Exception ExtInfo)
{
server.ReportException(ExtInfo);
return;
}
server.IsInstalled(EasyHook.RemoteHooking.GetCurrentProcessId());
EasyHook.RemoteHooking.WakeUpProcess();
//Waiting years
while( true)
{
System.Threading.Thread.Sleep(10000);
}
// Finalise cleanup of hooks
EasyHook.LocalHook.Release();
}
//Deleguate
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
delegate IntPtr SwapBuffers_Delegate(IntPtr hdc);
//Original
[DllImport("opengl32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern IntPtr wglSwapBuffers(IntPtr hdc);
//Hook function
public IntPtr SwapBuffers_Hook(IntPtr hdc)
{
server.ReportMessage(EasyHook.RemoteHooking.GetCurrentProcessId(), "I'm in SwapBuffers =DDDD");
//Here I need to grab frames
//Return
return wglSwapBuffers(hdc);
}
}
}
最佳答案
我怀疑您能否提高游戏的 FPS。顺便说一句,有一些可能性。
你在做什么是正确的方法。您只是错过了创建要显示的中间框架的 GL 代码(我建议使用当前的 GL 上下文在进程中创建它)。
问题是在执行 SwapBuffers
命令时显示视频帧。这是您 Hook 的那个,但问问自己和程序:为什么每秒调用 30 次 SwapBuffers
?
如果答案是“因为应用程序使用定时器挂起执行”,你就不能轻易解决这个问题:你的钩子(Hook)必须与应用程序一起工作,因此你需要了解应用程序如何休眠。
如果答案是“因为应用程序正在利用 WGL_EXT_swap_control”,那么您有一种可能性:
- 设置 wglSwapIntervalEXT(1)(因为应用程序调用了 wglSwapIntervalEXT(2) 以获得与 V-Sync 同步的 30 FPS。可能这只需要一次
- 在你的钩子(Hook)中,抓取两帧,然后在每隔一帧对它们进行插值(因此你的问题)。在每个钩子(Hook)执行时,您需要渲染插值帧,然后交换;然后渲染下一个原始帧,然后交换。
如何创建插值帧?如果您通过 CPU 进行操作,请将 glReadPixels 与后台缓冲区 (GL_BACK) 一起用作读取帧缓冲区(使用 glDrawBuffers)。这将下载帧缓冲区绑定(bind)的内容。然后使用混合方程得到插值像素。
关于C# Opengl Hook Swapbuffer 添加插值帧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43877395/