I'm trying to write a small chunk of code to grab the backbuffer into an array of pixels. I've barely used directX before as I'm more of a OpenGL fan.
My wish is to actually replace some code in a project that grabs the backbuffer using BitBlt and DC which is very slow.
This is supposed to work on all computers and that's why I chose directx7.
My question is.. how would I do that?
Thank you.
What I do is to use a helper class to do the lock /unlock as below. Then you use it like so :
mBackBuffer->Flip( DDFLIP_WAIT );
{
DDSURFACEDESC2 ddsd;
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize = sizeof( ddsd );
ReadLock r( mBackBuffer, ddsd, NULL /* for whole surface */ );
if ( r )
{
// ddsd.lpSurface contains the void* pointer to the bytes
// ddsd.lPitch contains the byte count of each horizontal line
}
} // ReadLock unlocks when it goes out of scope
class ReadLock
{
public:
ReadLock(IDirectDrawSurface7* surface, DDSURFACEDESC2& ddsd, LPRECT pRect = 0 ) : surface_(surface), mpRect( pRect ), hr( S_OK )
{
hr = surface_->Lock( mpRect, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, 0 );
}
HRESULT getResult() const { return hr; }
bool operator!() const { return FAILED( hr ); }
operator bool() const { return SUCCEEDED( hr ); }
~ReadLock()
{
if ( surface_ && SUCCEEDED( hr ) )
surface_->Unlock(mpRect);
}
private:
HRESULT hr;
RECT* mpRect;
IDirectDrawSurface7* surface_;
};
TBH DirectX 9 will work even with ancient cards. You don't have all the features available but you have a a SHED load more usable information out there. Although I think you might be a bit knackered on Win 95/9/me support and win 2K. Bear in mind NT4 never had a decent version of DirectX.
Alas I don't have the DX7 docs anywhere handy but I'm pretty sure you could just get the back buffer surface and then lock it to get at the data. Though you need to bear in mind just how slow grabbing the back buffer can be, especially on old cards. Copying the back buffer from local video memory to system memory across the PCI or AGP bus is incredibly slow.
What exactly are you trying to achieve? There must be better ways to achieve what you are after doing ...
Related
I made NDIS 6 network filter driver and am reading the packet.
When I use Intel I350 NIC, 'MmGetMdlByteCount' returns '9014'bytes.
This value is the same as the MTU size, so I can read the data at once.
However, when using the x540 NIC, 'MmGetMdlByteCount' is returned to '2048'bytes.
So I have to read the MDL over and over again. Why is this happening?
Is there a way to read data at once on the X540 NIC?
I want to reduce repetition because I think the consumption time will be longer if I bring the data several times.
Below is a part of my source code.
PVOID vpByTmpData = NULL;
for( pNbMdl = NET_BUFFER_CURRENT_MDL( pNetBuffer );
pNbMdl != NULL && ulDataLength > 0;
pNbMdl = NDIS_MDL_LINKAGE( pNbMdl ) )
{
ulBytesToCopy = MmGetMdlByteCount( pNbMdl );
if( ulBytesToCopy == 0 )
continue;
vpByTmpData = MmGetSystemAddressForMdlSafe( pNbMdl, NormalPagePriority );
if( !vpByTmpData )
{
bRet = FALSE;
__leave;
}
if( ulBytesToCopy > ulDataLength )
ulBytesToCopy = ulDataLength;
NdisMoveMemory( &baImage[ulMemIdxOffset], (PBYTE)(vpByTmpData), ulBytesToCopy);
ulMemIdxOffset += ulBytesToCopy;
}
Please help me.
What you're seeing is a result of how the NIC hardware physically works. Different hardware will use different buffer layout strategies. NDIS does not attempt to force every NIC to use the same strategy, since that would reduce performance on some NICs. Unfortunately for you, that means the complexity of dealing with different buffers gets pushed upwards into NDIS filter & protocol drivers.
You can use NdisGetDataBuffer to do some of this work for you. Internally, NdisGetDataBuffer works like this:
if MmGetSystemAddressForMdl fails:
return NULL;
else if the payload is already contiguous in memory:
return a pointer to that directly;
else if you provided your own buffer:
copy the payload into your buffer
return a pointer to your buffer;
else:
return NULL;
So you can use NdisGetDataBuffer to obtain a contiguous view of the payload. The simplest way to use it is this:
UCHAR ScratchBuffer[MAX_MTU_SIZE];
UCHAR *Payload = NdisGetDataBuffer(NetBuffer, NetBuffer->DataLength, ScratchBuffer, 1, 0);
if (!Payload) {
return NDIS_STATUS_RESOURCES; // very unlikely: MmGetSystemAddressForMdl failed
}
memcpy(baImage, Payload, NetBuffer->DataLength);
But this can have a double-copy in some cases. (Exercise to test your understanding: when would there be a double-copy?) For slightly better performance, you can avoid the double-copy with this trick:
UCHAR *Payload = NdisGetDataBuffer(NetBuffer, NetBuffer->DataLength, baImage, 1, 0);
if (!Payload) {
return NDIS_STATUS_RESOURCES; // very unlikely: MmGetSystemAddressForMdl failed
}
// Did NdisGetDataBuffer already copy the payload into my flat buffer?
if (Payload != baImage) {
// If not, copy from the MDL to my flat buffer now.
memcpy(baImage, Payload, NetBuffer->DataLength);
}
You haven't included a complete code sample, but I suspect there may be some bugs in your code. I don't see any attempt to handle NetBuffer->CurrentMdlOffset. While this is usually zero, it's not always zero, so your code would not always be correct.
Similarly, it looks like the copy isn't correctly constrained by ulDataLength. You would need a ulDataLength -= ulBytesToCopy in there somewhere to fix this.
I'm very sympathetic to how tricky it is to navigate NBLs, NBs, and MDLs -- my first NIC driver included a nasty bug in calculating MDL offsets. I have some MDL-handling code internally -- I will try to clean it up a bit and publish it at https://github.com/microsoft/ndis-driver-library/ in the next few days. I'll update this post if I get it published. I think there's clearly a need for some nice, reusable and well-documented sample code to just copy (subsets of) an MDL chain into a flat buffer, or vice-versa.
Update: Refer to MdlCopyMdlChainAtOffsetToFlatBuffer in mdl.h
the function in this https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11devicechild
this is useless code, dont see it, i just want to make this website happy
pContext->IAGetVertexBuffers(0, 1, &veBuffer, &Stride, &veBufferOffset);
if (veBuffer)
veBuffer->GetDesc(&vedesc);
D3D11_BUFFER_DESC bufferDesc;
bufferDesc.Usage = D3D11_USAGE_STAGING;
bufferDesc.ByteWidth = vedesc.ByteWidth;
bufferDesc.BindFlags = 0;
bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
bufferDesc.MiscFlags = 0;
ID3D11Device **pDeviceExtra = nullptr;
veBuffer->GetDevice(pDeviceExtra); //Can I use this to get to the device object?
//Create the buffer.
HRESULT hr = pDeviceExtra->CreateBuffer(&bufferDesc, NULL, &readVB);
assert(hr == S_OK);
pContext->CopyResource(readVB, veBuffer);
i know this function will set a iunknown interface to the device, but after that, how can i use this interface? what is this function mean?
i have try to search in web, but i cant find any anwser
This API is not for general consumption. It's there for internal reasons, and in normal Direct3D programming you will never use ID3D11DeviceChild::SetPrivateDataInterface. In almost all cases it's going to do nothing and return S_OK or do nothing and return E_NOTIMPL.
The ID3D11DeviceChild::SetPrivateData method can be used to provide debug name information for the debug layer:
static const char c_szName[] = "My name";
hr = pContext->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof( c_szName ) - 1, c_szName );
Otherwise, you don't really need to use the ID3D11DeviceChild interface itself for anything. Having an interface derived from it is mostly an indication that it's lifetime is tied to the lifetime of it's owning ID3D11Device instance regardless of that object's refcount: see Microsoft Docs.
I'm trying to read the NTFS Change Journal but I've noticed that all the example code I can find fails on Windows 10, even though it works on Windows 7.
For example, Microsofts own example Walking a Buffer of Change Journal Records works on Windows 7 but when I run the same code on Windows 10 I get an error 87 (The parameter is incorrect) when I call DeviceIoControl with FSCTL_READ_USN_JOURNAL (Note that the earlier call to DeviceIoControl with FSCTL_QUERY_USN_JOURNAL completes successfully and return valid data.).
I've even taken the EXE compiled and working on Windows 7 and copied it to the Windows 10 machine and it still fails, so I believe that Windows 10 may be more strict on parameter validation or something like that?
I am running the code as Administrator, so that's not the issue.
I can't find any other references to this problem, but I get the same issue if I take other peoples example code and attempt to run it on Windows 10.
Edit:
The code itself:
#include <Windows.h>
#include <WinIoCtl.h>
#include <stdio.h>
#define BUF_LEN 4096
void main()
{
HANDLE hVol;
CHAR Buffer[BUF_LEN];
USN_JOURNAL_DATA JournalData;
READ_USN_JOURNAL_DATA ReadData = {0, 0xFFFFFFFF, FALSE, 0, 0};
PUSN_RECORD UsnRecord;
DWORD dwBytes;
DWORD dwRetBytes;
int I;
hVol = CreateFile( TEXT("\\\\.\\c:"),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if( hVol == INVALID_HANDLE_VALUE )
{
printf("CreateFile failed (%d)\n", GetLastError());
return;
}
if( !DeviceIoControl( hVol,
FSCTL_QUERY_USN_JOURNAL,
NULL,
0,
&JournalData,
sizeof(JournalData),
&dwBytes,
NULL) )
{
printf( "Query journal failed (%d)\n", GetLastError());
return;
}
ReadData.UsnJournalID = JournalData.UsnJournalID;
printf( "Journal ID: %I64x\n", JournalData.UsnJournalID );
printf( "FirstUsn: %I64x\n\n", JournalData.FirstUsn );
for(I=0; I<=10; I++)
{
memset( Buffer, 0, BUF_LEN );
if( !DeviceIoControl( hVol,
FSCTL_READ_USN_JOURNAL,
&ReadData,
sizeof(ReadData),
&Buffer,
BUF_LEN,
&dwBytes,
NULL) )
{
printf( "Read journal failed (%d)\n", GetLastError());
return;
}
dwRetBytes = dwBytes - sizeof(USN);
// Find the first record
UsnRecord = (PUSN_RECORD)(((PUCHAR)Buffer) + sizeof(USN));
printf( "****************************************\n");
// This loop could go on for a long time, given the current buffer size.
while( dwRetBytes > 0 )
{
printf( "USN: %I64x\n", UsnRecord->Usn );
printf("File name: %.*S\n",
UsnRecord->FileNameLength/2,
UsnRecord->FileName );
printf( "Reason: %x\n", UsnRecord->Reason );
printf( "\n" );
dwRetBytes -= UsnRecord->RecordLength;
// Find the next record
UsnRecord = (PUSN_RECORD)(((PCHAR)UsnRecord) +
UsnRecord->RecordLength);
}
// Update starting USN for next call
ReadData.StartUsn = *(USN *)&Buffer;
}
CloseHandle(hVol);
}
I've managed to work out what the problem is.
The example Microsoft code creates a local variable defined as READ_USN_JOURNAL_DATA, which is defined as:
#if (NTDDI_VERSION >= NTDDI_WIN8)
typedef READ_USN_JOURNAL_DATA_V1 READ_USN_JOURNAL_DATA, *PREAD_USN_JOURNAL_DATA;
#else
typedef READ_USN_JOURNAL_DATA_V0 READ_USN_JOURNAL_DATA, *PREAD_USN_JOURNAL_DATA;
#endif
On my systems (Both the Win10 and Win7 systems) this evaluates to READ_USN_JOURNAL_DATA_V1.
Looking at the difference betweem READ_USN_JOURNAL_DATA_V0 and READ_USN_JOURNAL_DATA_V1 we can see that V0 is defined as:
typedef struct {
USN StartUsn;
DWORD ReasonMask;
DWORD ReturnOnlyOnClose;
DWORDLONG Timeout;
DWORDLONG BytesToWaitFor;
DWORDLONG UsnJournalID;
} READ_USN_JOURNAL_DATA_V0, *PREAD_USN_JOURNAL_DATA_V0;
and the V1 version is defined as:
typedef struct {
USN StartUsn;
DWORD ReasonMask;
DWORD ReturnOnlyOnClose;
DWORDLONG Timeout;
DWORDLONG BytesToWaitFor;
DWORDLONG UsnJournalID;
WORD MinMajorVersion;
WORD MaxMajorVersion;
} READ_USN_JOURNAL_DATA_V1, *PREAD_USN_JOURNAL_DATA_V1;
Note the new Min and Max Major version members.
So, the Microsoft code is defining a local variable called ReadData that is actually a V1 structure, yet it appears to fill in the data assuming it's a V0 structure. i.e. it doesn't set the Min and Max elements.
It appears that Win7 is fine with this but Win10 rejects it and returns error 87 (The parameter is incorrect).
Sure enough if I explicitly define the ReadData variable to be a READ_USN_JOURNAL_DATA_V0 then the code works on Win7 and Win10, whereas if I explicitly define it as a READ_USN_JOURNAL_DATA_V1 then it continues to work on Win7 but not on Win10.
The strange thing is that the API documentation for READ_USN_JOURNAL_DATA_V1 structure states that it's only supported from Windows 8 on, so it's odd that it works on Windows 7 at all. I guess it's just interpreting it as a READ_USN_JOURNAL_DATA_V0 structure given that V1 version is an extension of the V0 structure. If so then it must be ignoring the size parameter that is passed into DeviceIOControl.
Anyway, all working now. I hope someone finds this a useful reference in the future.
I came across this exact same issue with the sample code as the OP. At the start of the sample code, you will see a partial initialization of the structure at the point of declaration. A little later in the code, right before the offending call, there is a line that assigns the UsnJournalID into the read data structure.
For Windows 10, though, the other two members of the V1 structure are not initialized. I initialized them right after the UsnJournalID's initialization with:
#if (NTDDI_VERSION >= NTDDI_WIN8)
ReadData.MinMajorVersion = JournalData.MinSupportedMajorVersion;
ReadData.MaxMajorVersion = JournalData.MaxSupportedMajorVersion;
#endif
When I ran my code after doing this, it worked correctly without the error code. I remember reading in the Volume Management API discussion that the Min and Max versions needed to be set. I forgot exactly where, because I've been reading and testing this stuff for a couple days.
Anyway, I hope that clarifies the issue for anyone coming after me.
Replace the READ_USN_JOURNAL_DATA structure with READ_USN_JOURNAL_DATA_V0 data structure and initialize it.
This worked for me
READ_USN_JOURNAL_DATA_V0 ReadData;
ZeroMemory(&ReadData, sizeof(ReadData));
ReadData.ReasonMask = 0xFFFFFFFF;
Was having an odd situation with our product being too bright on win8 compared to win7. So tried PNTriangles11 DX SDK example and got the same results. This was after testing the behavior at BestBuy and 24 systems all behaved the same way, all failed calling EnumOutputs. It did not matter if desktop, laptop, all in one, or tablet.
Where our directx charting product looked like... also shows our WPF charting component and even more variation in brightness caused by this same failure issue.
So this led to researching and I find that I can reproduce on Win7 by adding below...
HRESULT CD3D11Enumeration::EnumerateOutputs( CD3D11EnumAdapterInfo* pAdapterInfo )
{
HRESULT hr;
IDXGIOutput* pOutput;
for( int iOutput = 0; ; ++iOutput )
{
pOutput = NULL;
hr = pAdapterInfo->m_pAdapter->EnumOutputs( iOutput, &pOutput );
// FORCE FAILURE ON WIN7 FOR TESTING PURPOSES
hr = DXGI_ERROR_NOT_FOUND;
if( DXGI_ERROR_NOT_FOUND == hr )
{
return S_OK;
}
else if( FAILED( hr ) )
{
return hr; //Something bad happened.
}
else //Success!
{
CD3D11EnumOutputInfo* pOutputInfo = new CD3D11EnumOutputInfo;
if( !pOutputInfo
Though to get the sdk example to run now on Win8 and Win7 you also need to modify (adding the test for non zero hAdapterMonitor...
// Check to see if the window needs to be resized.
// Handle cases where the window is minimized and maxmimized as well.
bool bNeedToResize = false;
if(hAdapterMonitor && DXUTGetIsWindowedFromDS( pNewDeviceSettings ) &&
!bKeepCurrentWindowSize )
{
UINT nClientWidth;
UINT nClientHeight;
if( IsIconic( DXUTGetHWNDDeviceWindowed() ) )
{
The above allows you to tinker with the issue on win7 as a way to develop for win8.
So my question is, what is the best solution to handling EnumOutputs failing on Win8. And or what other changes to DXUT do you find useful.
Microsoft is working on this, and I will post their response, hopefully shortly, but a discussion of this topic is needed as so many dxut inspired software projects or those using EnumOutputs are failing on Win8.
ANSWER 1: Microsoft did get back to me with an initial response to get PNTriangles11 working on Win8. They pointed me to the change within DXUTApplyDefaultDeviceSettings(DXUTDeviceSettings *modifySettings) where...
modifySettings->d3d11.sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
has to be changed to...
modifySettings->d3d11.sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
but the simple workaround for DXUTChangeDevice is still needed: a test for non zero hAdapterMonitor...
bool bNeedToResize = false;
if(hAdapterMonitor && DXUTGetIsWindowedFromDS( pNewDeviceSettings ) && !bKeepCurrentWindowSize )
{
UINT nClientWidth;
UINT nClientHeight;
if( ::IsIconic( DXUTGetHWNDDeviceWindowed() ) )
{
// Window is currently minimized. To tell if it needs to resize,
Still waiting for an explanation from Microsoft why EnumOutputs failed on all 24 systems I tested at BestBuy.
This is a bit esoteric, but there have to be a few people here who know how OS X's Carbon Component Manager works. I've made a couple of little apps to play around with making Components (see here for some background). Actually, one of the apps is a sample program straight from Apple called 'Fiendishthngs'. It lists all the Components that the Component Manager is making available. My program is a simple little thing that registers a Component, lists all the Components that the Component Manager has, and then waits around indefinately (to avoid purging the Component that it registered).
On my system, the Component Manager is tracking 873 Components (mostly codecs of one sort of another). My program that registers a Component registers it, and then counts 874 Components because it just registered one itself, of course). Here's the source:
void RegisterBasicComponent()
{
ComponentDescription desc;
desc.componentType = kMyComponentType;
desc.componentSubType = kMyComponentSubType;
desc.componentManufacturer = kMyComponentManufacturer;
desc.componentFlags = 0;
desc.componentFlagsMask = cmpIsMissing;
ComponentRoutineUPP MyComponentRoutineUPP
= NewComponentRoutineUPP( &MyComponentRoutineProc );
// Handle name_handle = NewHandle( sizeof( kMyComponentName ) );
//strcpy( *(char**)name_handle, kMyComponentName );
//RegisterComponent( &desc, MyComponentRoutineUPP, registerComponentGlobal, name_handle, NULL, NULL );
Component component = RegisterComponent( &desc, MyComponentRoutineUPP, registerComponentGlobal, NULL, NULL, NULL );
if ( NULL != component )
printf("The registration seems to have worked!\n");
else
printf("Nope - didn't work for some reason.\n");
}
int main( void )
{
RegisterBasicComponent();
ComponentDescription looking;
// OSType componentType; /* A unique 4-byte code indentifying the command set */
// OSType componentSubType; /* Particular flavor of this instance */
// OSType componentManufacturer; /* Vendor indentification */
// UInt32 componentFlags; /* 8 each for Component,Type,SubType,Manuf/revision */
// UInt32 componentFlagsMask; /* Mask for specifying which flags to consider in search, zero during registration */
looking.componentType = kAnyComponentType;
looking.componentSubType = kAnyComponentSubType;
// looking.componentSubType = kComponentResourceType
looking.componentManufacturer = kAnyComponentManufacturer;
looking.componentFlags = 0;
looking.componentFlagsMask = cmpIsMissing;
long numComponents = CountComponents ( &looking );
printf("Found %ld components.\n", numComponents);
Component component = 0;
int i = 0;
while (true)
{
component = FindNextComponent(component, &looking);
if ( 0 == component )
break;
ComponentDescription desc;
Handle componentName = NewHandle(256);
Handle componentInfo = NewHandle(1024);
Handle componentIcon = 0;
OSErr err = GetComponentInfo( component,
&desc,
componentName,
componentInfo,
componentIcon );
if ( err != noErr )
{
printf("Couldn't find any info on component %d of %ld in list!\n", i
, numComponents);
break;
}
printf( "%d of %ld: '%c%c%c%c', '%c%c%c%c', '%c%c%c%c', '%s'\n",
i, numComponents,
SPLAT_WORD( desc.componentManufacturer ),
SPLAT_WORD( desc.componentType ),
SPLAT_WORD( desc.componentSubType ),
*componentName );
RecoverHandle( *componentName );
RecoverHandle( *componentInfo );
++i;
}
while (true)
{
printf("Waiting around for someone to use me...\n");
sleep( 3 );
}
}
Anyways, when I run this, keep it running (so the Component would presumably stay registered with the Component Manager), and then run Fiendishthngs, Fiendishthngs can't see my test Component that I register - it only sees 873 Components. The 'registerComponentGlobal' flag passed into RegisterComponent() is supposed to make the Component available to other processes, but it seems like something is going awry.
Any ideas?
Well, I left this issue behind, resigning myself to the fact that OS X's Component Manager probably doesn't support the 'global' option anymore.
This would make a lot of sense, really. Making your component 'global' from your process to other processes would require out-of-process call marshalling, like with RPC, with OS X. On the other hand, in OS 9 and earlier, it would make perfect sense, given that all processes lived in a common address space. For OS 9 it would be trivial to make a component globally available to all processes.
Anyways, today I was disassembling RegisterComponentFileRefEntries() where the relevant code seems to reside, and sure enough, I saw this in the pre-amble to the function (the comments are mine):
0x9841a026 <+0018> mov eax, DWORD PTR [ebp+0x8] // load param spec
0x9841a029 <+0021> mov DWORD PTR [ebp-0xdc],eax // local spec = param spec
0x9841a02f <+0027> mov edx,DWORD PTR [ebp+0x10] // load param toRegister
0x9841a032 <+0030> mov DWORD PTR [ebp-0xe0],edx // local toRegister = param toRegister
The signature for RegisterComponentFileRefEntries is
extern OSErr
RegisterComponentFileEntries(
const FSSpec * spec,
short global,
const ComponentDescription * toRegister, /* can be NULL */
UInt32 registerCount)
The only 2 parameters that RegisterComponentFileRefEntries bothers with are spec (at ebp+0x8) and toRegister (at ebp+0x10). global (at ebp+0xc) and registerCount (at ebp+0x14) are completely ignored.