Compare commits

..

23 Commits
a5 ... a7

Author SHA1 Message Date
Geoffrey McRae
3784e9c06f [client] added support for new header format 2017-12-17 05:21:02 +11:00
Geoffrey McRae
24709ef186 [host] dxgi: set the pitch on new frames 2017-12-17 05:20:40 +11:00
Geoffrey McRae
db59e67ddb [host] update capture classes to return the pitch 2017-12-17 05:05:56 +11:00
Geoffrey McRae
1815387bbb [host] update to use the new header format 2017-12-17 05:03:47 +11:00
Geoffrey McRae
6595374a2c [common] cleanup header format and move resettable members 2017-12-17 05:03:16 +11:00
Geoffrey McRae
9d8f3a79b0 [host] detect switch user event and wait instead of terminating
Fixes #22
2017-12-17 04:50:04 +11:00
Geoffrey McRae
bf85358715 Revert "[host] simplify dll loading"
This reverts commit b89a8fee04.
2017-12-17 04:04:25 +11:00
Geoffrey McRae
b77a34b8b6 [host] DXGI: fix Secure Desktop wait logic
Fixes #20
2017-12-17 02:27:48 +11:00
Geoffrey McRae
287b983d27 [client] opengl: fixed broken mipmap logic
mipmapping is turned off for images that are scaled up a it degrades the
output quality.
2017-12-16 11:25:01 +11:00
Geoffrey McRae
7c5b2b5c1c [host] dxgi: re-send the last frame if capture times out
This change prevents the guest from stalling on startup if there are no
frames being captured
2017-12-16 10:24:37 +11:00
Geoffrey McRae
758b7af754 [host] use a local copy of the header and then update it all in one go
Writing to shared memory is much faster then reading as the shared
memory is not cached, this change ensures we are using a local copy
of the header performing the final update all in one go.
2017-12-16 10:06:55 +11:00
jmossman
b89a8fee04 [host] simplify dll loading 2017-12-16 07:01:41 +11:00
Geoffrey McRae
2bb8b0227c [client] don't send renderer mouse events until it's configured 2017-12-15 19:14:02 +11:00
Geoffrey McRae
ae4156d041 [client] don't update mouse scaling values until started 2017-12-15 19:14:02 +11:00
Patrick Steinhardt
fe337cf510 [client] ivshmem: fix missing <sys/select.h> include
While the function `ivshmem_wait_irq` makes use of the select(3)
function, it does not include <sys/select.h>. This happens to work on
glibc based systems, which include thet file transitively via other
header files. But on musl libc based systems, this breaks compilation.

Directly include <sys/select.h> to fix the problem.
2017-12-15 18:25:21 +11:00
Geoffrey McRae
7bfed41523 [client] opengl: update mouse draw time when doing decoupled draws 2017-12-15 17:03:51 +11:00
Geoffrey McRae
9bb66b7bd6 [client] opengl: decouple mouse updates from vsync 2017-12-15 16:58:21 +11:00
Geoffrey McRae
f7420317f1 [client] opengl: mouse shape updates bypass the draw timeout 2017-12-15 16:53:26 +11:00
Geoffrey McRae
c1379a45d2 [client] opengl: increase maximum mouse draw frequency 2017-12-15 16:34:29 +11:00
Geoffrey McRae
9c03327701 [client] opengl: added back double buffering and vsync support
This adds back in double buffering and vsync support. This has been
carefully implemented so that the render function blocks until the video
card reports that it has advanced a frame, this ensures that the OpenGL
pipeline never buffers frames.
2017-12-15 16:21:38 +11:00
Geoffrey McRae
0d8b2449cf [client] added back missing vsync disable option 2017-12-15 16:19:47 +11:00
Arti Zirk
d1bd5b3115 Do not minimize fullscreen window on focus loss 2017-12-15 15:59:28 +11:00
Jack Karamanian
e03621a622 [client] Add borderless fullscreen usage 2017-12-15 15:59:09 +11:00
11 changed files with 314 additions and 154 deletions

View File

@@ -30,6 +30,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/mman.h>
@@ -544,4 +545,4 @@ bool ivshmem_kick_irq(uint16_t clientID, uint16_t vector)
DEBUG_ERROR("failed to send kick");
return false;
}
}

View File

@@ -41,6 +41,7 @@ typedef struct LG_RendererParams
TTF_Font * font;
bool showFPS;
bool resample;
bool vsync;
}
LG_RendererParams;
@@ -97,4 +98,4 @@ typedef struct LG_Renderer
LG_RendererOnFrameEvent on_frame_event;
LG_RendererRender render;
}
LG_Renderer;
LG_Renderer;

View File

@@ -50,6 +50,7 @@ struct AppState
SDL_Point srcSize;
LG_RendererRect dstRect;
SDL_Point cursor;
bool cursorVisible;
bool haveCursorPos;
float scaleX, scaleY;
@@ -63,6 +64,7 @@ struct AppState
struct AppParams
{
bool vsync;
bool autoResize;
bool allowResize;
bool keepAspect;
@@ -84,6 +86,7 @@ struct AppParams
struct AppState state;
struct AppParams params =
{
.vsync = true,
.autoResize = false,
.allowResize = true,
.keepAspect = true,
@@ -136,8 +139,11 @@ inline void updatePositionInfo()
state.dstRect.h = h;
}
state.scaleX = (float)state.srcSize.y / (float)state.dstRect.h;
state.scaleY = (float)state.srcSize.x / (float)state.dstRect.w;
if (state.started)
{
state.scaleX = (float)state.srcSize.y / (float)state.dstRect.h;
state.scaleY = (float)state.srcSize.x / (float)state.dstRect.w;
}
if (state.lgr)
state.lgr->on_resize(state.lgrData, w, h, state.dstRect);
@@ -147,6 +153,12 @@ int renderThread(void * unused)
{
bool error = false;
struct KVMFRHeader header;
LG_RendererCursor cursorType = LG_CURSOR_COLOR;
struct KVMFRFrame cursor = {};
size_t cursorDataSize = 0;
uint8_t * cursorData = NULL;
volatile uint32_t * updateCount = &state.shm->updateCount;
memset(&header, 0, sizeof(struct KVMFRHeader));
@@ -191,12 +203,14 @@ int renderThread(void * unused)
{
// sainty check of the frame format
if (
header.frame.type >= FRAME_TYPE_MAX ||
header.frame.width == 0 ||
header.frame.height == 0 ||
header.frame.stride == 0 ||
header.frame.dataPos == 0 ||
header.frame.dataPos > state.shmSize
header.detail.frame.type >= FRAME_TYPE_MAX ||
header.detail.frame.width == 0 ||
header.detail.frame.height == 0 ||
header.detail.frame.stride == 0 ||
header.detail.frame.pitch == 0 ||
header.detail.frame.dataPos == 0 ||
header.detail.frame.dataPos > state.shmSize ||
header.detail.frame.pitch < header.detail.frame.width
){
usleep(1000);
continue;
@@ -204,19 +218,18 @@ int renderThread(void * unused)
// setup the renderer format with the frame format details
LG_RendererFormat lgrFormat;
lgrFormat.width = header.frame.width;
lgrFormat.height = header.frame.height;
lgrFormat.stride = header.frame.stride;
lgrFormat.width = header.detail.frame.width;
lgrFormat.height = header.detail.frame.height;
lgrFormat.stride = header.detail.frame.stride;
lgrFormat.pitch = header.detail.frame.pitch;
switch(header.frame.type)
switch(header.detail.frame.type)
{
case FRAME_TYPE_ARGB:
lgrFormat.pitch = header.frame.stride * 4;
lgrFormat.bpp = 32;
break;
case FRAME_TYPE_RGB:
lgrFormat.pitch = header.frame.stride * 3;
lgrFormat.bpp = 24;
break;
@@ -231,7 +244,7 @@ int renderThread(void * unused)
// check the header's dataPos is sane
const size_t dataSize = lgrFormat.height * lgrFormat.pitch;
if (header.frame.dataPos + dataSize > state.shmSize)
if (header.detail.frame.dataPos + dataSize > state.shmSize)
{
DEBUG_ERROR("The guest sent an invalid dataPos");
break;
@@ -250,14 +263,49 @@ int renderThread(void * unused)
break;
}
state.srcSize.x = header.frame.width;
state.srcSize.y = header.frame.height;
state.srcSize.x = header.detail.frame.width;
state.srcSize.y = header.detail.frame.height;
if (params.autoResize)
SDL_SetWindowSize(state.window, header.frame.width, header.frame.height);
SDL_SetWindowSize(state.window, header.detail.frame.width, header.detail.frame.height);
state.started = true;
updatePositionInfo();
// if we have saved shape info, send it now
if (cursorData)
{
if (!state.lgr->on_mouse_shape(
state.lgrData,
cursorType,
cursor.width,
cursor.height,
cursor.pitch,
cursorData
))
{
DEBUG_ERROR("Failed to update mouse shape");
break;
}
free(cursorData);
cursorData = NULL;
cursorDataSize = 0;
}
// if we have a cursor position, send it now
if (state.haveCursorPos)
{
state.lgr->on_mouse_event
(
state.lgrData,
state.cursorVisible,
state.cursor.x,
state.cursor.y
);
}
}
const uint8_t * data = (const uint8_t *)state.shm + header.frame.dataPos;
const uint8_t * data = (const uint8_t *)state.shm + header.detail.frame.dataPos;
if (!state.lgr->on_frame_event(state.lgrData, data))
{
DEBUG_ERROR("Failed to render the frame");
@@ -268,45 +316,64 @@ int renderThread(void * unused)
// if we have cursor data
if (header.flags & KVMFR_HEADER_FLAG_CURSOR)
{
if (header.cursor.flags & KVMFR_CURSOR_FLAG_POS)
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_POS)
{
state.cursor.x = header.cursor.x;
state.cursor.y = header.cursor.y;
state.cursor.x = header.detail.cursor.x;
state.cursor.y = header.detail.cursor.y;
state.cursorVisible = header.detail.cursor.flags & KVMFR_CURSOR_FLAG_VISIBLE;
state.haveCursorPos = true;
}
if (header.cursor.flags & KVMFR_CURSOR_FLAG_SHAPE)
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_SHAPE)
{
LG_RendererCursor c = LG_CURSOR_COLOR;
switch(header.cursor.type)
{
case CURSOR_TYPE_COLOR : c = LG_CURSOR_COLOR ; break;
case CURSOR_TYPE_MONOCHROME : c = LG_CURSOR_MONOCHROME ; break;
case CURSOR_TYPE_MASKED_COLOR: c = LG_CURSOR_MASKED_COLOR; break;
default:
DEBUG_ERROR("Invalid cursor type");
break;
}
if (state.lgr)
{
bool bad = false;
switch(header.detail.cursor.type)
{
case CURSOR_TYPE_COLOR : cursorType = LG_CURSOR_COLOR ; break;
case CURSOR_TYPE_MONOCHROME : cursorType = LG_CURSOR_MONOCHROME ; break;
case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break;
default:
DEBUG_ERROR("Invalid cursor type");
bad = true;
break;
}
if (bad)
break;
// check the data position is sane
const uint64_t dataSize = header.cursor.h * header.cursor.pitch;
if (header.cursor.dataPos + dataSize > state.shmSize)
const uint64_t dataSize = header.detail.frame.height * header.detail.frame.pitch;
if (header.detail.frame.dataPos + dataSize > state.shmSize)
{
DEBUG_ERROR("The guest sent an invalid mouse dataPos");
break;
}
const uint8_t * data = (const uint8_t *)state.shm + header.cursor.dataPos;
const uint8_t * data = (const uint8_t *)state.shm + header.detail.frame.dataPos;
if (!state.started)
{
// save off the cursor data so we can tell the renderer when it's ready
if (cursorDataSize < dataSize)
{
if (cursorData) free(cursorData);
cursorData = (uint8_t *)malloc(dataSize);
cursorDataSize = dataSize;
}
memcpy(&cursor, &header.detail.frame, sizeof(struct KVMFRFrame));
memcpy(cursorData, data, dataSize);
continue;
}
if (!state.lgr->on_mouse_shape(
state.lgrData,
c,
header.cursor.w,
header.cursor.h,
header.cursor.pitch,
data
))
cursorType,
header.detail.frame.width,
header.detail.frame.height,
header.detail.frame.pitch,
data)
)
{
DEBUG_ERROR("Failed to update mouse shape");
break;
@@ -314,21 +381,24 @@ int renderThread(void * unused)
}
}
if (state.lgr)
if (state.lgr && state.started)
{
state.lgr->on_mouse_event(
state.lgrData,
(header.cursor.flags & KVMFR_CURSOR_FLAG_VISIBLE) != 0,
state.cursorVisible,
state.cursor.x,
state.cursor.y
);
}
}
if (state.lgr)
if (state.started && state.lgr)
state.lgr->render(state.lgrData);
}
if (cursorData)
free(cursorData);
return 0;
}
@@ -631,6 +701,7 @@ int run()
lgrParams.font = state.font;
lgrParams.resample = params.useMipmap;
lgrParams.showFPS = params.showFPS;
lgrParams.vsync = params.vsync;
Uint32 sdlFlags;
// probe for a a suitable renderer
@@ -676,6 +747,11 @@ int run()
)
);
if (params.fullscreen)
{
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
}
// set the compositor hint to bypass for low latency
SDL_SysWMinfo wminfo;
SDL_VERSION(&wminfo.version);
@@ -743,8 +819,7 @@ int run()
DEBUG_ERROR("Failed to map memory");
break;
}
state.shmSize = ivshmem_get_map_size();
state.shm->hostID = ivshmem_get_id();
state.shmSize = ivshmem_get_map_size();
if (params.useSpice)
{
@@ -841,13 +916,14 @@ void doHelp(char * app)
" -M Don't hide the host cursor\n"
"\n"
" -m Disable mipmapping\n"
" -v Disable VSync\n"
" -v Disable VSYNC\n"
" -k Enable FPS display\n"
"\n"
" -a Auto resize the window to the guest\n"
" -n Don't allow the window to be manually resized\n"
" -r Don't maintain the aspect ratio\n"
" -d Borderless mode\n"
" -F Borderless fullscreen mode\n"
" -x XPOS Initial window X position [current: %s]\n"
" -y YPOS Initial window Y position [current: %s]\n"
" -w WIDTH Initial window width [current: %u]\n"
@@ -894,7 +970,7 @@ void doLicense()
int main(int argc, char * argv[])
{
int c;
while((c = getopt(argc, argv, "hf:sc:p:jMmkanrdFx:y:w:b:l")) != -1)
while((c = getopt(argc, argv, "hf:sc:p:jMmvkanrdFx:y:w:b:l")) != -1)
switch(c)
{
case '?':
@@ -931,6 +1007,10 @@ int main(int argc, char * argv[])
params.useMipmap = false;
break;
case 'v':
params.vsync = false;
break;
case 'k':
params.showFPS = true;
break;

View File

@@ -47,6 +47,7 @@ struct LGR_OpenGL
{
LG_RendererParams params;
bool configured;
SDL_Window * sdlWindow;
SDL_GLContext glContext;
bool doneInfo;
@@ -68,7 +69,6 @@ struct LGR_OpenGL
int fpsList;
int mouseList;
LG_RendererRect destRect;
bool mipmap;
bool hasTextures;
GLuint textures[TEXTURE_COUNT];
@@ -81,6 +81,7 @@ struct LGR_OpenGL
SDL_Rect fpsRect;
bool mouseUpdate;
bool newShape;
uint64_t lastMouseDraw;
LG_RendererCursor mouseType;
bool mouseVisible;
@@ -131,7 +132,7 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, Uint3
}
*sdlFlags = SDL_WINDOW_OPENGL;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
return true;
}
@@ -147,6 +148,7 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
return false;
}
this->sdlWindow = window;
this->glContext = SDL_GL_CreateContext(window);
if (!this->glContext)
{
@@ -168,7 +170,7 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
return false;
}
SDL_GL_SetSwapInterval(0);
SDL_GL_SetSwapInterval(this->params.vsync ? 1 : 0);
// check if the GPU supports GL_ARB_buffer_storage first
// there is no advantage to this renderer if it is not present.
@@ -362,7 +364,7 @@ void lgr_opengl_on_resize(void * opaque, const int width, const int height, cons
if (!this || !this->configured)
return;
this->window.x = width;
this->window.x = width;
this->window.y = height;
memcpy(&this->destRect, &destRect, sizeof(LG_RendererRect));
@@ -491,6 +493,7 @@ bool lgr_opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, co
}
this->mouseUpdate = true;
this->newShape = true;
return true;
}
@@ -630,15 +633,17 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data)
(this->format.width > this->destRect.w) ||
(this->format.height > this->destRect.h));
if (this->mipmap != mipmap)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
this->mipmap = mipmap;
}
if (mipmap)
{
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
glBindTexture(GL_TEXTURE_2D, 0);
@@ -700,15 +705,27 @@ bool lgr_opengl_render(void * opaque)
if (!this->mouseUpdate)
return true;
// don't update the mouse too fast
const uint64_t delta = nanotime() - this->lastMouseDraw;
if (delta < 1e7)
return true;
}
if (!this->newShape)
{
// don't update the mouse too fast
const uint64_t delta = nanotime() - this->lastMouseDraw;
if (delta < 5e6)
return true;
}
this->newShape = false;
// wait for vsync
unsigned int count;
glXWaitVideoSyncSGI(1, 0, &count);
glDrawBuffer(GL_FRONT);
glCallList(this->texList + this->texIndex);
lgr_opengl_draw_mouse(this);
if (this->fpsTexture)
glCallList(this->fpsList);
glDrawBuffer(GL_BACK);
glFlush();
this->mouseUpdate = false;
this->lastMouseDraw = nanotime();
return true;
}
glDisable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT);
@@ -718,7 +735,15 @@ bool lgr_opengl_render(void * opaque)
lgr_opengl_draw_mouse(this);
if (this->fpsTexture)
glCallList(this->fpsList);
glFlush();
unsigned int before, after;
glXGetVideoSyncSGI(&before);
SDL_GL_SwapWindow(this->sdlWindow);
// wait for the swap to happen to ensure we dont buffer frames
glXGetVideoSyncSGI(&after);
if (before == after)
glXWaitVideoSyncSGI(1, 0, &before);
++this->frameCount;
const uint64_t t = nanotime();

View File

@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdint.h>
#define KVMFR_HEADER_MAGIC "[[KVMFR]]"
#define KVMFR_HEADER_VERSION 3
#define KVMFR_HEADER_VERSION 4
typedef enum FrameType
{
@@ -49,9 +49,6 @@ typedef struct KVMFRCursor
uint8_t flags; // KVMFR_CURSOR_FLAGS
int16_t x, y; // cursor x & y position
CursorType type; // shape buffer data type
uint8_t w, h; // shape width and height
uint8_t pitch; // shape row length in bytes
uint64_t dataPos; // offset to the cursor data
}
KVMFRCursor;
@@ -60,7 +57,8 @@ typedef struct KVMFRFrame
FrameType type; // the frame data type
uint32_t width; // the width
uint32_t height; // the height
uint32_t stride; // the row stride
uint32_t stride; // the row stride (zero if cursor data)
uint32_t pitch; // the row pitch (stride in bytes)
uint64_t dataPos; // offset to the frame
}
KVMFRFrame;
@@ -70,16 +68,19 @@ KVMFRFrame;
#define KVMFR_HEADER_FLAG_RESTART 4 // restart signal from client
#define KVMFR_HEADER_FLAG_READY 8 // ready signal from client
typedef struct KVMFRDetail
{
KVMFRFrame frame; // the frame information
KVMFRCursor cursor; // the cursor information
}
KVMFRDetail;
typedef struct KVMFRHeader
{
char magic[sizeof(KVMFR_HEADER_MAGIC)];
uint32_t version; // version of this structure
uint16_t hostID; // the host ivshmem client id
uint16_t guestID; // the guest ivshmem client id
uint32_t updateCount; // updated each change
uint8_t flags; // KVMFR_HEADER_FLAGS
KVMFRFrame frame; // the frame information
KVMFRCursor cursor; // the cursor information
KVMFRDetail detail; // details
}
KVMFRHeader;
KVMFRHeader;

View File

@@ -31,6 +31,7 @@ DXGI::DXGI() :
m_deviceContext(),
m_dup(),
m_texture(),
m_surface(),
m_pointer(NULL)
{
}
@@ -190,6 +191,15 @@ void DXGI::DeInitialize()
m_pointerBufSize = 0;
}
if (m_surfaceMapped)
{
m_surface->Unmap();
m_surfaceMapped = false;
}
if (m_surface)
m_surface.Release();
if (m_texture)
m_texture.Release();
@@ -241,7 +251,23 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
{
while(true)
{
status = m_dup->AcquireNextFrame(INFINITE, &frameInfo, &res);
status = m_dup->AcquireNextFrame(1000, &frameInfo, &res);
if (status == DXGI_ERROR_WAIT_TIMEOUT)
{
if (!m_surfaceMapped)
break;
// send the last frame again if we timeout to prevent the client stalling on restart
frame.width = m_desc.Width;
frame.height = m_desc.Height;
frame.pitch = m_rect.Pitch;
frame.stride = m_rect.Pitch / 4;
unsigned int size = m_height * m_rect.Pitch;
memcpySSE(frame.buffer, m_rect.pBits, size < frame.bufferSize ? size : frame.bufferSize);
return GRAB_STATUS_OK;
}
if (!SUCCEEDED(status))
break;
@@ -321,32 +347,34 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
if (SUCCEEDED(status))
break;
HDESK desktop = NULL;
switch (status)
{
case DXGI_ERROR_ACCESS_LOST: // desktop switch, mode change or switch DWM on or off
return GRAB_STATUS_REINIT;
// desktop switch, mode change, switch DWM on or off or Secure Desktop
case DXGI_ERROR_ACCESS_LOST:
// see if we can open the desktop, if so request a reinit
// if not the secure desktop is active so just wait for it
// instead of aborting out
desktop = OpenInputDesktop(0, TRUE, GENERIC_READ);
if (desktop)
{
// open suceeded, not on the secure desktop, return to reinit
CloseDesktop(desktop);
return GRAB_STATUS_REINIT;
}
// fall through
case WAIT_ABANDONED: // this can happen also during desktop switches, not documented by MS though
{
// see if we can open the desktop, if not the secure desktop
// is active so just wait for it instead of aborting out
HDESK desktop = NULL;
while(!desktop)
// this can happen during desktop switches also, not documented by MS though
case WAIT_ABANDONED:
do
{
desktop = OpenInputDesktop(0, TRUE, GENERIC_READ);
if (desktop)
break;
Sleep(100);
}
} while (!desktop);
CloseDesktop(desktop);
if (!ReInitialize())
{
DEBUG_ERROR("Failed to ReInitialize after lost access to desktop");
return GRAB_STATUS_ERROR;
}
continue;
}
return GRAB_STATUS_REINIT;
default:
// unknown failure
@@ -371,8 +399,7 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
return GRAB_STATUS_ERROR;
}
D3D11_TEXTURE2D_DESC desc;
src->GetDesc(&desc);
src->GetDesc(&m_desc);
m_deviceContext->CopyResource(m_texture, src);
@@ -380,38 +407,43 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
res.Release();
src.Release();
IDXGISurface1Ptr surface(m_texture);
if (!surface)
if (m_surfaceMapped)
{
status = m_surface->Unmap();
if (FAILED(status))
{
DEBUG_ERROR("Failed to unmap surface: %08x", (int)status);
return GRAB_STATUS_ERROR;
}
m_surfaceMapped = false;
m_surface.Release();
}
m_surface = m_texture;
if (!m_surface)
{
DEBUG_ERROR("Failed to get IDXGISurface1");
return GRAB_STATUS_ERROR;
}
DXGI_MAPPED_RECT rect;
status = surface->Map(&rect, DXGI_MAP_READ);
status = m_surface->Map(&m_rect, DXGI_MAP_READ);
if (FAILED(status))
{
DEBUG_ERROR("Failed to map surface: %08x", (int)status);
return GRAB_STATUS_ERROR;
}
m_surfaceMapped = true;
m_width = desc.Width;
m_height = desc.Height;
m_width = m_desc.Width;
m_height = m_desc.Height;
frame.width = desc.Width;
frame.height = desc.Height;
frame.stride = rect.Pitch / 4;
frame.width = m_desc.Width;
frame.height = m_desc.Height;
frame.pitch = m_rect.Pitch;
frame.stride = m_rect.Pitch / 4;
unsigned int size = m_height * rect.Pitch;
memcpySSE(frame.buffer, rect.pBits, size < frame.bufferSize ? size : frame.bufferSize);
status = surface->Unmap();
if (FAILED(status))
{
DEBUG_ERROR("Failed to unmap surface: %08x", (int)status);
return GRAB_STATUS_ERROR;
}
unsigned int size = m_height * m_rect.Pitch;
memcpySSE(frame.buffer, m_rect.pBits, size < frame.bufferSize ? size : frame.bufferSize);
return GRAB_STATUS_OK;
}

View File

@@ -80,6 +80,10 @@ namespace Capture
IDXGIOutput1Ptr m_output;
IDXGIOutputDuplicationPtr m_dup;
ID3D11Texture2DPtr m_texture;
IDXGISurface1Ptr m_surface;
D3D11_TEXTURE2D_DESC m_desc;
DXGI_MAPPED_RECT m_rect;
bool m_surfaceMapped;
BYTE * m_pointer;
UINT m_pointerBufSize;
UINT m_pointerSize;

View File

@@ -289,6 +289,7 @@ enum GrabStatus NvFBC::GrabFrame(struct FrameInfo & frame)
}
frame.stride = frame.width;
frame.pitch = dataWidth;
uint8_t *src = (uint8_t *)m_frameBuffer + dataOffset;
uint8_t *dst = (uint8_t *)frame.buffer;
for(unsigned int y = 0; y < frame.height; ++y, dst += dataWidth, src += m_grabInfo.dwBufferWidth * 4)

View File

@@ -42,6 +42,7 @@ struct FrameInfo
unsigned int width;
unsigned int height;
unsigned int stride;
unsigned int pitch;
void * buffer;
size_t bufferSize;

View File

@@ -40,12 +40,13 @@ Service::Service() :
m_memory(NULL),
m_timer(NULL),
m_capture(NULL),
m_header(NULL),
m_shmHeader(NULL),
m_frameIndex(0),
m_cursorDataSize(0),
m_cursorData(NULL),
m_shapePending(false)
{
m_consoleSessionID = WTSGetActiveConsoleSessionId();
m_ivshmem = IVSHMEM::Get();
}
@@ -92,15 +93,13 @@ bool Service::Initialize(ICapture * captureDevice)
}
// update everything except for the hostID
memcpy(m_header->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC));
m_header->version = KVMFR_HEADER_VERSION;
m_header->guestID = m_ivshmem->GetPeerID();
m_header->updateCount = 0;
memcpy(m_shmHeader->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC));
m_shmHeader->version = KVMFR_HEADER_VERSION;
m_shmHeader->updateCount = 0;
// clear but retain the restart flag if it was set by the client
INTERLOCKED_AND8((char *)&m_header->flags, KVMFR_HEADER_FLAG_RESTART);
ZeroMemory(&m_header->frame , sizeof(KVMFRFrame ));
ZeroMemory(&m_header->cursor, sizeof(KVMFRCursor));
INTERLOCKED_AND8((char *)&m_shmHeader->flags, KVMFR_HEADER_FLAG_RESTART);
ZeroMemory(&m_shmHeader->detail, sizeof(KVMFRDetail));
m_initialized = true;
return true;
@@ -108,7 +107,7 @@ bool Service::Initialize(ICapture * captureDevice)
bool Service::InitPointers()
{
m_header = reinterpret_cast<KVMFRHeader *>(m_memory);
m_shmHeader = reinterpret_cast<KVMFRHeader *>(m_memory);
m_frame[0] = (uint8_t *)(((uintptr_t)m_memory + sizeof(KVMFRHeader *) + 0x7F) & ~0x7F);
m_frameSize = ((m_ivshmem->GetSize() - (m_frame[0] - m_memory)) & ~0x7F) >> 1;
m_frame[1] = m_frame[0] + m_frameSize;
@@ -142,7 +141,7 @@ void Service::DeInitialize()
m_cursorData = NULL;
}
m_header = NULL;
m_shmHeader = NULL;
m_frame[0] = NULL;
m_frame[1] = NULL;
m_dataOffset[0] = 0;
@@ -171,7 +170,7 @@ bool Service::Process()
frame.buffer = m_frame[m_frameIndex];
frame.bufferSize = m_frameSize;
volatile uint8_t *flags = &m_header->flags;
volatile uint8_t *flags = &m_shmHeader->flags;
// wait for the host to notify that is it is ready to proceed
while (true)
@@ -181,7 +180,7 @@ bool Service::Process()
// check if the client has flagged a restart
if (f & KVMFR_HEADER_FLAG_RESTART)
{
m_header->updateCount = 0;
m_shmHeader->updateCount = 0;
INTERLOCKED_AND8((volatile char *)flags, ~(KVMFR_HEADER_FLAG_RESTART));
restart = true;
break;
@@ -233,6 +232,13 @@ bool Service::Process()
case GRAB_STATUS_REINIT:
DEBUG_INFO("ReInitialize Requested");
if(WTSGetActiveConsoleSessionId() != m_consoleSessionID)
{
DEBUG_INFO("User switch detected, waiting to regain control");
while (WTSGetActiveConsoleSessionId() != m_consoleSessionID)
Sleep(100);
}
if (!m_capture->ReInitialize() || !InitPointers())
{
DEBUG_ERROR("ReInitialize Failed");
@@ -252,17 +258,18 @@ bool Service::Process()
}
uint8_t updateFlags = 0;
m_header->cursor.flags = 0;
m_detail.cursor.flags = 0;
if (!cursorOnly)
{
// signal a frame update
updateFlags |= KVMFR_HEADER_FLAG_FRAME;
m_header->frame.type = m_capture->GetFrameType();
m_header->frame.width = frame.width;
m_header->frame.height = frame.height;
m_header->frame.stride = frame.stride;
m_header->frame.dataPos = m_dataOffset[m_frameIndex];
m_detail.frame.type = m_capture->GetFrameType();
m_detail.frame.width = frame.width;
m_detail.frame.height = frame.height;
m_detail.frame.stride = frame.stride;
m_detail.frame.pitch = frame.pitch;
m_detail.frame.dataPos = m_dataOffset[m_frameIndex];
if (++m_frameIndex == 2)
m_frameIndex = 0;
}
@@ -280,11 +287,11 @@ bool Service::Process()
// tell the host where the cursor is
updateFlags |= KVMFR_HEADER_FLAG_CURSOR;
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_POS;
m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_POS;
if (m_cursor.visible)
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
m_header->cursor.x = m_cursor.x;
m_header->cursor.y = m_cursor.y;
m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
m_detail.cursor.x = m_cursor.x;
m_detail.cursor.y = m_cursor.y;
}
if (frame.cursor.hasShape || m_shapePending || (m_cursor.hasShape && restart))
@@ -323,15 +330,17 @@ bool Service::Process()
{
// give the host the new cursor shape
updateFlags |= KVMFR_HEADER_FLAG_CURSOR;
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_SHAPE;
m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_SHAPE;
if (m_cursor.visible)
m_header->cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE;
m_header->cursor.type = m_cursor.type;
m_header->cursor.w = m_cursor.w;
m_header->cursor.h = m_cursor.h;
m_header->cursor.pitch = m_cursor.pitch;
m_header->cursor.dataPos = m_dataOffset[m_frameIndex];
// shapes are sent as frame data
m_detail.cursor.type = m_cursor.type;
m_detail.frame.width = m_cursor.w;
m_detail.frame.height = m_cursor.h;
m_detail.frame.stride = 0;
m_detail.frame.pitch = m_cursor.pitch;
m_detail.frame.dataPos = m_dataOffset[m_frameIndex];
memcpy(m_frame[m_frameIndex], m_cursorData, m_cursor.dataSize);
m_shapePending = false;
@@ -344,8 +353,11 @@ bool Service::Process()
INTERLOCKED_AND8((volatile char *)flags, KVMFR_HEADER_FLAG_RESTART);
INTERLOCKED_OR8 ((volatile char *)flags, updateFlags);
// increment the update count to resume the host
++m_header->updateCount;
// update the shared details only
memcpy(&m_shmHeader->detail, &m_detail, sizeof(KVMFRDetail));
// increment the update count so the guest stops waiting
++m_shmHeader->updateCount;
return true;
}

View File

@@ -49,12 +49,14 @@ private:
~Service();
bool m_initialized;
DWORD m_consoleSessionID;
uint8_t * m_memory;
IVSHMEM * m_ivshmem;
HANDLE m_timer;
ICapture * m_capture;
KVMFRHeader * m_header;
KVMFRDetail m_detail;
KVMFRHeader * m_shmHeader;
uint8_t * m_frame[2];
size_t m_frameSize;
uint64_t m_dataOffset[2];