Thiết kế cơ bản cho điều khiển kết xuất mở rộng:
Định nghĩa lớp cơ sở FramebufferBase để quản lý kích thước và tài nguyên hình ảnh.
public abstract class FramebufferBase : IDisposable
{
public abstract int Width { get; }
public abstract int Height { get; }
public abstract D3DImage D3DSurface { get; }
public abstract void Dispose();
}
Xây dựng lớp điều khiển cơ sở GameBase.
public abstract class GameBase<TFrame> : Control where TFrame : FramebufferBase
Thêm sự kiện chuẩn bị, kết xuất và cập nhật.
public abstract event Action Initialize;
public abstract event Action<TimeSpan> RenderFrame;
public abstract event Action<object, TimeSpan> PostRender;
Khai báo phương thức trừu tượng cốt lõi.
protected abstract void SetupResources();
protected abstract void DrawContent(DrawingContext ctx);
protected abstract void Resize(SizeChangedInfo sizeInfo);
Xử lý thay đổi kích thước và kết xuất.
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
Resize(sizeInfo);
}
}
protected override void OnRender(DrawingContext ctx)
{
if (DesignerProperties.GetIsInDesignMode(this))
{
DrawDesignPreview(ctx);
}
else if (Frame != null && Frame.D3DSurface.IsFrontBufferAvailable)
{
DrawContent(ctx);
_timer.Restart();
}
}
Khởi động vòng lặp kết xuất.
public void StartRendering()
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
IsVisibleChanged += (s, e) =>
{
if ((bool)e.NewValue)
CompositionTarget.Rendering += OnCompositionRendering;
else
CompositionTarget.Rendering -= OnCompositionRendering;
};
Loaded += (s, e) => InvalidateVisual();
SetupResources();
}
}
private void OnCompositionRendering(object sender, EventArgs e)
{
var args = (RenderingEventArgs)e;
if (_prevRenderTime != args.RenderingTime)
{
InvalidateVisual();
UpdateFrameRate(args.RenderingTime);
_prevRenderTime = args.RenderingTime;
}
}
Khai báo biến và thuộc tính.
public static readonly DependencyProperty FrameRateProperty =
DependencyProperty.Register(nameof(FPS), typeof(int), typeof(GameBase<TFrame>));
protected TFrame Frame { get; set; }
protected Stopwatch _timer = Stopwatch.StartNew();
private TimeSpan _prevRenderTime = TimeSpan.Zero;
public int FPS
{
get => (int)GetValue(FrameRateProperty);
set => SetValue(FrameRateProperty, value);
}
Triển khai D3D9:
Thiết lập ngữ cảnh kết xuất D3D9.
public unsafe class D3DContext
{
public IDirect3DDevice9Ex* Device { get; }
public Format SurfaceFormat { get; }
public D3DContext()
{
IDirect3D9Ex* d3d9;
D3D9.GetApi().Direct3DCreate9Ex(D3D9.SdkVersion, &d3d9);
Displaymodeex mode = new((uint)sizeof(Displaymodeex));
d3d9->GetAdapterDisplayModeEx(D3D9.AdapterDefault, ref mode, null);
PresentParameters parameters = new()
{
Windowed = 1,
SwapEffect = Swapeffect.Discard,
BackBufferFormat = mode.Format,
BackBufferWidth = 1,
BackBufferHeight = 1
};
IDirect3DDevice9Ex* device;
d3d9->CreateDeviceEx(/*...*/, &device);
Device = device;
SurfaceFormat = mode.Format;
}
}
Tạo framebuffer cho D3D9.
public unsafe class D3DFrameBuffer : FramebufferBase
{
public D3DContext Context { get; }
public override int Width { get; }
public override int Height { get; }
public override D3DImage D3DSurface { get; }
public D3DFrameBuffer(D3DContext ctx, int w, int h)
{
Context = ctx;
Width = w;
Height = h;
IDirect3DSurface9* surface;
ctx.Device->CreateRenderTarget(/*...*/, &surface);
D3DSurface = new D3DImage();
D3DSurface.Lock();
D3DSurface.SetBackBuffer(D3DResourceType.IDirect3DSurface9, (IntPtr)surface);
D3DSurface.Unlock();
}
}
Điều khiển kết xuất D3D9.
public unsafe class D3DControl : GameBase<D3DFrameBuffer>
{
private D3DContext _ctx;
protected override void SetupResources()
{
_ctx = new D3DContext();
Initialize?.Invoke();
}
protected override void Resize(SizeChangedInfo info)
{
if (_ctx != null && info.NewSize.Width > 0)
{
Frame?.Dispose();
Frame = new D3DFrameBuffer(_ctx, (int)info.NewSize.Width, (int)info.NewSize.Height);
}
}
protected override void DrawContent(DrawingContext ctx)
{
Frame.D3DSurface.Lock();
RenderFrame?.Invoke(_timer.Elapsed);
Frame.D3DSurface.AddDirtyRect(new Int32Rect(0, 0, Width, Height));
Frame.D3DSurface.Unlock();
ctx.DrawImage(Frame.D3DSurface, new Rect(0, 0, Width, Height));
PostRender?.Invoke(this, _timer.Elapsed);
}
}
Triển khai OpenGL:
Thiết lập ngữ cảnh chia sẻ OpenGL.
public unsafe class GLContext
{
public IntPtr DxDeviceHandle { get; }
public IntPtr GlContextHandle { get; }
public IGraphicsContext GraphicsContext { get; }
public GLContext(GLConfig config)
{
// Khởi tạo thiết bị D3D9
IDirect3DDevice9Ex* d3dDevice = InitializeD3D();
DxDeviceHandle = (IntPtr)d3dDevice;
// Tạo ngữ cảnh OpenGL chia sẻ
GraphicsContext = CreateSharedGLContext(config);
GlContextHandle = Wgl.DXOpenDeviceNV(DxDeviceHandle);
}
}
Framebuffer liên kết D3D-OpenGL.
public class GLFrameBuffer : FramebufferBase
{
public int FboHandle { get; }
public int ColorTexture { get; }
public GLFrameBuffer(GLContext ctx, int w, int h)
{
// Tạo surface D3D9
IDirect3DSurface9* surface = CreateD3DSurface(ctx, w, h);
// Đăng ký tài nguyên chia sẻ
IntPtr dxResource = Wgl.DXRegisterObjectNV(/*...*/);
// Tạo FBO OpenGL
FboHandle = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FboHandle);
GL.FramebufferTexture2D(/*...*/);
// Liên kết với D3DImage
D3DSurface = new D3DImage();
D3DSurface.SetBackBuffer(D3DResourceType.IDirect3DSurface9, (IntPtr)surface);
}
}
Điều khiển kết xuất OpenGL.
public class GLControl : GameBase<GLFrameBuffer>
{
protected override void DrawContent(DrawingContext ctx)
{
Frame.D3DSurface.Lock();
Wgl.DXLockObjectsNV(/*...*/);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Frame.FboHandle);
RenderFrame?.Invoke(_timer.Elapsed);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
Wgl.DXUnlockObjectsNV(/*...*/);
// Áp dụng biến đổi tọa độ
ctx.PushTransform(new TranslateTransform(0, Height));
ctx.PushTransform(new ScaleTransform(1, -1));
ctx.DrawImage(Frame.D3DSurface, new Rect(0, 0, Width, Height));
ctx.Pop();
ctx.Pop();
PostRender?.Invoke(this, _timer.Elapsed);
}
}