Related
I'm trying to write an application that takes a screen shot of an area on one of the monitors and saves it to an image. The Screen shot should be taken at a x,y coordinate with a given width and height. When the image saves it should be saved at 0,0 and the same width height. I have this working on my main monitor but when I attempt to take the screen shot of my 2nd or 3rd monitor it doesnt work. I either get a black image or a file that when opened says its an invalid format.
My code is a modified version of the following code that takes a screen shot of the entire desktop.
https://github.com/GERD0GDU/dxgi_desktop_capture
Main Monitor width/height
2560 X 1440
Main monitor top corner x/y coordinates are 0,0 retrieved by calling
POINT cursorPos;
GetCursorPos(&cursorPos);
Secondary Monitor width/height
1080 x 1920
Secondary monitor top corner x/y coordinates are 2560,-238
For reference for my image coordinates that work on the main monitor i'm passing in x:801 y:227, width:1756, height:1115
For my 2nd monitor which sits to the right of my main monitor and is flipped sideways making it taller than wide I'm passing in x:2907 y:-11 width:737 height:1595
HRESULT CDXGICapture::CaptureToFile(_In_ LPCWSTR lpcwOutputFileName, int x, int y, int width, int height)
{
AUTOLOCK();
if (!m_bInitialized) {
return D2DERR_NOT_INITIALIZED;
}
CHECK_POINTER_EX(m_ipDxgiOutputDuplication, E_INVALIDARG);
CHECK_POINTER_EX(lpcwOutputFileName, E_INVALIDARG);
HRESULT hr = S_OK;
hr = DXGICaptureHelper::IsRendererInfoValid(&m_rendererInfo);
if (FAILED(hr)) {
return hr;
}
// is valid?
hr = DXGICaptureHelper::GetContainerFormatByFileName(lpcwOutputFileName);
if (FAILED(hr)) {
return hr;
}
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
CComPtr<IDXGIResource> ipDesktopResource;
CComPtr<ID3D11Texture2D> ipAcquiredDesktopImage;
CComPtr<ID2D1Bitmap> ipD2D1SourceBitmap;
// Get new frame
int lTryCount = 4;
do
{
Sleep(1);
hr = m_ipDxgiOutputDuplication->AcquireNextFrame(250, &FrameInfo, &ipDesktopResource);
if (SUCCEEDED(hr))
break;
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
continue;
}
else if (FAILED(hr))
break;
} while (--lTryCount > 0);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
return S_FALSE;
}
else if (FAILED(hr))
{
return hr;
}
// QI for ID3D11Texture2D
hr = ipDesktopResource->QueryInterface(IID_PPV_ARGS(&ipAcquiredDesktopImage));
ipDesktopResource = nullptr;
CHECK_HR_RETURN(hr);
if (nullptr == ipAcquiredDesktopImage)
{
// release frame
m_ipDxgiOutputDuplication->ReleaseFrame();
return E_OUTOFMEMORY;
}
// Copy needed full part of desktop image
m_ipD3D11DeviceContext->CopyResource(m_ipCopyTexture2D, ipAcquiredDesktopImage);
// release frame
hr = m_ipDxgiOutputDuplication->ReleaseFrame();
CHECK_HR_RETURN(hr);
// create D2D1 source bitmap
hr = DXGICaptureHelper::CreateBitmap(m_ipD2D1RenderTarget, m_ipCopyTexture2D, &ipD2D1SourceBitmap);
CHECK_HR_RETURN(hr);
//try this
D2D1_RECT_F rcSource = D2D1::RectF(
(FLOAT)x,
(FLOAT)y,
(FLOAT)x + width,
(FLOAT)y+ height);
D2D1_RECT_F rcTarget = D2D1::RectF(
(FLOAT)0,
(FLOAT)0,
(FLOAT)width,
(FLOAT)height);
D2D1_POINT_2F ptTransformCenter = D2D1::Point2F(width / 2.0f, height / 2.0f);
// Apply the rotation transform to the render target.
D2D1::Matrix3x2F rotate = D2D1::Matrix3x2F::Rotation(
m_rendererInfo.RotationDegrees,
ptTransformCenter
);
D2D1::Matrix3x2F scale = D2D1::Matrix3x2F::Scale(
D2D1::SizeF(1, 1),
ptTransformCenter
);
// Priority: first rotate, after scale...
m_ipD2D1RenderTarget->SetTransform(rotate * scale);
m_ipD2D1RenderTarget->BeginDraw();
// clear background color
m_ipD2D1RenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White, 1.0f));
m_ipD2D1RenderTarget->DrawBitmap(ipD2D1SourceBitmap, rcTarget, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, rcSource);
//m_ipD2D1RenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
hr = m_ipD2D1RenderTarget->EndDraw();
if (FAILED(hr)) {
return hr;
}
hr = DXGICaptureHelper::SaveImageToFile(m_ipWICImageFactory, m_ipWICOutputBitmap, lpcwOutputFileName, width, height);
if (FAILED(hr)) {
return hr;
}
return S_OK;
} // CaptureToFile
My save to file method
static
COM_DECLSPEC_NOTHROW
inline
HRESULT
SaveImageToFile(
_In_ IWICImagingFactory* pWICImagingFactory,
_In_ IWICBitmapSource* pWICBitmapSource,
_In_ LPCWSTR lpcwFileName,
_In_ unsigned int uiWidth,
_In_ unsigned int uiHeight
)
{
CHECK_POINTER_EX(pWICImagingFactory, E_INVALIDARG);
CHECK_POINTER_EX(pWICBitmapSource, E_INVALIDARG);
HRESULT hr = S_OK;
GUID guidContainerFormat;
hr = GetContainerFormatByFileName(lpcwFileName, &guidContainerFormat);
if (FAILED(hr)) {
return hr;
}
WICPixelFormatGUID format = GUID_WICPixelFormatDontCare;
CComPtr<IWICImagingFactory> ipWICImagingFactory(pWICImagingFactory);
CComPtr<IWICBitmapSource> ipWICBitmapSource(pWICBitmapSource);
CComPtr<IWICStream> ipStream;
CComPtr<IWICBitmapEncoder> ipEncoder;
CComPtr<IWICBitmapFrameEncode> ipFrameEncode;
// unsigned int uiWidth = 1756;
// unsigned int uiHeight = 1115;
hr = ipWICImagingFactory->CreateStream(&ipStream);
if (SUCCEEDED(hr)) {
hr = ipStream->InitializeFromFilename(lpcwFileName, GENERIC_WRITE);
}
if (SUCCEEDED(hr)) {
hr = ipWICImagingFactory->CreateEncoder(guidContainerFormat, NULL, &ipEncoder);
}
if (SUCCEEDED(hr))
{
hr = ipEncoder->Initialize(ipStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr))
{
hr = ipEncoder->CreateNewFrame(&ipFrameEncode, NULL);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->Initialize(NULL);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->SetSize(uiWidth, uiHeight);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->SetPixelFormat(&format);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->WriteSource(ipWICBitmapSource, NULL);
}
if (SUCCEEDED(hr))
{
hr = ipFrameEncode->Commit();
}
if (SUCCEEDED(hr))
{
hr = ipEncoder->Commit();
}
return hr;
} // SaveImageToFile
The coordinates being passed in needed to be normalized to the monitor by itself without considering the other monitors. IE all x and y values needed to be positive from a 0,0 being the top left of the monitor.
To get the coordinates for a monitor I used the following where the Control is the view I was trying to capture within my application.
Monitor[] monitors = Display.getDefault().getMonitors();
Control control = getScreenCaptureControlArea();
Point p = control.toDisplay(0, 0);
for (Monitor monitor : monitors) {
Rectangle bounds = monitor.getBounds();
if (monitor.getBounds().contains(p.x, p.y)) {
setxCord(Math.abs(Math.abs(bounds.x) - Math.abs(p.x)));
setyCord(Math.abs(Math.abs(bounds.y) - Math.abs(p.y)));
setWidth(control.getSize().x);
setHeight(control.getSize().y);
break;
}
}
Will note that this still doesnt work when the monitor is in Portrait mode as the image saved is incorrect. But seeing as my application isnt meant to be used in a Portrait mode it didnt matter much so i didnt put any more effort into figuring out that problem.
I'm trying to work with both Direct2D and Direct3D. Right now, I'm having Direct2D draw content to a separate Device Context (with an HDC) and then copy the contents of that Device Context into my window. I could show the code I use to set that up in an edit to this post if requested, but before Direct3D gets involved, that part works.
Here is a simplified version of the Window Drawing code I use.
if (d3dEngine.Get()) // Object used to hold Direct3D Resources (.Get() returns a pointer for a null check)
{
// d3dEngine->PrepareScene(D2D1::ColorF(D2D1::ColorF::Wheat));
}
// Drawing Board holds the Direct 2D Render Target
drawingBoard->GetRenderer()->BeginDraw();
drawingBoard->GetRenderer()->Clear(D2D1::ColorF(1.0f,1.0f,1.0f,1.0f));
mainPage->Draw(); // Main Page Holds various objects that draw to Direct2D
if (d3dEngine.Get())
d3dEngine->FinalizeScene();
drawingBoard->GetRenderer()->EndDraw();
// Get the Secondary Device Context that Direct2D draws to
HDC dc = drawingBoard->GetDc();
RECT r{ 0,0,0,0 };
int err = 0;
// Retrieve the Rectangle for the window (currentWindow is the window handle used)
if(!GetClientRect(currentWindow, &r))
err = GetLastError();
// Use the BitBlt function to copy Direct2D content into a window
if (!BitBlt(GetDC(currentWindow), r.left, r.top, r.right - r.left, r.bottom - r.top, dc, 0, 0, SRCCOPY))
err = GetLastError();
Before any Direct3D resources are created (and the d3dEngine.Get() call returns null), this code runs to my satisfaction.
However, following the creation of Direct3D resources, the code fails:
RECT r{ 0,0,0,0 };
int err = 0;
// Retrieve the Rectangle for the window (currentWindow is the window handle used)
if(!GetClientRect(currentWindow, &r))
err = GetLastError();
The window handle currentWindow becomes invalid as GetLastError() returns 1400 after the call to GetClientRect. I suspect that the Swap Chain in Direct3D 11 may play a role due to the following code used to activate Direct3D.
GetClientRect(window, &Location);
unsigned int width = Location.right - Location.left,
height = Location.bottom - Location.top;
D3D_DRIVER_TYPE dTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE//, D3D_DRIVER_TYPE_WARP
};
int tTypes = ARRAYSIZE(dTypes);
D3D_FEATURE_LEVEL dLevels[] =
{
D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0
};
int tLevels = ARRAYSIZE(dLevels);
DXGI_SWAP_CHAIN_DESC swapChainDescription;
// Initialize the swap cahin
swapChainDescription.BufferCount = 2;
swapChainDescription.BufferDesc.Width = Location.right - Location.left;
swapChainDescription.BufferDesc.Height = Location.bottom - Location.top;
swapChainDescription.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDescription.BufferDesc.RefreshRate.Numerator = 30;
swapChainDescription.BufferDesc.RefreshRate.Denominator = 1;
swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDescription.OutputWindow = window;
swapChainDescription.Windowed = true;
swapChainDescription.SampleDesc.Count = 1;
swapChainDescription.SampleDesc.Quality = 0;
swapChainDescription.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapChainDescription.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDescription.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;
swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;// DXGI_SWAP_EFFECT_DISCARD;
unsigned int flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
/// Other initialization Code
HRESULT results = 0;
// Initialize ID3D11Device "graphicsDevice"
IDXGISwapChain* sc = nullptr;
results = dxFact->CreateSwapChain(graphicsDevice, &swapChainDescription, &sc);
This code is in a different class than the Window drawing code towards the beginning.
The window variable in the Direct3D code holds the same value that currentWindow holds in the Window Code.
Is there anyone who could provide insight as to what is going on and why the window handle stops working? And perhaps suggest a workaround?
While I'm still unsure why the window handle seemed to be useless when I checked, I was able to develop a workaround.
Basically, I have my Window class hold a RECT indicating the size of the Window and I use that instead of getting the RECT from the Window every time.
RECT r{ 0,0,0,0 };
int err = 0;
// Retrieve the Rectangle for the window (currentWindow is the window handle used)
if(!GetClientRect(currentWindow, &r))
err = GetLastError();
// Use the BitBlt function to copy Direct2D content into a window
if (!BitBlt(GetDC(currentWindow), r.left, r.top, r.right - r.left, r.bottom - r.top, dc, 0, 0, SRCCOPY))
err = GetLastError();
// Now, it uses a 'size' attribute
int err = 0;
if (!BitBlt(GetTWindowDc(), size.left, size.top, size.right - size.left, size.bottom - size.top, dc, 0, 0, SRCCOPY))
err = GetLastError();
When it comes to getting the Window Device Context, the new GetTWindowDc() method takes care of that:
if (d3dEngine.Get())
return d3dEngine->GetDC();
return GetDC(currentWindow);
Basically, if Direct3D is activated, the 3D manager object (which holds a DXGISurface) retrieves the HDC from Direct3D to use.
HDC TWindowEngine::GetDC()
{
if (!surface.Get()) // My Unique Smart pointer to a DXGISurface1 object
return 0;
HDC dc;
assert(SUCCEEDED(surface->GetDC(FALSE, &dc)));
return dc;
}
According to Surface-GetDc Documentation, you need to call the corresponding ReleaseDc on the DXGISurface1 object. So this is how I do it.
In the Window Draw code:
if (!BitBlt(GetTWindowDc(), size.left, size.top, size.right - size.left, size.bottom - size.top, dc, 0, 0, SRCCOPY))
err = GetLastError();
FlushDc();
Here is the FlushDc method:
void TWindow::FlushDc()
{
if (d3dEngine.Get())
d3dEngine->ClearDC();
}
And the ClearDC method is impelented.
void TWindowEngine::ClearDC()
{
if (surface.Get())
surface->ReleaseDC(nullptr);
}
I've made quite a bit of progress since the last time I asked a question. I've got a really cool build now with a terrain mesh with mountains and lakes:
LakeAndMountains
I made a release build, debugged it, and now it works just fine on my computer. I can take the application folder and move it around on my computer and run the .exe file with no issues. However, when I copy the application directory over to my other computer (and my friend's computer), it fails during the D3D11CreateDeviceAndSwapChain step with an HR of S_False.
For both my other computer and my friend's computer, it triggers only the if(failed(hr)) after the create function is called, and it says dev/devcon/swapchain are all NULL. Because they are all null, when it hits the function from swapchain, it crashes with an access violation error.
I searched for a few days looking for solutions, but I have yet to see one that had the S_FALSE occur. I tried some of the more straightforward answers but got nothing. All the computers are running 64bit on 64 bit OS, all have DirectX12, all are high end gaming PCs, my other computer actually has VS2017CE as well set up for c++ game programming, but they both still fail on that line. I'd really appreciate it if anyone could give me some guidance on where to begin deciphering why a release build works on my computer but not on anyone else's computer. Here is the code where it gets initialized, as well as the code for the window creation (code is excerpted to just the relevant parts of the functions):
WinMain:
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//MessageBox(NULL, "Start of WinMain", "WinMain", 0);
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
//Screen size variables
long screenWidth = 1280;
long screenHeight = 720;
UINT screenWidthUINT = 1280;
UINT screenHeightUINT = 720;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
//wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = "WindowClass1";
// register the window class
RegisterClassEx(&wc);
RECT wr = { 0, 0, screenWidth, screenHeight }; // set the size, but not the position
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // adjust the size
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
"WindowClass1",
"Game",
WS_OVERLAPPEDWINDOW,
0, // x-position of the window
0, // y-position of the window
wr.right - wr.left, // width of the window
wr.bottom - wr.top, // height of the window
NULL,
NULL,
hInstance,
NULL);
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
//MessageBox(NULL, "called ShowWindow", "WinMain", 0);
/*****************************************
// GAME ENGINE INITIALIZATION
******************************************/
//Need to initialize the graphics engine. This passes the window pointer through the
//game engine to the D3D engine so it knows what window to look at.
//MessageBox(NULL, "Calling InitGraphics", "WinMain", 0);
InitGraphics(hWnd);
InitGraphics:
//DEVICE CREATION
#include Windows.h
#include <windowsx.h>
#include <d3d11.h>
#include <DirectXMath.h>
#include <D3Dcompiler.h>
#include <dxgi.h>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3dcompiler.lib")
#include "DirectXDeviceEngine.h"
using namespace DirectX;
InitGraphics(HWND hWnd)
{
//call at start to initialize D3DX
if (hWnd == NULL)
{
MessageBox(NULL, "hWnd was NULL", "DirectXDeviceEngine", 0);
}
HRESULT hr;
//initialize swap chain
//Describe our Buffer
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = winWidth;
bufferDesc.Height = winHeight;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
// create a struct to hold information about the swap chain
DXGI_SWAP_CHAIN_DESC scd;
// clear out the struct for use
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
// fill the swap chain description struct
scd.BufferCount = 1; // one back buffer
scd.BufferDesc = bufferDesc;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used
scd.OutputWindow = hWnd; // the window to be used
scd.SampleDesc.Count = 4; // how many multisamples
scd.SampleDesc.Quality = 1;
scd.Windowed = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // windowed/full-screen mode
scd.Flags = 0;
// create a device, device context and swap chain using the information in the scd struct
dev = NULL;
devcon = NULL;
swapchain = NULL;
hr = D3D11CreateDeviceAndSwapChain(
NULL, //null
D3D_DRIVER_TYPE_HARDWARE, //driver type
NULL, //handle to a software rasterizer, should be null if driver type isn't software
NULL, //UINT device creation flags, can be ORed together
NULL, //D3D Feature level, when NULL defaults to 11.0-9.1 feature levels
NULL, //the number of elements in the D3D feature level array
D3D11_SDK_VERSION, //set to D3D11_SDK_VERSION
&scd, //reference to the swap chain desc
&swapchain, //reference to the pointer to the swap chain
&dev, //reference to the pointer to the device
NULL, //returns a reference to the first supported element in the feature level array
&devcon); //reference to the pointer to the device context
if (FAILED(hr))
{
//CODE FALLS INTO THIS BLOCK ON OTHER COMPUTER
MessageBox(NULL, "Create Device and SwapChain FAILED", "DirectXDeviceEngine", 0);
if (swapchain == NULL)
{
MessageBox(NULL, "swapchain was NULL", "DirectXDeviceEngine", 0);
}
if (dev == NULL)
{
MessageBox(NULL, "dev was NULL", "DirectXDeviceEngine", 0);
}
if (devcon == NULL)
{
MessageBox(NULL, "devcon was NULL", "DirectXDeviceEngine", 0);
}
}
//prepare buffers
// get the address of the back buffer
ID3D11Texture2D *pBackBuffer;
//VVVV CODE THEN CRASHES ON NEXT LINE BECAUSE SWAPCHAIN IS NULL VVVV
swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
Thanks in advance.
----UPDATE 2017/09/08-----
So I swapped some code around but I still get the error on the other computer. Current code looks like this:
//call at start to initialize D3DX
if (&hWnd == NULL)
{
MessageBox(NULL, "hWnd was NULL", "DirectXDeviceEngine", 0);
}
HRESULT hr;
// create a device, device context and swap chain using the information in the scd struct
dev = NULL;
devcon = NULL;
swapchain = NULL;
D3D_FEATURE_LEVEL lvl[] = {
D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 };
DWORD createDeviceFlags = 0;
hash ifdef _DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
hash endif
D3D_FEATURE_LEVEL fl;
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
createDeviceFlags, lvl, _countof(lvl),
D3D11_SDK_VERSION, &dev, &fl, &devcon);
if (hr == E_INVALIDARG)
{
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
createDeviceFlags, &lvl[1], _countof(lvl) - 1,
D3D11_SDK_VERSION, &dev, &fl, &devcon);
}
if (FAILED(hr))
{
MessageBox(NULL, "Create Device Failed", "DirectXDeviceEngine", 0);
}
IDXGIDevice * dxgiDevice = 0;
dev->QueryInterface(__uuidof(IDXGIDevice), (void **)& dxgiDevice);
IDXGIAdapter * dxgiAdapter = 0;
dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void **)& dxgiAdapter);
IDXGIFactory * dxgiFactory = 0;
dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void **)& dxgiFactory);
//initialize swap chain
//Describe our Buffer
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
/*bufferDesc.Width = winWidth;
bufferDesc.Height = winHeight;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
*/
bufferDesc.Width = 0;
bufferDesc.Height = 0;
bufferDesc.RefreshRate.Numerator = 0;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
// create a struct to hold information about the swap chain
DXGI_SWAP_CHAIN_DESC scd;
// clear out the struct for use
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
// fill the swap chain description struct
scd.BufferCount = 1; // one back buffer
scd.BufferDesc = bufferDesc;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used
scd.OutputWindow = hWnd; // the window to be used
scd.SampleDesc.Count = 4; // how many multisamples
scd.SampleDesc.Quality = 1;
scd.Windowed = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // windowed/full-screen mode
scd.Flags = 0;
hr = dxgiFactory->CreateSwapChain(dev, &scd, &swapchain);
if (FAILED(hr))
{
//fails here
MessageBox(NULL, "Create Swap Chain Failed", "DirectXDeviceEngine", 0);
}
if (dxgiDevice != NULL)
{
dxgiDevice->Release();
}
if (dxgiAdapter != NULL)
{
dxgiAdapter->Release();
}
if (dxgiFactory != NULL)
{
dxgiFactory->Release();
}
//prepare buffers
// get the address of the back buffer
ID3D11Texture2D *pBackBuffer;
swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
So, there is still some weird stuff happening here I'm not getting. If someone has an example of how to use FindClosestMatchingMode properly, I'd really appreciate it.
There is one other really peculiar thing though. When I am in the debugger on my laptop, the variable hWnd has a value, but the compiler says that it is Unused and can't display a value for it. Is this normal or could this be the source of the problem as hWnd is used in the Swap Chain Description? How can hWnd be bad/unused on the other computer but not on my desktop?
Thanks again.
So I FINALLY found the cause of the issue. For some reason, the Count 4 Quality 1 MSAA was not working on the other computer. When I set it to 1/0, it ran perfectly. I'll need to look into how to properly enumerate the MSAA stuff so it can be adjusted depending on the computer.
I am pretty new to DirectX 10 programming, and I have been trying to do the following with my limited skills..
I am trying to display an Image from one machine on to another output device(Another Monitor/TV) connected via HDMI. I researched on it and came to know that DXGI can be pretty useful for rendering purposes. Moreover while researching further I came across this link here. It does show how to display different images on multiple monitors connected to the main machine but it requires extended display for that.
My requirement is that when I run the code It should render the image on another output device without making it an extended display.
Approcah I am trying is that I am enumerating Video Adapters(which in my case is 1 ) and and then enumerating outputs available which should be two in numbers as I have an HDMI out connected to my PC. But if I do not set extended display it shows only one output available in my enumerated outputs array and if I extend the display it shows two of them.Once I am done with enumerating outputs I want to Render that image on desired output.
As far as i know every video adapter has a port for HDMI connected to it.
I wonder, if there is way I can access that port programatically and could render the image using that port??
Other than MSDN, documentation or explanation of those concepts are rather limited, so any help would be very welcome. Here is some code I have :
// include the basic windows header files and the Direct3D header files
#include <windows.h>
#include <windowsx.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
#include<vector>
#include<iostream>
#include <sstream>
#include<dxgi.h>
using namespace std;
// include the Direct3D Library file
#pragma comment (lib, "d3d11.lib")
#pragma comment (lib, "d3dx11.lib")
#pragma comment (lib, "d3dx10.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dxgi.lib")
std::vector<IDXGIAdapter*> EnumerateAdapters();
void PrintAdapterInfo(IDXGIAdapter* pAdapter);
std::vector<IDXGIOutput*> EnumerateOutputs(IDXGIAdapter* pAdapter);
void PrintOutputInfo(IDXGIOutput* output);
std::vector<DXGI_MODE_DESC*> GetDisplayModeList(IDXGIOutput* output);
void PrintDisplayModeInfo(DXGI_MODE_DESC* pModeDesc);
// global declarations
IDXGISwapChain *swapchain; // the pointer to the swap chain interface
ID3D11Device *dev; // the pointer to our Direct3D device interface
ID3D11DeviceContext *devcon; // the pointer to our Direct3D device context
ID3D11RenderTargetView *backbuffer; // the pointer to our back buffer
IDXGIAdapter *pAdapter;
IDXGIAdapter* adapter = NULL;
IDXGIFactory1* factory = NULL;
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
HWND hWnd;
std::vector<IDXGIOutput*> outputArray;
int nCmdShow;
// function prototypes
void InitD3D(HWND hWnd); // sets up and initializes Direct3D
void RenderFrame(void); // renders a single frame
void CleanD3D(void); // closes Direct3D and releases memory
void CreateWindowsForOutputs();
void GetAdapter();
void MultiRender();
void CreateSwapChainsAndViews();
std::vector<IDXGIAdapter*> EnumerateAdapters();
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
struct WindowDataContainer
{
//Direct3D 10 stuff per window data
IDXGISwapChain* swapChain;
ID3D11RenderTargetView* renderTargetView;
ID3D11DepthStencilView* depthStencilView;
// window goodies
HWND hWnd;
int width;
int height;
};
std::vector<WindowDataContainer*> WindowsArray;
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
std::cout << "Hello \nworld!"; // Will show a popup
std::vector<IDXGIAdapter*> adapters = EnumerateAdapters();
for(std::vector<IDXGIAdapter*>::iterator itor = adapters.begin(); itor != adapters.end(); ++itor)
{
//PrintAdapterInfo(*itor);
// Get Output info
std::vector<IDXGIOutput*> outputArray = EnumerateOutputs(*itor);
for(std::vector<IDXGIOutput*>::iterator outputItor = outputArray.begin(); outputItor != outputArray.end(); ++outputItor)
{
// PrintOutputInfo(*outputItor);
// Get display mode list
std::vector<DXGI_MODE_DESC*> modeList = GetDisplayModeList(*outputItor);
for(std::vector<DXGI_MODE_DESC*>::iterator modeItor = modeList.begin(); modeItor != modeList.end(); ++modeItor)
{
// PrintDisplayModeInfo(*modeItor);
}
}
CreateWindowsForOutputs();
MSG msg;
while(TRUE)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if(msg.message == WM_QUIT)
break;
}
MultiRender();
}
//std::getchar();
// return 0;
}
//std:getchar();
}
std::vector<IDXGIAdapter*> EnumerateAdapters()
{
// Create DXGI factory
IDXGIFactory1* pFactory = NULL;
HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory));
if (FAILED(hr))
{
MessageBox(NULL, L"Create DXGI factory failed", L"Error", 0);
}
// Enumerate devices
IDXGIAdapter* pAdapter = NULL;
std::vector<IDXGIAdapter*> vAdapters;
for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i)
{
vAdapters.push_back(pAdapter);
}
if (pFactory)
{
pFactory->Release();
pFactory = NULL;
}
return vAdapters;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
// this function initializes and prepares Direct3D for use
void InitD3D(HWND hWnd)
{
// create a struct to hold information about the swap chain
DXGI_SWAP_CHAIN_DESC scd;
// clear out the struct for use
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
// fill the swap chain description struct
scd.BufferCount = 1; // one back buffer
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // use 32-bit color
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used
scd.OutputWindow = hWnd; // the window to be used
scd.SampleDesc.Count = 1; // how many multisamples
scd.SampleDesc.Quality = 0; // multisample quality level
scd.Windowed = TRUE; // windowed/full-screen mode
// create a device, device context and swap chain using the information in the scd struct
D3D11CreateDeviceAndSwapChain( NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
NULL,
NULL,
NULL,
D3D11_SDK_VERSION,
&scd,
&swapchain,
&dev,
NULL,
&devcon);
// get the address of the back buffer
ID3D11Texture2D *pBackBuffer;
swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
// use the back buffer address to create the render target
dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
pBackBuffer->Release();
// set the render target as the back buffer
devcon->OMSetRenderTargets(1, &backbuffer, NULL);
// Set the viewport
D3D11_VIEWPORT viewport;
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = 800;
viewport.Height = 600;
devcon->RSSetViewports(1, &viewport);
}
// this is the function used to render a single frame
void RenderFrame(void)
{
// clear the back buffer to a deep blue
devcon->ClearRenderTargetView(backbuffer, D3DXCOLOR(0.0f, 0.2f, 0.4f, 1.0f));
// do 3D rendering on the back buffer here
// switch the back buffer and the front buffer
swapchain->Present(0, 0);
}
// this is the function that cleans up Direct3D and COM
void CleanD3D(void)
{
// close and release all existing COM objects
swapchain->Release();
backbuffer->Release();
dev->Release();
devcon->Release();
}
//Acquiring the outputs on our adapter
std::vector<IDXGIOutput*> EnumerateOutputs(IDXGIAdapter* pAdapter)
{
//std::vector<IDXGIOutput*> outputs;
IDXGIOutput* pOutput = NULL;
for (UINT i = 0; pAdapter->EnumOutputs(i, &pOutput) != DXGI_ERROR_NOT_FOUND; ++i)
{
outputArray.push_back(pOutput);
}
return outputArray;
}
void CreateWindowsForOutputs()
{ // std::vector<IDXGIOutput*> outputArray;
for( int i = 0; i < outputArray.size(); ++i )
{
IDXGIOutput* output = outputArray.at(i);
DXGI_OUTPUT_DESC outputDesc;
output->GetDesc( &outputDesc );
int x = outputDesc.DesktopCoordinates.left;
int y = outputDesc.DesktopCoordinates.top;
int width = outputDesc.DesktopCoordinates.right - x;
int height = outputDesc.DesktopCoordinates.bottom - y;
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass";
RegisterClassEx(&wc);
RECT wr = {0, 0, 800, 600};
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
// Don't forget to clean this up. And all D3D COM objects.
WindowDataContainer* window = new WindowDataContainer;
window->hWnd = CreateWindowEx(NULL,
L"WindowClass",
L"Our First Direct3D Program",
WS_OVERLAPPEDWINDOW,
300,
300,
wr.right - wr.left,
wr.bottom - wr.top,
NULL,
NULL,
hInstance,
NULL);
// show the window
ShowWindow( window->hWnd, SW_SHOWDEFAULT );
// InitD3D(hWnd);
CreateSwapChainsAndViews();
// set width and height
window->width = width;
window->height = height;
std::vector<WindowDataContainer*> windowsArray;
// shove it in the std::vector
windowsArray.push_back(window);
//if first window, associate it with DXGI so it can jump in
// when there is something of interest in the message queue
// think fullscreen mode switches etc. MSDN for more info.
// if(i == 0)
// factory->MakeWindowAssociation( window->hWnd, 0 );
}
}
std::vector<DXGI_MODE_DESC*> GetDisplayModeList(IDXGIOutput* output)
{
UINT num = 0;
DXGI_FORMAT format = DXGI_FORMAT_R32G32B32A32_TYPELESS;
UINT flags = DXGI_ENUM_MODES_INTERLACED | DXGI_ENUM_MODES_SCALING;
// Get number of display modes
output->GetDisplayModeList(format, flags, &num, 0);
// Get display mode list
DXGI_MODE_DESC * pDescs = new DXGI_MODE_DESC[num];
output->GetDisplayModeList(format, flags, &num, pDescs);
std::vector<DXGI_MODE_DESC*> displayList;
for(int i = 0; i < num; ++i)
{
displayList.push_back(&pDescs[i]);
}
return displayList;
}
void CreateSwapChainsAndViews()
{
std::vector<WindowDataContainer*> windowsArray;
for( int i = 0; i < windowsArray.size(); i++ )
{
WindowDataContainer* window = windowsArray.at(i);
// get the dxgi device
IDXGIDevice* DXGIDevice = NULL;
dev->QueryInterface( IID_IDXGIDevice, ( void** )&DXGIDevice ); // COM stuff, hopefully you are familiar
// create a swap chain
DXGI_SWAP_CHAIN_DESC swapChainDesc;
// fill it in
WindowDataContainer *p_Window = new WindowDataContainer;
HRESULT hr = factory->CreateSwapChain( DXGIDevice, &swapChainDesc, &p_Window->swapChain );
DXGIDevice->Release();
DXGIDevice = NULL;
// get the backbuffer
ID3D11Texture2D* backBuffer = NULL;
hr = window->swapChain->GetBuffer( 0, IID_ID3D11Texture2D, ( void** )&backBuffer );
// get the backbuffer desc
D3D11_TEXTURE2D_DESC backBufferDesc;
backBuffer->GetDesc( &backBufferDesc );
// create the render target view
D3D11_RENDER_TARGET_VIEW_DESC RTVDesc;
// fill it in
dev->CreateRenderTargetView( backBuffer, &RTVDesc, &window->renderTargetView );
backBuffer->Release();
backBuffer = NULL;
// Create depth stencil texture
ID3D11Texture2D* depthStencil = NULL;
D3D11_TEXTURE2D_DESC descDepth;
// fill it in
dev->CreateTexture2D( &descDepth, NULL, &depthStencil );
// Create the depth stencil view
D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
// fill it in
dev->CreateDepthStencilView( depthStencil, &descDSV, &window->depthStencilView );
}
}
void MultiRender( )
{
std::vector<WindowDataContainer*> windowsArray;
// Clear them all
for( int i = 0; i < windowsArray.size(); i++ )
{
WindowDataContainer* window = windowsArray.at(i);
// There is the answer to your second question:
devcon->OMSetRenderTargets( 1, &window->renderTargetView, NULL );
// Don't forget to adjust the viewport, in fullscreen it's not important...
D3D11_VIEWPORT Viewport;
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
Viewport.Width = window->width;
Viewport.Height = window->height;
Viewport.MinDepth = 0.0f;
Viewport.MaxDepth = 1.0f;
devcon->RSSetViewports( 1, &Viewport );
// TO DO: AMAZING STUFF PER WINDOW
}
}
//std::vector<IDXGIOutput*> outputArray; // contains outputs per adapter
void EnumOutputsOnAdapter()
{
IDXGIOutput* output = NULL;
for(int i = 0; DXGI_ERROR_NOT_FOUND != adapter->EnumOutputs(i, &output); ++i)
{
// get the description
DXGI_OUTPUT_DESC outputDesc;
HRESULT hr = output->GetDesc( &outputDesc );
outputArray.push_back( output );
}
}
// applicable for multiple ones with little effort
void GetAdapter()
{
// remember, we assume there's only one adapter (example purposes)
for( int i = 0; DXGI_ERROR_NOT_FOUND != factory->EnumAdapters( i, &adapter ); ++i );
{
// get the description of the adapter, assuming no failure
DXGI_ADAPTER_DESC adapterDesc;
HRESULT hr = adapter->GetDesc( &adapterDesc );
// Getting the outputs active on our adapter
EnumOutputsOnAdapter();
}
}
I want to overlay and D3D game using a D3D hardware overlay. I'm creating the overlay with this function: (source)
HRESULT CreateHWOverlay(HWND hwnd, IDirect3D9Ex *pD3D, IDirect3DDevice9Ex **ppDevice)
{
*ppDevice = NULL;
D3DCAPS9 caps;
ZeroMemory(&caps, sizeof(caps));
HRESULT hr = pD3D->GetDeviceCaps(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
&caps
);
if (FAILED(hr))
{
return hr;
}
// Check if overlay is supported.
if (!(caps.Caps & D3DCAPS_OVERLAY))
{
return D3DERR_UNSUPPORTEDOVERLAY;
}
D3DOVERLAYCAPS overlayCaps = { 0 };
IDirect3DDevice9Ex *pDevice = NULL;
IDirect3D9ExOverlayExtension *pOverlay = NULL;
// Check specific overlay capabilities.
hr = pD3D->QueryInterface(IID_PPV_ARGS(&pOverlay));
if (SUCCEEDED(hr))
{
hr = pOverlay->CheckDeviceOverlayType(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
1920,
1200,
D3DFMT_X8R8G8B8,
NULL,
D3DDISPLAYROTATION_IDENTITY,
&overlayCaps
);
}
// Create the overlay.
if (SUCCEEDED(hr))
{
DWORD flags = D3DCREATE_FPU_PRESERVE |
D3DCREATE_MULTITHREADED |
D3DCREATE_SOFTWARE_VERTEXPROCESSING;
D3DPRESENT_PARAMETERS pp = { 0 };
pp.BackBufferWidth = 1920;//overlayCaps.MaxOverlayDisplayWidth;
pp.BackBufferHeight = 1200;//overlayCaps.MaxOverlayDisplayHeight;
pp.BackBufferFormat = D3DFMT_X8R8G8B8;
pp.SwapEffect = D3DSWAPEFFECT_OVERLAY;
pp.hDeviceWindow = hwnd;
pp.Windowed = TRUE;
pp.Flags = D3DPRESENTFLAG_VIDEO;
pp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
pp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
hr = pD3D->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
NULL, flags, &pp, NULL, &pDevice);
}
if (SUCCEEDED(hr))
{
(*ppDevice) = pDevice;
(*ppDevice)->AddRef();
}
SafeRelease(&pD3D);
SafeRelease(&pDevice);
SafeRelease(&pOverlay);
return hr;
}
painting here:
while(true)
{
//Sleep(10);
D3DRECT rect = {50,50,200,200};
g_pDeviceEx->BeginScene();
RECT r = {0,0,1920,1200};
RECT xrect = {200, 200, 500, 300};
g_Font->DrawTextA(0, "D3D HARDWARE OVERLAY", -1, &xrect, DT_LEFT, D3DCOLOR_ARGB(255,255,0,0));
g_pDeviceEx->Clear(1, &rect, D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 0,255,0), 50.0,0);
g_pDeviceEx->EndScene();
g_pDeviceEx->PresentEx(&r, &r, hWnd, NULL, 0);
}
The overlay is just being displayed when i change the game's resolution when its black. as soon as you can see the world the text and the rect is gone. What am i doing wrong? and yes, im running windowed mode.
The post is 2 month old but the answer is :
g_pDeviceEx->Clear -> erase backbuffer with color (255, 0,255,0), put clear before BeginScene
a+