I am trying to create a map editor based on WPF. Currently I'm using a hack to render DirectX contents. I created a WinFormsHost and rendered on a WinForms-Panel.
This all because DirectX (I´m using DirectX 11 with Featurelevel 10) wants a Handle (alias IntPtr) where to render. I don´t know how I can initialize and use the DX Device without a handle.
But a WPF control has no handle. So I just found out, there is an interop class called "D3DImage". But I don't understand how to use it.
My current system works like this:
The inner loop goes through a list of "IGameloopElement"s. For each, it renders its content calling "Draw()". After that, it calls "Present()" of the swap chain to show the changes. Then it resets the device to switch the handle to the next element (mostly there is only one element).
Now, because D3DImage doesn't have a handle, how do I render onto it? I just know I have to use "Lock()" then "SetBackBuffer()", "AddDirtyRect()" and then "Unlock()".
But how do I render onto a DirectX11.Texture2D object without specifying a handle for the device?
I´m really lost... I just found the "DirectX 4 WPF" sample on codeplex, but this implements all versions of DirectX, manages the device itself and has such a huge overhead.
I want to stay at my current system. I´m managing the device by myself. I don´t want the WPF control to handle it.
The loop just should call "Render()" and then passes the backbuffer texture to the WPF control.
Could anyone tell me how to do this? I´m totally stuck ...
Thanks a lot :)
R
WPF's D3DImage only supports Direct3D9/Direct3D9Ex, it does not support Direct3D 11. You need to use DXGI Surface Sharing to make it work.
Another answer wrote, "D3DImage only supports Direct3D9/Direct3D9Ex"... which is perhaps not entirely true for the last few years anyway. As I summarized in a comment here, the key appears to be that Direct3D11 with DXGI has a very specific interop compatibility mode (D3D11_SHARED_WITHOUT_MUTEX flag) which makes the ID3D11Texture2D1 directly usable as a D3DResourceType.IDirect3DSurface9, without copying any bits, which just so happens to be exactly (and only) what WPF D3DImage is willing to accept.
This is a rough sketch of what worked for me, to create a D3D11 SampleAllocator that produces ID3D11Texture2D1 that are directly compatible with WPF's Direct3D9. Because all the .NET interop shown here is of my own design, this will not be totally ready-to-run code to drop in your project, but the method, intent, and procedures should be clear for easy adaptation.
1. preliminary helper
static D3D_FEATURE_LEVEL[] levels =
{
D3D_FEATURE_LEVEL._11_1,
D3D_FEATURE_LEVEL._11_0,
};
static IMFAttributes GetSampleAllocatorAttribs()
{
MF.CreateAttributes(out IMFAttributes attr, 6);
attr.SetUINT32(in MF_SA_D3D11_AWARE, 1U);
attr.SetUINT32(in MF_SA_D3D11_BINDFLAGS, (uint)D3D11_BIND.RENDER_TARGET);
attr.SetUINT32(in MF_SA_D3D11_USAGE, (uint)D3D11_USAGE.DEFAULT);
attr.SetUINT32(in MF_SA_D3D11_SHARED_WITHOUT_MUTEX, (uint)BOOL.TRUE);
attr.SetUINT32(in MF_SA_BUFFERS_PER_SAMPLE, 1U);
return attr;
}
static IMFMediaType GetMediaType()
{
MF.CreateMediaType(out IMFMediaType mt);
mt.SetUINT64(in MF_MT_FRAME_SIZE, new SIZEU(1920, 1080).ToSwap64());
mt.SetGUID(in MF_MT_MAJOR_TYPE, in WMMEDIATYPE.Video);
mt.SetUINT32(in MF_MT_INTERLACE_MODE, (uint)MFVideoInterlaceMode.Progressive);
mt.SetGUID(in MF_MT_SUBTYPE, in MF_VideoFormat.RGB32);
return mt;
}
2. the D3D11 device and context instances go somewhere
ID3D11Device4 m_d3D11_device;
ID3D11DeviceContext2 m_d3D11_context;
3. initialization code is next
void InitialSetup()
{
D3D11.CreateDevice(
null,
D3D_DRIVER_TYPE.HARDWARE,
IntPtr.Zero,
D3D11_CREATE_DEVICE.BGRA_SUPPORT,
levels,
levels.Length,
D3D11.SDK_VERSION,
out m_d3D11_device,
out D3D_FEATURE_LEVEL _,
out m_d3D11_context);
MF.CreateDXGIDeviceManager(out uint tok, out IMFDXGIDeviceManager m_dxgi);
m_dxgi.ResetDevice(m_d3D11_device, tok);
MF.CreateVideoSampleAllocatorEx(
ref REFGUID<IMFVideoSampleAllocatorEx>.GUID,
out IMFVideoSampleAllocatorEx sa);
sa.SetDirectXManager(m_dxgi);
sa.InitializeSampleAllocatorEx(
PrerollSampleSink.QueueMax,
PrerollSampleSink.QueueMax * 2,
GetSampleAllocatorAttribs(),
GetMediaType());
}
4. use sample allocator to repeatedly generate textures, as needed
ID3D11Texture2D1 CreateTexture2D(SIZEU sz)
{
var vp = new D3D11_VIEWPORT
{
TopLeftX = 0f,
TopLeftY = 0f,
Width = sz.Width,
Height = sz.Height,
MinDepth = 0f,
MaxDepth = 1f,
};
m_d3D11_context.RSSetViewports(1, ref vp);
var desc = new D3D11_TEXTURE2D_DESC1
{
SIZEU = sz,
MipLevels = 1,
ArraySize = 1,
Format = DXGI_FORMAT.B8G8R8X8_UNORM,
SampleDesc = new DXGI_SAMPLE_DESC { Count = 1, Quality = 0 },
Usage = D3D11_USAGE.DEFAULT,
BindFlags = D3D11_BIND.RENDER_TARGET | D3D11_BIND.SHADER_RESOURCE,
CPUAccessFlags = D3D11_CPU_ACCESS.NOT_REQUESTED,
MiscFlags = D3D11_RESOURCE_MISC.SHARED,
TextureLayout = D3D11_TEXTURE_LAYOUT.UNDEFINED,
};
m_d3D11_device.CreateTexture2D1(ref desc, IntPtr.Zero, out ID3D11Texture2D1 tex2D);
return tex2D;
}
Related
I am looking for the normal way (that is, the way Microsoft thought about when creating its API) to load standard icons for commands in a pure C program, like the floppy disc image for saving, etc.
The Internet doesn't seem to contain the answer. I know the toolbars of commctl32 can receive a TB_LOADIMAGES message that fills an image list with standard icons, but what if I don't need toolbars in my app? I want icons in another control, let's say on an owner-draw button, or a menu item. Is there a way to fill the image list without creating a useless toolbar? Or even better, getting HICON or HBITMAP handles to avoid the commctl32 dependency?
The loud silence about it in Microsoft's docs, and on the web, makes me think there is no such feature. Maybe when an application is developed with Microsoft tools, the application creator injects as resources in the executable files the default icons the developer chooses in a predefined list? I cannot check it because I only use Unix-like compilers, can somebody confirm that and/or tell me more about default icon usage under Windows?
Also, would it be reliable to assume we can load libraries like shell32.dll to use its embedded icons (like all the good old functions that are available since an eternity in kernel32.dll, `user32.dll, ...)? I mean, is there a guarantee that icons in libraries will keep their semantics and indexes in future versions of Windows?
Coming some days later to post the final solution I opted for, just in case somebody would need to use the same. The most normal way to show command icons is to embed them in the application, so despite being unhappy with the creation of a dummy control, I use a temporary toolbar to load the icons in global image lists. This would unlikely fail, but since icons may sometimes be the only thing that indicates to the user what a widget does, I reinforced the conditions with the ability to run a fallback code when something is wrong.
The function that loads the icons is named WIN32_initIcons(), because it is supposed to be implemented alongside with something like a GTK_initIcons() or X11_initIcons() counterpart for Unix-like platforms.
HIMAGELIST WIN32_imglst16 = NULL;
HIMAGELIST WIN32_imglst24 = NULL;
bool WIN32_initIcons(void) {
bool def16 = false;
bool def32 = false;
HWND hWnd;
WIN32_imglst16 = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 80, 16);
WIN32_imglst32 = ImageList_Create(24, 24, ILC_COLOR32 | ILC_MASK, 80, 16);
if (hWnd = CreateWindowA("ToolbarWindow32", NULL, TBSTYLE_LIST, 0, 0, 0, 0, NULL, (HMENU)0, GetModuleHandle(NULL), NULL)) {
if (WIN32_imglst16) {
SendMessage(hWnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)WIN32_imglst16);
if (!SendMessage(hWnd, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL)) def16 = true;
if (SendMessage(hWnd, TB_LOADIMAGES, (WPARAM)IDB_VIEW_SMALL_COLOR, (LPARAM)HINST_COMMCTRL) != 15) def16 = false;
if (SendMessage(hWnd, TB_LOADIMAGES, (WPARAM)IDB_HIST_SMALL_COLOR, (LPARAM)HINST_COMMCTRL) != 27) def16 = false;
if (ImageList_GetImageCount(WIN32_imglst16) != 32) def16 = false;
}
if (WIN32_imglst24) {
SendMessage(hWnd, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)WIN32_imglst24);
if (!SendMessage(hWnd, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL)) def24 = true;
if (SendMessage(hWnd, TB_LOADIMAGES, (WPARAM)IDB_VIEW_SMALL_COLOR, (LPARAM)HINST_COMMCTRL) != 15) def24 = false;
if (SendMessage(hWnd, TB_LOADIMAGES, (WPARAM)IDB_HIST_SMALL_COLOR, (LPARAM)HINST_COMMCTRL) != 27) def24 = false;
if (ImageList_GetImageCount(WIN32_imglst24) != 32) def24 = false;
}
DestroyWindow(hWnd);
}
if (WIN32_imglst16 && !def16) {
// Load your custom 16x16 fallback icons in the list "WIN32_imglst16" here
// Don't forget to set "def16 = true" on success
}
if (WIN32_imglst24 && !def24) {
// Load your custom 24x24 fallback icons in the list "WIN32_imglst24" here
// Don't forget to set "def24 = true" on success
}
if (!def16) {
// Trigger your error management here because 16x16 icons cannot be loaded
// Maybe, call "ImageList_Destroy(WIN32_imglst16)" and set "WIN32_imglst16 = NULL"
}
if (!def24) {
// Trigger your error management here because 24x24 icons cannot be loaded
// Maybe, call "ImageList_Destroy(WIN32_imglst24)" and set "WIN32_imglst24 = NULL"
}
return (def16 && def24);
}
Note: In simple C, use <stdbool.h> to define the bool value.
I am trying to develop a software based on Kinect v2 and I need to keep the capturedframes in an array. I have a problem and I dont have any idea about it as follow.
The captured frames are processed by my processing class and the processed writable bitmap will be called as the source of the image box in my ui window which works perfectly and I have a realtime frames in my ui.
for example:
/// Color
_ProcessingInstance.ProcessColor(colorFrame);
ImageBoxRGB.Source = _ProcessingInstance.colorBitmap;
but when I want to assign this to an element of an array, all of the elements in array will be identical as the first frame!! I should mention that, this action is in the reading event which above action is there.
the code:
ColorFrames_Array[CapturingFrameCounter] = _ProcessingInstance.colorBitmap;
the equal check in intermediate window:
ColorFrames_Array[0].Equals(ColorFrames_Array[1])
true
ColorFrames_Array[0].Equals(ColorFrames_Array[2])
true
Please give me some hints about this problem. Any idea?
Thanks Yar
You are right and when I create a new instance, frames are saved correctly.
But my code was based on the Microsoft example and problem is that creating new instances makes the memory leakage because writablebitmap is not disposable.
similar problem is discussed in the following link which the frames are frizzed to the first frame and this is from the intrinsic properties of writeablebitmap:
http://www.wintellect.com/devcenter/jprosise/silverlight-s-big-image-problem-and-what-you-can-do-about-it
Therefore i use a strategy similar to the above solution and try to get a copy instead of the original bitmap frame. In this scenario, I have create a new writeblebitmap for each element of ColorFrames_Array[] at initialization step.
ColorFrames_Array = new riteableBitmap[MaximumFramesNumbers_Capturing];
for (int i=0; i < MaximumFramesNumbers_Capturing; ++i)
{
ColorFrames_Array[i] = new WriteableBitmap(color_width, color_height, 96.0, 96.0, PixelFormats.Bgr32, null);
}
and finally, use clone method to copy the bitmap frames to array elements.
ColorFrames_ArrayBuffer[CapturingFrameCounter] = _ProcessingInstance.colorBitmap.Clone();
While above solution works, but it has a huge memory leakage!!.
Therefore I use Array and .copypixel methods (of writeablebitmap) to copy the pixels of the frame to array and hold it (while the corresponding writeablebitmap will be disposed correctly without leakage).
public Array[] ColorPixels_Array;
for (int i=0; i< MaximumFramesNumbers_Capturing; ++i)
{
ColorPixels_Array[i]=new int[color_Width * color_Height];
}
colorBitmap.CopyPixels(ColorPixels_Array[Counter_CapturingFrame], color_Width * 4, 0);
Finally, when we want to save the arrays of pixels, we need to convert them new writeablebitmap instances and write them on hard.
wb = new WriteableBitmap(color_Width, color_Height, 96.0, 96.0, PixelFormats.Bgr32, null);
wb.WritePixels(new Int32Rect(0, 0, color_Width, color_Height)
, Ar_Px,
color_Width * 4, 0);
I have started working with DirectX in WPF app. My first step was to use simple library:
SharpdDX.WPF. Based on samples I've implemented WPF control drawing simple line. SharpDX.WPF uses D3DImage to render images in WPF.
Unfortunately application's memory increasing all time.
I implemented class TestControlRenderer : D3D10.
Vertex shader is initialized like:
var sizeInBytes = dataLength * sizeof(int) * 3;
var bufferDescription = new BufferDescription(
sizeInBytes,
ResourceUsage.Dynamic,
BindFlags.VertexBuffer,
CpuAccessFlags.Write,
ResourceOptionFlags.None);
using (var stream = new DataStream(sizeInBytes, true, true))
{
stream.Position = 0;
_graphDataVertexBuffer = new SharpDX.Direct3D10.Buffer(Device, stream, bufferDescription);
}
Device.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(_graphDataVertexBuffer, sizeof(int) * 3, 0));
Device.InputAssembler.PrimitiveTopology = PrimitiveTopology.LineStrip;
Then constant buffer with parameters used in shader:
_controlInfoConstantBuffer = new ConstantBuffer<ControlParamsShaderData>(Device);
Device.VertexShader.SetConstantBuffer(0, _controlInfoConstantBuffer.Buffer);
To init animation Reset method was overriden like that:
base.Reset(args);
if (args.RenderSize.Width == 0) return;
_drawArgs = args;
InitVertexBuffer(dataLength);
_controlInfoConstantBuffer.Value = new ControlParamsShaderData
{
SamplesInControl = dataLength,
MinSignalDataY = -1500,
MaxSignalDataY = 1500
};
Device.VertexShader.SetConstantBuffer(0, _controlInfoConstantBuffer.Buffer);
The last step is RenderScene method:
public override void RenderScene(DrawEventArgs args)
{
if (args.RenderSize.Width == 0) return;
Device.ClearRenderTargetView(RenderTargetView, Color.Transparent);
using (var stream = _graphDataVertexBuffer.Map(MapMode.WriteDiscard, SharpDX.Direct3D10.MapFlags.None))
{
for (int i = 0; i < Data.Length; i++)
{
stream.Write(new Vector3(i, Data[i], 0));
}
}
_graphDataVertexBuffer.Unmap();
Device.Draw(Data.Length, 0);
}
Rendering is controlled by DispatcherTimer where OnTickMethod updates array with points coordinates and then invoke Render() method.
My question is simply, is that memory leak or something is created on each render iteration?
I don't change backbuffer or create another objects. Only change Data array, update it to GPU and Shaders process it to display.
My case is to display about 30 wpf controls width DirectX on one screen. Controls are with simple but realtime animation. Is that possible in that way?
Most likely you are leaking resources. You can see this by setting the static configuration property
SharpDX.Configuration.EnableObjectTracking = true;
then calling
SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects()
at various points in your application lifetime to see if anything is leaking (at least on the SharpDX side). You can edit your code to make sure to dispose these objects. Only enable object tracking while debugging - it hurts performance.
SharpDX used to release COM objects when the finalizer ran if the object had not already been Diposed (at least as in version 2.4.2), but later disabled that (they detail why in one of their changelogs, I forget which one).
Additionally, DirectX requires that you release objects in the reverse order they were created - this can create hard-to-debug memory leaks. So when your code is
var device = new Devie(...);
var effect = new Effec(Device, byteCode);
technique = effect.GetTechniqueByName(techniqueName);
inputLayout = new InputLayout(Device, _technique.GetPassByIndex(0).Description.Signature, ...);
then your dispose code has to be
_inputLayout.Dispose();
_technique.Dispose();
_effect.Dispose();
_device.Dispose();
A few months ago I built some online samples like this one from Jeff Prosise that use the WriteableBitmap class in Silverlight.
Revisiting them today with the latest Silverlight3 installer (3.0.40624.0), the API seems to have changed.
I figured out some of the changes. For example, the WriteableBitmap array accessor has disappeared, but I found it in the new Pixels property, so instead of writing:
bmp[x]
I can write
bmp.Pixels[x]
Are there similar simple replacements for these calls, or has the use pattern itself changed?
bmp = new WriteableBitmap(width, height, PixelFormats.Bgr32);
bmp.Lock();
bmp.Unlock();
Can anybody point me to a working example using the updated API?
Another important detail about switching to the new WriteableBitmap is given in this answer ... because the pixel format is now always pbgra32, you must set an alpha value for each pixel, otherwise you just get an all-white picture. In other words, code that formerly generated pixel values like this:
byte[] components = new byte[4];
components[0] = (byte)(blue % 256); // blue
components[1] = (byte)(grn % 256); // green
components[2] = (byte)(red % 256); // red
components[3] = 0; // unused
should be changed to read:
byte[] components = new byte[4];
components[0] = (byte)(blue % 256); // blue
components[1] = (byte)(grn % 256); // green
components[2] = (byte)(red % 256); // red
components[3] = 255; // alpha
What happens if you don't use Lock and Unlock and just use the WritabelBitmap(int, int) constructor? Do things break?
It would seem that between SL3 Beta and the release this API has changed. See Breaking Changes Document Errata (Silverlight 3)
Anyone know of a way to reliably take a snapshot of a WPF window? The PrintWindow api works well for "standard" win32 windows but since WPF uses DirectX, PrintWindow fails to capture an image. I think that one would need to grab the front buffer for the DirectX object associated with the window, but I am not sure how to do that.
Thanks!
I'm not sure if this is what you mean, and I'm not sure I'm allowed to link to my blog or not, but is this any use? It basically uses a RenderTargetBitmap to generate a JPG. You can use it to "screenshot" an entire window then print that.
If this is against the rules, someone feel free to delete :)
This Method should help you print the entire WPF / XAML Window
private void PrintWindow(PrintDialog pdPrint,
System.Windows.Window wWin,
string sTitle,
System.Windows.Thickness? thMargin)
{
Grid drawing_area = new Grid();
drawing_area.Width = pdPrint.PrintableAreaWidth;
drawing_area.Height = pdPrint.PrintableAreaHeight;
Viewbox view_box = new Viewbox();
drawing_area.Children.Add(view_box);
view_box.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
view_box.VerticalAlignment = System.Windows.VerticalAlignment.Center;
if (thMargin == null)
{
view_box.Stretch = System.Windows.Media.Stretch.None;
}
else
{
view_box.Margin = thMargin.Value;
view_box.Stretch = System.Windows.Media.Stretch.Uniform;
}
VisualBrush vis_br = new VisualBrush(wWin);
System.Windows.Shapes.Rectangle win_rect = new System.Windows.Shapes.Rectangle();
view_box.Child = win_rect;
win_rect.Width = wWin.Width;
win_rect.Height = wWin.Height;
win_rect.Fill = vis_br;
win_rect.Stroke = System.Windows.Media.Brushes.Black;
win_rect.BitmapEffect = new System.Windows.Media.Effects.DropShadowBitmapEffect();
// Arrange to produce output.
Rect rect = new Rect(0, 0, pdPrint.PrintableAreaWidth, pdPrint.PrintableAreaHeight);
drawing_area.Arrange(rect);
// Print it.
pdPrint.PrintVisual(drawing_area, sTitle);
}
Regards Sean Campbell
You can use the PrintDialog.PrintVisual() method.
MSDN Link: http://msdn.microsoft.com/en-us/library/system.windows.controls.printdialog.printvisual.aspx
A sample: http://www.thejoyofcode.com/Reason_9._Printing.aspx