API Duplikasi Desktop
Windows 8 menonaktifkan driver cermin Windows 2000 Display Driver Model (XDDM) standar dan menawarkan API duplikasi desktop sebagai gantinya. API duplikasi desktop menyediakan akses jarak jauh ke gambar desktop untuk skenario kolaborasi. Aplikasi dapat menggunakan API duplikasi desktop untuk mengakses pembaruan frame-by-frame ke desktop. Karena aplikasi menerima pembaruan pada gambar desktop di permukaan DXGI, aplikasi dapat menggunakan kekuatan penuh GPU untuk memproses pembaruan gambar.
Memperbarui data gambar desktop
DXGI menyediakan permukaan yang berisi gambar desktop saat ini melalui metode IDXGIOutputDuplication::AcquireNextFrame baru. Format gambar desktop selalu DXGI_FORMAT_B8G8R8A8_UNORM apa pun mode tampilan saat ini. Seiring dengan permukaan ini, metode IDXGIOutputDuplication ini mengembalikan jenis info yang ditunjukkan yang membantu Anda menentukan piksel mana dalam permukaan yang perlu Anda proses:
- IDXGIOutputDuplication::GetFrameDirtyRects mengembalikan wilayah kotor, yang merupakan persegi panjang yang tidak tumpang tindih yang menunjukkan area gambar desktop yang diperbarui sistem operasi sejak Anda memproses gambar desktop sebelumnya.
- IDXGIOutputDuplication::GetFrameMoveRects mengembalikan wilayah pemindahan, yang merupakan persegi panjang piksel dalam gambar desktop yang dipindahkan sistem operasi ke lokasi lain dalam gambar yang sama. Setiap wilayah pemindahan terdiri dari persegi panjang tujuan dan titik sumber. Titik sumber menentukan lokasi dari mana sistem operasi menyalin wilayah dan persegi panjang tujuan menentukan ke tempat sistem operasi memindahkan wilayah tersebut. Wilayah pemindahan selalu merupakan wilayah yang tidak direntangkan sehingga sumber selalu berukuran sama dengan tujuan.
Misalkan gambar desktop ditransmisikan melalui koneksi lambat ke aplikasi klien jarak jauh Anda. Jumlah data yang dikirim melalui koneksi dikurangi dengan hanya menerima data tentang bagaimana aplikasi klien Anda harus memindahkan wilayah piksel daripada data piksel aktual. Untuk memproses pemindahan, aplikasi klien Anda harus menyimpan gambar terakhir lengkap.
Meskipun sistem operasi mengakumulasi pembaruan gambar desktop yang tidak diproses, sistem operasi mungkin kehabisan ruang untuk menyimpan wilayah pembaruan secara akurat. Dalam situasi ini, sistem operasi mulai mengakumulasi pembaruan dengan menyatukannya dengan wilayah pembaruan yang ada untuk mencakup semua pembaruan baru. Akibatnya, sistem operasi mencakup piksel yang sebenarnya belum diperbarui dalam bingkai tersebut. Tetapi situasi ini tidak menghasilkan masalah visual di aplikasi klien Anda karena Anda menerima seluruh gambar desktop dan bukan hanya piksel yang diperbarui.
Untuk merekonstruksi gambar desktop yang benar, aplikasi klien Anda harus terlebih dahulu memproses semua wilayah pemindahan lalu memproses semua wilayah kotor. Salah satu dari daftar wilayah kotor dan pemindahan ini bisa benar-benar kosong. Contoh kode dari Sampel Duplikasi Desktop menunjukkan cara memproses wilayah yang kotor dan bergerak dalam satu bingkai:
//
// Get next frame and write it into Data
//
HRESULT DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data)
{
HRESULT hr = S_OK;
IDXGIResource* DesktopResource = NULL;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
//Get new frame
hr = DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
if (FAILED(hr))
{
if ((hr != DXGI_ERROR_ACCESS_LOST) && (hr != DXGI_ERROR_WAIT_TIMEOUT))
{
DisplayErr(L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr);
}
return hr;
}
// If still holding old frame, destroy it
if (AcquiredDesktopImage)
{
AcquiredDesktopImage->Release();
AcquiredDesktopImage = NULL;
}
// QI for IDXGIResource
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&AcquiredDesktopImage));
DesktopResource->Release();
DesktopResource = NULL;
if (FAILED(hr))
{
DisplayErr(L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr);
return hr;
}
// Get metadata
if (FrameInfo.TotalMetadataBufferSize)
{
// Old buffer too small
if (FrameInfo.TotalMetadataBufferSize > MetaDataSize)
{
if (MetaDataBuffer)
{
delete [] MetaDataBuffer;
MetaDataBuffer = NULL;
}
MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize];
if (!MetaDataBuffer)
{
DisplayErr(L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
MetaDataSize = 0;
Data->MoveCount = 0;
Data->DirtyCount = 0;
return E_OUTOFMEMORY;
}
MetaDataSize = FrameInfo.TotalMetadataBufferSize;
}
UINT BufSize = FrameInfo.TotalMetadataBufferSize;
// Get move rectangles
hr = DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(MetaDataBuffer), &BufSize);
if (FAILED(hr))
{
if (hr != DXGI_ERROR_ACCESS_LOST)
{
DisplayErr(L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr);
}
Data->MoveCount = 0;
Data->DirtyCount = 0;
return hr;
}
Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
BYTE* DirtyRects = MetaDataBuffer + BufSize;
BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
// Get dirty rectangles
hr = DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize);
if (FAILED(hr))
{
if (hr != DXGI_ERROR_ACCESS_LOST)
{
DisplayErr(L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr);
}
Data->MoveCount = 0;
Data->DirtyCount = 0;
return hr;
}
Data->DirtyCount = BufSize / sizeof(RECT);
Data->MetaData = MetaDataBuffer;
}
Data->Frame = AcquiredDesktopImage;
Data->FrameInfo = FrameInfo;
return hr;
}
//
// Release frame
//
HRESULT DUPLICATIONMANAGER::DoneWithFrame()
{
HRESULT hr = S_OK;
hr = DeskDupl->ReleaseFrame();
if (FAILED(hr))
{
DisplayErr(L"Failed to release frame in DUPLICATIONMANAGER", L"Error", hr);
return hr;
}
if (AcquiredDesktopImage)
{
AcquiredDesktopImage->Release();
AcquiredDesktopImage = NULL;
}
return hr;
}
Memutar gambar desktop
Anda harus menambahkan kode eksplisit ke aplikasi klien duplikasi desktop untuk mendukung mode yang diputar. Dalam mode yang diputar, permukaan yang Anda terima dari IDXGIOutputDuplication::AcquireNextFrame selalu dalam orientasi yang tidak diputar, dan gambar desktop diputar di dalam permukaan. Misalnya, jika desktop diatur ke 768x1024 pada rotasi 90 derajat, AcquireNextFrame mengembalikan permukaan 1024x768 dengan gambar desktop diputar di dalamnya. Berikut adalah beberapa contoh rotasi.
Mode tampilan diatur dari panel kontrol tampilan | Mode tampilan yang dikembalikan oleh GDI atau DXGI | Surface dikembalikan dari AcquireNextFrame |
---|---|---|
Lanskap 1024x768 | Rotasi 1024x768 0 derajat | 1024x768[newline] ![]() |
1024x768 potret | Rotasi 768x1024 90 derajat | 1024x768[newline] ![]() |
Lanskap 1024x768 (dibalik) | Rotasi 1024x768 180 derajat | 1024x768[newline] ![]() |
1024x768 potret (dibalik) | Rotasi 768x1024 270 derajat | 1024x768[newline] ![]() |
Kode di aplikasi klien duplikasi desktop Anda harus memutar gambar desktop dengan tepat sebelum Anda menampilkan gambar desktop.
Catatan
Dalam skenario multi-monitor, Anda dapat memutar gambar desktop untuk setiap monitor secara independen.
Memperbarui penunjuk desktop
Anda perlu menggunakan API duplikasi desktop untuk menentukan apakah aplikasi klien Anda harus menggambar bentuk penunjuk mouse ke gambar desktop. Baik penunjuk mouse sudah digambar ke gambar desktop yang disediakan IDXGIOutputDuplication::AcquireNextFrame atau penunjuk mouse terpisah dari gambar desktop. Jika penunjuk mouse digambar ke gambar desktop, data posisi penunjuk yang dilaporkan oleh AcquireNextFrame (di anggota PointerPositionDXGI_OUTDUPL_FRAME_INFO bahwa parameter pFrameInfo menunjuk ke) menunjukkan bahwa pointer terpisah tidak terlihat. Jika adaptor grafis melapisi penunjuk mouse di atas gambar desktop, AcquireNextFrame melaporkan bahwa penunjuk terpisah terlihat. Jadi, aplikasi klien Anda harus menggambar bentuk penunjuk mouse ke gambar desktop untuk secara akurat mewakili apa yang akan dilihat pengguna saat ini di monitor mereka.
Untuk menggambar penunjuk mouse desktop, gunakan anggota PointerPositionDXGI_OUTDUPL_FRAME_INFO dari parameter pFrameInfoacquireNextFrame untuk menentukan tempat menemukan sudut kiri atas penunjuk mouse pada gambar desktop. Saat menggambar bingkai pertama, Anda harus menggunakan metode IDXGIOutputDuplication::GetFramePointerShape untuk mendapatkan info tentang bentuk penunjuk mouse. Setiap panggilan ke AcquireNextFrame untuk mendapatkan bingkai berikutnya juga menyediakan posisi pointer saat ini untuk bingkai tersebut. Di sisi lain, Anda perlu menggunakan GetFramePointerShape lagi hanya jika bentuk berubah. Jadi, simpan salinan gambar penunjuk terakhir dan gunakan untuk menggambar di desktop kecuali bentuk penunjuk mouse berubah.
Catatan
Bersama dengan gambar bentuk penunjuk, GetFramePointerShape menyediakan ukuran lokasi hot spot. Hot spot diberikan hanya untuk tujuan informasi. Lokasi tempat menggambar gambar penunjuk independen ke hotspot.
Contoh kode dari Sampel Duplikasi Desktop ini menunjukkan cara mendapatkan bentuk penunjuk mouse:
//
// Retrieves mouse info and write it into PtrInfo
//
HRESULT DUPLICATIONMANAGER::GetMouse(_Out_ PTR_INFO* PtrInfo, _In_ DXGI_OUTDUPL_FRAME_INFO* FrameInfo, INT OffsetX, INT OffsetY)
{
HRESULT hr = S_OK;
// A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change
if (FrameInfo->LastMouseUpdateTime.QuadPart == 0)
{
return hr;
}
bool UpdatePosition = true;
// Make sure we don't update pointer position wrongly
// If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer
// was visible, if so, don't set it to invisible or update.
if (!FrameInfo->PointerPosition.Visible && (PtrInfo->WhoUpdatedPositionLast != OutputNumber))
{
UpdatePosition = false;
}
// If two outputs both say they have a visible, only update if new update has newer timestamp
if (FrameInfo->PointerPosition.Visible && PtrInfo->Visible && (PtrInfo->WhoUpdatedPositionLast != OutputNumber) && (PtrInfo->LastTimeStamp.QuadPart > FrameInfo->LastMouseUpdateTime.QuadPart))
{
UpdatePosition = false;
}
// Update position
if (UpdatePosition)
{
PtrInfo->Position.x = FrameInfo->PointerPosition.Position.x + OutputDesc.DesktopCoordinates.left - OffsetX;
PtrInfo->Position.y = FrameInfo->PointerPosition.Position.y + OutputDesc.DesktopCoordinates.top - OffsetY;
PtrInfo->WhoUpdatedPositionLast = OutputNumber;
PtrInfo->LastTimeStamp = FrameInfo->LastMouseUpdateTime;
PtrInfo->Visible = FrameInfo->PointerPosition.Visible != 0;
}
// No new shape
if (FrameInfo->PointerShapeBufferSize == 0)
{
return hr;
}
// Old buffer too small
if (FrameInfo->PointerShapeBufferSize > PtrInfo->BufferSize)
{
if (PtrInfo->PtrShapeBuffer)
{
delete [] PtrInfo->PtrShapeBuffer;
PtrInfo->PtrShapeBuffer = NULL;
}
PtrInfo->PtrShapeBuffer = new (std::nothrow) BYTE[FrameInfo->PointerShapeBufferSize];
if (!PtrInfo->PtrShapeBuffer)
{
DisplayErr(L"Failed to allocate memory for pointer shape in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
PtrInfo->BufferSize = 0;
return E_OUTOFMEMORY;
}
// Update buffer size
PtrInfo->BufferSize = FrameInfo->PointerShapeBufferSize;
}
UINT BufferSizeRequired;
// Get shape
hr = DeskDupl->GetFramePointerShape(FrameInfo->PointerShapeBufferSize, reinterpret_cast<VOID*>(PtrInfo->PtrShapeBuffer), &BufferSizeRequired, &(PtrInfo->ShapeInfo));
if (FAILED(hr))
{
if (hr != DXGI_ERROR_ACCESS_LOST)
{
DisplayErr(L"Failed to get frame pointer shape in DUPLICATIONMANAGER", L"Error", hr);
}
delete [] PtrInfo->PtrShapeBuffer;
PtrInfo->PtrShapeBuffer = NULL;
PtrInfo->BufferSize = 0;
return hr;
}
return hr;
}
Topik terkait