Image detection optimization - c#

I'm writing a script to automate the boring parts of a game. Essentially, I have a check boxes for different parts of the game that multiple players can use. If a checkbox its ticked then the script takes a screenshot and then if the location is available, my character is able to click the location and hops on this particular location before another player can, which looks like figure1.
Now, I have at most 18 possible locations my character can access (as long as another character is not already there.) If I have all 18 of the boxes checked, then my script will start running through all 18 of these locations until it finds an open one. Which is represented by basically a copy/paste of figure1 but adjusted in figure2.
I have a timer that kicks off every 5 seconds that checks to see if the screen has changed to check if it needs to take action. While, its not using a huge amount of CPU/memory sometimes it stops responding which isnt good. Also, every 5 seconds (which is when its performing screen checks) the power usage jumps to Very High. How can I optimize this so its more efficient? Are there better ways to check for screen changes?
--figure1--
private void Form1_Load(object sender, EventArgs e)
{
//reference picture in resource folder
ImageName = new LockBitmap(Properties.Resources.ImageName);
ImageName.LockBits();
//ImageDetection
private LockBitmap ImageName;
private void RunImageName()
{
var handle = GetWindowsHandle();
using (var screenshot = new LockBitmap(CaptureClient(handle)))
{
screenshot.LockBits();
//Extensions menthod
//screenshot.DoesImageExist(playbutton);
var location = screenshot.GetFirstLocation(ImageName);
if (location != Point.Empty)
{
var topleft = GetProcessWindowPoint(handle);
var clickpoint = new Point(topleft.X + location.X, topleft.Y + location.Y);
User32.SetCursorPos(clickpoint.X, clickpoint.Y);
User32.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Thread.Sleep(50);
User32.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
//Argument
if (ImageName01.Checked)
{
RunImageName();
RunImageNameIcon();
}
}
--figure2--
private void Form1_Load(object sender, EventArgs e)
{
//reference picture in resource folder
ImageName1 = new LockBitmap(Properties.Resources.ImageName1);
ImageName1.LockBits();
ImageName2 = new LockBitmap(Properties.Resources.ImageName2);
ImageName2.LockBits();
ImageName3 = new LockBitmap(Properties.Resources.ImageName3);
ImageName3.LockBits();
}
//ImageDetection
private LockBitmap ImageName1;
private void RunImageName1()
{
var handle = GetWindowsHandle();
using (var screenshot = new LockBitmap(CaptureClient(handle)))
{
screenshot.LockBits();
//Extensions menthod
//screenshot.DoesImageExist(playbutton);
var location = screenshot.GetFirstLocation(ImageName1);
if (location != Point.Empty)
{
var topleft = GetProcessWindowPoint(handle);
var clickpoint = new Point(topleft.X + location.X, topleft.Y + location.Y);
User32.SetCursorPos(clickpoint.X, clickpoint.Y);
User32.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Thread.Sleep(50);
User32.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
}
private LockBitmap ImageName2;
private void RunImageName2()
{
var handle = GetWindowsHandle();
using (var screenshot = new LockBitmap(CaptureClient(handle)))
{
screenshot.LockBits();
//Extensions menthod
//screenshot.DoesImageExist(playbutton);
var location = screenshot.GetFirstLocation(ImageName2);
if (location != Point.Empty)
{
var topleft = GetProcessWindowPoint(handle);
var clickpoint = new Point(topleft.X + location.X, topleft.Y + location.Y);
User32.SetCursorPos(clickpoint.X, clickpoint.Y);
User32.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Thread.Sleep(50);
User32.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
}
private LockBitmap ImageName3;
private void RunImageName3()
{
var handle = GetWindowsHandle();
using (var screenshot = new LockBitmap(CaptureClient(handle)))
{
screenshot.LockBits();
//Extensions menthod
//screenshot.DoesImageExist(playbutton);
var location = screenshot.GetFirstLocation(ImageName3);
if (location != Point.Empty)
{
var topleft = GetProcessWindowPoint(handle);
var clickpoint = new Point(topleft.X + location.X, topleft.Y + location.Y);
User32.SetCursorPos(clickpoint.X, clickpoint.Y);
User32.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Thread.Sleep(50);
User32.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
//Argument
if (ImageName01.Checked)
{
RunImageName1();
RunImageNameIcon();
}
if (ImageName02.Checked)
{
RunImageName2();
RunImageNameIcon();
}
if (ImageName03.Checked)
{
RunImageName3();
RunImageNameIcon();
}
}

Try using a background worker. When you run the computations on the same thread as the UI it creates bottlenecks which causes the UI to freeze up and sometimes do other strange stuff. Here's a link that might be useful Using a Background Worker.

Related

Trying to match pixel colour then click [C#]

I need help getting my program to match the "stored" colour with the current one in the same location then click the mouse if it's the same. The grabbing of the colour works great so far in my code just unsure how to match a colour and a point, etc.
Also a start/stop button for the loop would be nice.
My code so far:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Pixel_detection_test_3
{
public partial class PixelDetectionForm : Form
{
private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;
[DllImport("user32.dll")]
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, uint dwExtraInf);
private int pixelY;
private int pixelX;
private Point pixelYX;
private static Color currentColour;
private static Color storedColour;
public PixelDetectionForm()
{
InitializeComponent();
}
static Color GetPixel(Point position)
{
using (var bitmap = new Bitmap(1, 1))
{
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(position, new Point(0, 0), new Size(1, 1));
}
return bitmap.GetPixel(0, 0);
}
}
private void PixelDetectionForm_KeyDown(object sender, KeyEventArgs e)
{
// Get Cursor Pixel Position
if (e.KeyCode == Keys.F1 || e.KeyCode == Keys.F2)
{
pixelY = Cursor.Position.Y;
pixelX = Cursor.Position.X;
pixelYX = Cursor.Position;
textBoxYPos.Text = pixelY.ToString();
textBoxXPos.Text = pixelX.ToString();
e.Handled = true;
}
// Get Cursor Pixel Colour
if (e.KeyCode == Keys.F1 || e.KeyCode == Keys.F3)
{
storedColour = GetPixel(Cursor.Position);
textBoxColour.Text = storedColour.ToString().Remove(0, 14).TrimEnd(']');
panelColourDisplay.BackColor = storedColour;
e.Handled = true;
}
}
// Not working, need help with this
private async void buttonStart_Click(object sender, EventArgs e)
{
while (true)
{
GetPixel(pixelYX);
// Should get position of 'pixelY' and 'pixelX'
panelColourDisplay2.BackColor = GetPixel(Cursor.Position);
if (pixelYX == storedColour)
{
MousePress();
}
// Need this to prevent not responding
await Task.Delay(3);
}
}
private void MousePress()
{
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
private void PixelDetectionForm_Click(object sender, EventArgs e)
{
ActiveControl = null;
}
private void PixelDetectionForm_Activated(object sender, EventArgs e)
{
ActiveControl = null;
}
}
}
Thanks
Well, an alternative to the while..loop is using a Timer to achieve that.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Pixel_detection_test_3
{
public partial class PixelDetectionForm : Form
{
private readonly Timer Tmr;
private Point lastPoint;
//Assign this from your input code.
private Color targetColor;
public PixelDetectionForm()
{
Tmr = new Timer { Interval = 50 };
Tmr.Tick += (s, e) => FindMatches(Cursor.Position);
}
//...
In the timer's Tick event, the FindMatches(..) method is called to check the current Cursor.Position and add the distinct matches into a ListBox. You can replace the last part with what you really need to do when you find a match. Like calling the MousePress() method in your code:
//...
private void FindMatches(Point p)
{
//To avoid the redundant calls..
if (p.Equals(lastPoint)) return;
lastPoint = p;
using (var b = new Bitmap(1, 1))
using (var g = Graphics.FromImage(b))
{
g.CopyFromScreen(p, Point.Empty, b.Size);
var c = b.GetPixel(0, 0);
if (c.ToArgb().Equals(targetColor.ToArgb()) &&
!listBox1.Items.Cast<Point>().Contains(p))
{
listBox1.Items.Add(p);
listBox1.SelectedItem = p;
}
}
}
private void PixelDetectionForm_FormClosing(object sender, FormClosingEventArgs e)
{
Tmr.Dispose();
}
}
}
Start and stop the timer in the click events of the Start and Stop buttons.
Here's a demo:
Another alternative is to use the Global Mouse and Keyboard Hooks. Check this, this, and this for more details.
Edit 2/11/2020
If you just want to check whether a given color at a given point exists in a given image, then you can do:
private void buttonStart_Click(object sender, EventArgs e)
{
var targetColor = ...; //target color.
var targetPoint = ...; //target point.
var sz = Screen.PrimaryScreen.Bounds.Size;
using (var b = new Bitmap(sz.Width, sz.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(b))
{
g.CopyFromScreen(Point.Empty, Point.Empty, b.Size, CopyPixelOperation.SourceCopy);
var bmpData = b.LockBits(new Rectangle(Point.Empty, sz), ImageLockMode.ReadOnly, b.PixelFormat);
var pixBuff = new byte[bmpData.Stride * bmpData.Height];
Marshal.Copy(bmpData.Scan0, pixBuff, 0, pixBuff.Length);
b.UnlockBits(bmpData);
for (var y = 0; y < b.Height; y++)
for(var x = 0; x < b.Width; x++)
{
var pos = (y * bmpData.Stride) + (x * 4);
var blue = pixBuff[pos];
var green = pixBuff[pos + 1];
var red = pixBuff[pos + 2];
var alpha = pixBuff[pos + 3];
if (Color.FromArgb(alpha, red, green, blue).ToArgb().Equals(targetColor.ToArgb()) &&
new Point(x, y).Equals(targetPoint))
{
//execute you code here..
MessageBox.Show("The given color exists at the given point.");
return;
}
}
}
MessageBox.Show("The given color doesn't exist at the given point.");
}
If you want to get a list of all the positions of a given color, then create a new List<Point>() and change the check condition to:
//...
var points = new List<Point>();
if (Color.FromArgb(alpha, red, green, blue).ToArgb().Equals(targetColor.ToArgb()))
{
points.Add(new Point(x, y));
}

Second timer.Start() isn't being triggered by first timer tick

So I have a small logo in the lower right corner of a Form that I want to fade in and out at a preset speed, about 6 seconds per fade. I have tried a few different methods but I can never get the picture to fade back in again once the first timer has finished. Here's my code for the the 2 timers and their respective tick methods.
EDIT The declarations for the timers included now.
Timer fade = new Timer();
Timer fade2 = new Timer();
fade.Interval = (200);
fade.Tick += new EventHandler(fade_Tick);
fade2.Interval = (200);
fade2.Tick += new EventHandler(fade_Tick_Two);
fade.Start();
private void fade_Tick(object sender, EventArgs e)
{
if (alpha < 255)
{
image = picboxPic.Image;
using (Graphics g = Graphics.FromImage(image))
{
Pen pen = new Pen(Color.FromArgb(alpha, 255, 255, 255), image.Width);
g.DrawLine(pen, -1, -1, image.Width, image.Height);
g.Save();
}
picboxPic.Image = image;
this.Invalidate();
alpha++;
}
else
{
fade.Stop();
fade2.Start();
}
}
private void fade_Tick_Two(object sender, EventArgs e)
{
if (alpha > 0)
{
image = picboxPic.Image;
using (Graphics g = Graphics.FromImage(image))
{
Pen pen = new Pen(Color.FromArgb(alpha, 255, 255, 255), image.Width);
g.DrawLine(pen, -1, -1, image.Width, image.Height);
g.Save();
}
picboxPic.Image = image;
this.Invalidate();
alpha--;
}
else
{
fade2.Stop();
fade.Start();
}
}
Any ideas as to why the second timer won't start? I've used breakpoints and the alpha level does reach 255 but then it doesn't trigger the second Tick event.
The method described in the link I quoted works for me:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
test();
}
System.Timers.Timer fade = new System.Timers.Timer(50);
System.Timers.Timer fade2 = new System.Timers.Timer(50);
Image originalImage = Image.FromFile(#"D:\kevin\Pictures\odds&Sods\kitchener.jpg");
int alpha = 100;
void test()
{
fade.Elapsed +=new System.Timers.ElapsedEventHandler(fade_Tick);
fade2.Elapsed+=new System.Timers.ElapsedEventHandler(fade_Tick_Two);
fade.Start();
}
delegate void timerDelegate(object sender, EventArgs e);
private void fade_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new timerDelegate(fade_Tick), sender, e);
return;
}
if (alpha >= 0)
{
picboxPic.Image = SetImgOpacity(originalImage, alphaToOpacity(alpha));
picboxPic.Invalidate();
alpha--;
}
if (alpha < 0)
{
alpha = 0;
fade.Stop();
fade2.Start();
}
}
private void fade_Tick_Two(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new timerDelegate(fade_Tick_Two), sender, e);
return;
}
if (alpha <= 100)
{
picboxPic.Image = SetImgOpacity(originalImage, alphaToOpacity(alpha));
picboxPic.Invalidate();
alpha++;
}
if (alpha > 100)
{
alpha = 100;
fade2.Stop();
fade.Start();
}
}
float alphaToOpacity(int alpha)
{
if (alpha == 0)
return 0.0f;
return (float)alpha / 100.0f;
}
//Setting the opacity of the image
public static Image SetImgOpacity(Image imgPic, float imgOpac)
{
Bitmap bmpPic = new Bitmap(imgPic.Width, imgPic.Height);
Graphics gfxPic = Graphics.FromImage(bmpPic);
ColorMatrix cmxPic = new ColorMatrix();
cmxPic.Matrix33 = imgOpac;
ImageAttributes iaPic = new ImageAttributes();
iaPic.SetColorMatrix(cmxPic, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
gfxPic.DrawImage(imgPic, new Rectangle(0, 0, bmpPic.Width, bmpPic.Height), 0, 0, imgPic.Width, imgPic.Height, GraphicsUnit.Pixel, iaPic);
gfxPic.Dispose();
return bmpPic;
}
}
The code is a bit crude and you will need to take care of the dispose when you close the form, but it fades up and and down and the rest can easily be taken care of - I also made it quicker for testing 'cos life's short :-)

How to check for mouse click whilst tool tip is on c#

in the following image generated by my code,
I want the tooltip to show value for each colour whilst my cursor is on it and when I click a particular place on the image i want a dash line to appear on the image.
this is my code:
RefBar.MouseMove += new MouseEventHandler(RefBar_MouseMove);
RefBar.MouseClick += new MouseEventHandler(RefBar_Click);
private void RefBar_MouseMove(object sender, MouseEventArgs e)
{
if (gotMapFirstTime == true)
{
Point LocalMousePosition = RefBar.PointToClient(System.Windows.Forms.Cursor.Position);
MousePointDisplay.SetToolTip(RefBar, WaferMap.getParamValueFromMousePointerXY(LocalMousePosition.X, LocalMousePosition.Y, 1, true).ToString());
}
}
private void RefBar_Click(object sender, EventArgs e)
{
byte[] bytes2;
Image image;
MouseEventArgs me = (MouseEventArgs)e;
Point coordinates = me.Location;
WaferMap.RefBarDashLines.Add(coordinates.Y);
int[] rfd = WaferMap.RefBarDashLines.ToArray();
if (rfd.Length > 2)
{
RefBar.Image.Dispose();
bytes2 = WaferMap.CreateMapReferenceBar(40, 580, 0, 0, 1);
WaferMap.RefBarDashLines = new List<int>();
WaferMap.UpperTrackBarLimit = 0.0;
WaferMap.LowerTrackBarLimit = 0.0;
pictureBox2.Image.Dispose();
bytes2 = WaferMap.CreateGridImage(120, 120, 9, 9, 5);
image = Image.FromFile(WaferMapImage);
pictureBox2.Image = image;
}
else if(rfd.Length == 2)
{
RefBar.Image.Dispose();
bytes2 = WaferMap.CreateMapReferenceBarByClick(40, 580, 0, 0, 1);
pictureBox2.Image.Dispose();
bytes2 = WaferMap.CreateGridImageFilteredByTrackBar(120, 120, 9, 9, 5);
image = Image.FromFile(WaferMapImage);
pictureBox2.Image = image;
}
else
{
RefBar.Image.Dispose();
bytes2 = WaferMap.CreateMapReferenceBarByClick(40, 580, 0, 0, 1);
}
image = Image.FromFile(ReferenceBarImage);
RefBar.Image = image;
MapLowerLimit.Text = coordinates.X.ToString() + " " + coordinates.Y.ToString();
}
in class wafermap we have this:
public static double getParamValueFromMousePointerXY(int x, int y, int boxSize, bool isRefBarOrHistogram)
{
double returnVal = 0.0;
Point UL;
Point BR;
int cellX;
int invertY;
int cellY;
if (isRefBarOrHistogram)
{
invertY = -1*(y - RefBarLength);
return get_YCell_to_ParamValue(invertY, RefBarLength);
}
else
{
foreach (die dd in dieList)
{
cellX = dd.col;
cellY = dd.row;
UL = new Point(boxSize * (cellX + 2), boxSize * (cellY + 4));
BR = new Point((boxSize * (cellX + 2)) + boxSize, (boxSize * (cellY + 4)) + boxSize);
if ((UL.X < x && x <= BR.X) && (UL.Y < y && y <= BR.Y))
{
return dd.ParamValue;
}
}
}
return returnVal;
}
public struct die
{
public int row;
public int col;
public int site;
public string param;
public double ParamValue;
}
the code for the Mouse click event works if the tool tip function is commented off, however when the tooltip function is is invoked for the mouse move function the code doesnt detect or detects after multiple clicks the mouse click event, how do i rectify this?
Your problem is probably that the getParamValueFromMousePointerXY takes so long to execute that your UI thread is blocked from execution any other task, such as handling your clicks.
You can offload the work to a background task and marshal setting the tooltip back to the UI thread:
Task.Run(() => {
string paramValue = WaferMap.getParamValueFromMousePointerXY(LocalMousePosition.X, LocalMousePosition.Y, 1, true).ToString();
MethodInvoker setTooltip = delegate() {
MousePointDisplay.SetToolTip(RefBar, paramValue);
};
RefBar.Invoke(setTooltip);
});
What you basically are doing here is to execute getParamValueFromMousePointerXY in a background task, while you continue to execute SetToolTip in the UI thread.
The only caveat here is that you possibly run a lot of background tasks here that will be in a race condition to set the tool tip. You can prevent that by using a cancellation token. You define a variable for a CancellationTokenSource:
CancellationTokenSource tooltipSource = null;
You can use this cancellation token source to prevent old updates to the tooltip:
tooltipSource?.Cancel();
tooltipSource = new CancellationTokenSource();
Task tooltipTask = new Task((tokenObj) => {
string paramValue = WaferMap.getParamValueFromMousePointerXY(LocalMousePosition.X, LocalMousePosition.Y, 1, true).ToString();
((CancellationToken)tokenObj).ThrowIfCancellationRequested();
MethodInvoker setTooltip = delegate() {
MousePointDisplay.SetToolTip(RefBar, paramValue);
};
RefBar.Invoke(setTooltip);
}, tooltipSource.Token);
tooltipTask.Start();
With this you should reduce the number of updates to your tooltip.
Of course you can pass the CancellationToken to getParamValueFromMousePointerXY and cancel the task even earlier.

Is there a way to host a DirectX12 application inside a WPF window?

I know the terminology of this question must be all wrong, but please bear with me and try to see things from my layman's point of view (I have no formation in computer technology, I'm a self taught enthusiast. The closest I get from a formal education in programming language is my school's robotics club).
What I want is to be able to use managed DirectX 12 as the "background" of my application, with a game loop and all. And, if possible, to be able to have WPF controls like a ribbon or a toolbox or a menu around the actual directX game. I've been looking all over the internet and all I find is very old stuff for Windows and DirectX 9.0; i'm hoping there's something new these days.
I tryed the Windows Form approach, which is basically this:
using System;
using System.Windows;
using System.Windows.Interop;
using Microsoft.DirectX.Direct3D;
using DColor = System.Drawing.Color;
public partial class MainWindow : Window
{
Device device;
public MainWindow()
{
InitializeComponent();
initDevice();
}
private void initDevice()
{
try
{
PresentParameters parameters = new PresentParameters();
parameters.Windowed = true;
parameters.SwapEffect = SwapEffect.Discard;
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
device = new Device(0, DeviceType.Hardware, windowHandle, CreateFlags.HardwareVertexProcessing, parameters);
}
catch(Exception e)
{
MessageBox.Show("initDevice threw an Exception\n" + e.Message, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void render()
{
device.Clear(ClearFlags.Target, DColor.LightGreen, 0f, 1);
device.Present();
}
}
No exception is thrown, the window is never rendered at all. The application runs, but the window doesn't show up. I didn't think this would work, because there's no game loop and render doesn't get invoked from anywhere, but I didn't expect the window not even being displayed. If I comment out the line that invokes initDevice(), WPF's blank window is shown normally
Then I that discovered the CompositionTarget.Rendering event gets called once every frame (or tick?), so the handler for this event must be used as the game loop.
and so I tried this:
using System;
using System.Drawing;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Forms.Integration;
using Microsoft.DirectX.Direct3D;
using DColor = System.Drawing.Color;
using System.Windows.Forms;
public partial class MainWindow : Window
{
Device device = null;
MemoryStream stream;
PictureBox display;
WindowsFormsHost host;
public MainWindow()
{
InitializeComponent();
initDevice();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
private void CompositionTarget_Rendering(object sender, EventArgs e)
{
render();
}
private void initDevice()
{
try
{
PresentParameters parameters = new PresentParameters();
parameters.Windowed = true;
parameters.SwapEffect = SwapEffect.Discard;
device = new Device(0, DeviceType.Hardware, display, CreateFlags.HardwareVertexProcessing, parameters);
stream = new MemoryStream();
device.SetRenderTarget(0, new Surface(device, stream, Pool.Managed));
}
catch(Exception e)
{
System.Windows.MessageBox.Show("initDevice threw an Exception\n" + e.Message, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void render()
{
device.Clear(ClearFlags.Target, DColor.LightGreen, 0f, 1);
device.Present();
display.Image = Image.FromStream(stream);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
host = new WindowsFormsHost();
display = new PictureBox();
host.Child = display;
mainGrid.Children.Add(host);
}
}
Still no window is shown, even though the application is running and not crashing.
Finally I tried the same thing but without handling CompositionTarget.Rendering, but using a DispatcherTimer instead, and called render from inside its Tick event handler. Same result: no Window.
Can anyone point me to the right direction?
I know it's an old post but for those who search a solution, there is the one I found.
The solution is based on D3D11Image from the project mentioned by Chuck.
1. On Window_Loaded_Event :
private void Window_Loaded(object sender, RoutedEventArgs e) {
InitDx12();
CreateDx11Stuff();
DxImage.SetPixelSize(1280, 720);
DxImage.WindowOwner = (new System.Windows.Interop.WindowInteropHelper(this)).Handle;
DxImage.OnRender += Render;
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
2. Create Dx11 Stuff :
private void CreateDx11Stuff() {
D3D11Device = SharpDX.Direct3D11.Device.CreateFromDirect3D12(D3D12Device, SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport | SharpDX.Direct3D11.DeviceCreationFlags.Debug, new[] { SharpDX.Direct3D.FeatureLevel.Level_12_1 }, Adatper, CommandQueue);
D3D11On12 = ComObject.QueryInterfaceOrNull<SharpDX.Direct3D11.Device11On12>(D3D11Device.NativePointer);
for(int idx = 0; idx < BackBufferCount; idx++) {
D3D11On12.CreateWrappedResource(BackBuffers[idx], new D3D11ResourceFlags { BindFlags = (int)BindFlags.RenderTarget, CPUAccessFlags = 0, MiscFlags = (int)0x2L, StructureByteStride = 0 }, (int)ResourceStates.RenderTarget, (int)ResourceStates.Present, typeof(Texture2D).GUID, out D3D11BackBuffers[idx]);
}
}
3. CompositionTarget Rendering : is quite simple
private void CompositionTarget_Rendering(object sender, EventArgs e) {
DxImage.RequestRender();
}
4. The render function :
private void Render(IntPtr surface, bool newSurface) {
DoDx12Rendering();
var unk = new ComObject(surface);
var dxgiRes = unk.QueryInterface<SharpDX.DXGI.Resource>();
var tempRes = D3D11Device.OpenSharedResource<SharpDX.Direct3D11.Resource>(dxgiRes.SharedHandle);
var backBuffer = tempRes.QueryInterface<Texture2D>();
var d3d11BackBuffer = D3D11BackBuffers[CurrentFrame];
D3D11On12.AcquireWrappedResources(new[] { d3d11BackBuffer }, 1);
D3D11Device.ImmediateContext.CopyResource(d3d11BackBuffer, backBuffer);
D3D11Device.ImmediateContext.Flush();
D3D11On12.ReleaseWrappedResources(new[] { d3d11BackBuffer }, 1);
}
Bonus
You can also do you rendering without the composition target event.
For this, in the Render callback --> void Render(IntPtr surface, bool newSurface), just store the handle of the surface.
Call DxImage.RequestRender() for this.
Do you render in your render loop and add the D3D11on12 to D3D11 copy at the end.
Note
If you handle the resize event, think to resize the DxImage with DxImage.SetPixelSize then recreate your wrapped resources.
More Explanations
I create the Device like this :
_D3D9Device = new DeviceEx(new Direct3DEx(), 0, DeviceType.Hardware, handle, CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded | CreateFlags.FpuPreserve, new SharpDX.Direct3D9.PresentParameters(1, 1) {
Windowed = true,
SwapEffect = SharpDX.Direct3D9.SwapEffect.Discard,
DeviceWindowHandle = handle,
PresentationInterval = PresentInterval.Immediate
});
_D3D11Device = SharpDX.Direct3D11.Device.CreateFromDirect3D12(Device, DeviceCreationFlags.BgraSupport, new[] { SharpDX.Direct3D.FeatureLevel.Level_12_0 }, null, RenderCommandQueue);
And I create the Dx11 and Dx9 FBOs like that :
private void CreateWPFInteropFBO()
{
var desc = new Texture2DDescription {
ArraySize = 1,
BindFlags = BindFlags.RenderTarget,
Format = SharpDX.DXGI.Format.B8G8R8A8_UNorm,
Height = RenderTargetSize.Height,
Width = RenderTargetSize.Width,
MipLevels = 1,
OptionFlags = ResourceOptionFlags.Shared,
SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0),
Usage = ResourceUsage.Default
};
Dx11Texture?.Dispose();
Dx11Texture = new Texture2D(_D3D11Device, desc);
var ptr = Dx11Texture.NativePointer;
var comobj = new ComObject(ptr);
using (var dxgiRes = comobj.QueryInterface<SharpDX.DXGI.Resource>()) {
var sharedHandle = dxgiRes.SharedHandle;
var texture = new Texture(_D3D9Device, desc.Width, desc.Height, 1, SharpDX.Direct3D9.Usage.RenderTarget, SharpDX.Direct3D9.Format.A8R8G8B8, Pool.Default, ref sharedHandle);
Dx9Surface?.Dispose();
Dx9Surface = texture.GetSurfaceLevel(0);
}
}
In fact they are the sames.
Then, after rendering I copy my Dx12 RenderTarget to my Dx11 RenderTarget.
var ptr = GetDx12ResourceFromHandle(Resources.Dx11Texture.NativePointer);
commandList.CopyResource(ptr, Resources.RenderTarget);
In my RenderLoop I update the BackBuffer like this :
private async void UpdateDx9Image()
{
if (Application.Current == null) return;
await Application.Current?.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
{
if (DxImage.TryLock(new Duration(new TimeSpan(0, 0, 0, 0, 16))))
{
DxImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _Renderer.Resources.Dx9Surface.NativePointer, false);
DxImage.AddDirtyRect(new Int32Rect(0, 0, _Renderer.Resources.Dx9Surface.Description.Width, _Renderer.Resources.Dx9Surface.Description.Height));
}
DxImage.Unlock();
}));
}
This project should help. It currently only supports Direct3D 11, but the principles are the same with DirectX 12.
That said, why do you need DirectX 12 instead of just sticking with DirectX 11? The answer should be something more technical than "12 is bigger than 11."

Can't Stop The DispacherTimer

I am writting some code in a wpf and I have a mouse that performs a click if the cursor stands still for a few seconds..I want to stop clicking if I open a new wpf window that i created...But it seems that dispachers doesn't stop, even if I tried almost everything...Is there any way??
public DispatcherTimer NewDispacher = new DispatcherTimer();
public DispatcherTimer NewDispacher2 = new DispatcherTimer();
public void CreateDispachers()
{
NewDispacher.Tick += new EventHandler(NewDispacher_Tick);
NewDispacher.Interval = new TimeSpan(0, 0, 0, 0, 10);
NewDispacher.Start();
NewDispacher2.Tick += new EventHandler(NewDispacher2_Tick);
NewDispacher2.Interval = new TimeSpan(0, 0, 0, 4);
NewDispacher2.Start();
}
public void NewDispacher_Tick(object sender, EventArgs e)
{
pointcur = GetCursorPosition();
}
public void NewDispacher2_Tick(object sender, EventArgs e)
{
pointdiff = GetCursorPosition();
if(form1opened==true)
{
NewDispacher.Stop();
NewDispacher = null;
NewDispacher2.Stop();
NewDispacher2 = null;
}
else if ((pointdiff.X >= pointcur.X - 5)
&& (pointdiff.X <= pointcur.X + 5)
&& (pointdiff.Y >= pointcur.Y - 5)
&& (pointdiff.Y <= pointdiff.Y + 5))
{
DoMouseClick();
pointcur.X = 0;
}
}
I make the bool Form1opened=true when the new Form is opened, but even if it gets into the if, dispachers doesn't stop...
Thanks in advance..
The fact is, Stop() *emphasized text*does*emphasized text* stop a DispatcherTimer in its tracks. So there must be an explanation elsewhere in your code. Perhaps you are restarting the timer somehow? Check when the code can execute that instantiates/starts the timers, in case it's getting called again unintentionally.

Categories