/* Copyright (C) 2009 Red Hat, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "os_dep.h" #include "res.h" #include "ioaccess.h" #include "utils.h" #include "mspace.h" #include "quic.h" #include "murmur_hash2a.h" #include "surface.h" #include "rop.h" #include "devioctl.h" #include "ntddvdeo.h" #if (WINVER < 0x0501) #define WAIT_FOR_EVENT(pdev, event, timeout) (pdev)->WaitForEvent(event, timeout) #else #define WAIT_FOR_EVENT(pdev, event, timeout) EngWaitForSingleObject(event, timeout) #endif #define SURFACE_ALLOC_RELEASE_BUNCH_SIZE 3 static _inline QXLPHYSICAL PA(PDev *pdev, PVOID virt, UINT8 slot_id) { PMemSlot *p_slot = &pdev->mem_slots[slot_id]; return p_slot->high_bits | ((UINT64)virt - p_slot->slot.start_virt_addr); } static _inline UINT64 VA(PDev *pdev, QXLPHYSICAL paddr, UINT8 slot_id) { UINT64 virt; PMemSlot *p_slot = &pdev->mem_slots[slot_id]; ASSERT(pdev, (paddr >> (64 - pdev->slot_id_bits)) == slot_id); ASSERT(pdev, ((paddr << pdev->slot_id_bits) >> (64 - pdev->slot_gen_bits)) == p_slot->slot.generation); virt = paddr & pdev->va_slot_mask; virt += p_slot->slot.start_virt_addr;; return virt; } #define RELEASE_RES(pdev, res) if (!--(res)->refs) (res)->free(pdev, res); #define GET_RES(res) (++(res)->refs) typedef struct Resource Resource; struct Resource { UINT32 refs; void (*free)(PDev *pdev, Resource *res); UINT8 res[0]; }; static void FreeMem(PDev* pdev, UINT32 mspace_type, void *ptr); static BOOL SetClip(PDev *pdev, CLIPOBJ *clip, QXLDrawable *drawable); #define PUSH_CMD(pdev) do { \ int notify; \ EngAcquireSemaphore(pdev->cmd_sem); \ SPICE_RING_PUSH(pdev->cmd_ring, notify); \ if (notify) { \ WRITE_PORT_UCHAR(pdev->notify_cmd_port, 0); \ } \ EngReleaseSemaphore(pdev->cmd_sem); \ } while (0); #define PUSH_CURSOR_CMD(pdev) do { \ int notify; \ SPICE_RING_PUSH(pdev->cursor_ring, notify); \ if (notify) { \ WRITE_PORT_UCHAR(pdev->notify_cursor_port, 0); \ } \ } while (0); #define MAX_OUTPUT_RES 6 typedef struct QXLOutput { UINT32 num_res; Resource *resources[MAX_OUTPUT_RES]; UINT8 data[0]; } QXLOutput; static int have_sse2 = FALSE; UINT64 ReleaseOutput(PDev *pdev, UINT64 output_id) { QXLOutput *output = (QXLOutput *)output_id; Resource **now; Resource **end; UINT64 next; ASSERT(pdev, output_id); DEBUG_PRINT((pdev, 9, "%s 0x%x\n", __FUNCTION__, output)); for (now = output->resources, end = now + output->num_res; now < end; now++) { RELEASE_RES(pdev, *now); } next = *(UINT64*)output->data; FreeMem(pdev, MSPACE_TYPE_DEVRAM, output); DEBUG_PRINT((pdev, 10, "%s done\n", __FUNCTION__)); ONDBG(pdev->Res.num_outputs--); //todo: atomic return next; } static void AddRes(PDev *pdev, QXLOutput *output, Resource *res) { DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); ASSERT(pdev, output->num_res < MAX_OUTPUT_RES); res->refs++; output->resources[output->num_res++] = res; DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); } static _inline void DrawableAddRes(PDev *pdev, QXLDrawable *drawable, Resource *res) { QXLOutput *output; output = (QXLOutput *)((UINT8 *)drawable - sizeof(QXLOutput)); AddRes(pdev, output, res); } static _inline void SurfaceAddRes(PDev *pdev, QXLSurfaceCmd *surface, Resource *res) { QXLOutput *output; output = (QXLOutput *)((UINT8 *)surface - sizeof(QXLOutput)); AddRes(pdev, output, res); } static _inline void CursorCmdAddRes(PDev *pdev, QXLCursorCmd *cmd, Resource *res) { QXLOutput *output; output = (QXLOutput *)((UINT8 *)cmd - sizeof(QXLOutput)); AddRes(pdev, output, res); } static void WaitForCursorRing(PDev* pdev) { int wait; DEBUG_PRINT((pdev, 9, "%s: 0x%lx\n", __FUNCTION__, pdev)); for (;;) { SPICE_RING_PROD_WAIT(pdev->cursor_ring, wait); if (!wait) { break; } #ifdef DBG { LARGE_INTEGER timeout; // 1 => 100 nanoseconds timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s #if (WINVER < 0x0501) pdev->WaitForEvent(pdev->cursor_event, &timeout); #else EngWaitForSingleObject(pdev->cursor_event, &timeout); #endif // (WINVER < 0x0501) if (SPICE_RING_IS_FULL(pdev->cursor_ring)) { DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); } } #else #if (WINVER < 0x0501) pdev->WaitForEvent(pdev->cursor_event, NULL); #else EngWaitForSingleObject(pdev->cursor_event, NULL); #endif // (WINVER < 0x0501) #endif //DBG } } static void WaitForCmdRing(PDev* pdev) { int wait; DEBUG_PRINT((pdev, 9, "%s: 0x%lx\n", __FUNCTION__, pdev)); for (;;) { SPICE_RING_PROD_WAIT(pdev->cmd_ring, wait); if (!wait) { break; } #ifdef DBG { LARGE_INTEGER timeout; // 1 => 100 nanoseconds timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s #if (WINVER < 0x0501) pdev->WaitForEvent(pdev->display_event, &timeout); #else EngWaitForSingleObject(pdev->display_event, &timeout); #endif // (WINVER < 0x0501) if (SPICE_RING_IS_FULL(pdev->cmd_ring)) { DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); } } #else #if (WINVER < 0x0501) pdev->WaitForEvent(pdev->display_event, NULL); #else EngWaitForSingleObject(pdev->display_event, NULL); #endif // (WINVER < 0x0501) #endif //DBG } } static void QXLSleep(PDev* pdev, int msec) { LARGE_INTEGER timeout; DEBUG_PRINT((pdev, 18, "%s: 0x%lx msec %u\n", __FUNCTION__, pdev, msec)); timeout.QuadPart = -msec * 1000 * 10; WAIT_FOR_EVENT(pdev, pdev->sleep_event, &timeout); DEBUG_PRINT((pdev, 19, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); } static void WaitForReleaseRing(PDev* pdev) { int wait; DEBUG_PRINT((pdev, 15, "%s: 0x%lx\n", __FUNCTION__, pdev)); for (;;) { LARGE_INTEGER timeout; if (SPICE_RING_IS_EMPTY(pdev->release_ring)) { QXLSleep(pdev, 10); if (!SPICE_RING_IS_EMPTY(pdev->release_ring)) { break; } WRITE_PORT_UCHAR(pdev->notify_oom_port, 0); } SPICE_RING_CONS_WAIT(pdev->release_ring, wait); if (!wait) { break; } timeout.QuadPart = -30 * 1000 * 10; //30ms WAIT_FOR_EVENT(pdev, pdev->display_event, &timeout); if (SPICE_RING_IS_EMPTY(pdev->release_ring)) { #ifdef DBG DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); DEBUG_PRINT((pdev, 0, "\tfree %d out %d path %d rect %d bits %d\n", pdev->Res.num_free_pages, pdev->Res.num_outputs, pdev->Res.num_path_pages, pdev->Res.num_rects_pages, pdev->Res.num_bits_pages, pdev->Res.num_buf_pages, pdev->Res.num_glyphs_pages, pdev->Res.num_cursor_pages)); #endif //oom WRITE_PORT_UCHAR(pdev->notify_oom_port, 0); } } DEBUG_PRINT((pdev, 16, "%s: 0x%lx, done\n", __FUNCTION__, pdev)); } // todo: separate VRAM releases from DEVRAM releases #define AllocMem(pdev, mspace_type, size) __AllocMem(pdev, mspace_type, size, TRUE, 1) static void *__AllocMem(PDev* pdev, UINT32 mspace_type, size_t size, BOOL force, INT32 release_bunch) { UINT8 *ptr; ASSERT(pdev, pdev && pdev->Res.mspaces[mspace_type]._mspace); DEBUG_PRINT((pdev, 12, "%s: 0x%lx size %u\n", __FUNCTION__, pdev, size)); EngAcquireSemaphore(pdev->malloc_sem); while (!(ptr = mspace_malloc(pdev->Res.mspaces[mspace_type]._mspace, size))) { int notify; int num_to_release = release_bunch; while (pdev->Res.free_outputs && num_to_release) { pdev->Res.free_outputs = ReleaseOutput(pdev, pdev->Res.free_outputs); num_to_release--; } if (!num_to_release) { continue; } if (force) { WaitForReleaseRing(pdev); } else { if (SPICE_RING_IS_EMPTY(pdev->release_ring)) { break; } } pdev->Res.free_outputs = *SPICE_RING_CONS_ITEM(pdev->release_ring); SPICE_RING_POP(pdev->release_ring, notify); while (pdev->Res.free_outputs && num_to_release) { pdev->Res.free_outputs = ReleaseOutput(pdev, pdev->Res.free_outputs); num_to_release--; } } EngReleaseSemaphore(pdev->malloc_sem); ASSERT(pdev, (!ptr && !force) || (ptr >= pdev->Res.mspaces[mspace_type].mspace_start && ptr < pdev->Res.mspaces[mspace_type].mspace_end)); DEBUG_PRINT((pdev, 13, "%s: 0x%lx done 0x%x\n", __FUNCTION__, pdev, ptr)); return ptr; } static void FreeMem(PDev* pdev, UINT32 mspace_type, void *ptr) { ASSERT(pdev, pdev && pdev->Res.mspaces[mspace_type]._mspace); ASSERT(pdev, (UINT8 *)ptr >= pdev->Res.mspaces[mspace_type].mspace_start && (UINT8 *)ptr < pdev->Res.mspaces[mspace_type].mspace_end); mspace_free(pdev->Res.mspaces[mspace_type]._mspace, ptr); } DevRes *global_res = NULL; UINT8 num_global_res = 0; HSEMAPHORE res_sem = NULL; void CleanGlobalRes() { UINT32 i; if (global_res) { for (i = 0; i < num_global_res; ++i) { if (global_res[i].dynamic) { EngFreeMem(global_res[i].dynamic); global_res[i].dynamic = NULL; } if (global_res[i].surfaces_used) { EngFreeMem(global_res[i].surfaces_used); global_res[i].surfaces_used = NULL; } } EngFreeMem(global_res); global_res = NULL; } num_global_res = 0; if (res_sem) { EngDeleteSemaphore(res_sem); res_sem = NULL; } } void InitGlobalRes() { CleanGlobalRes(); res_sem = EngCreateSemaphore(); if (!res_sem) { EngDebugBreak(); } } static void InitMspace(DevRes *res, UINT32 mspace_type, UINT8 *io_pages_virt, size_t capacity) { res->mspaces[mspace_type]._mspace = create_mspace_with_base(io_pages_virt, capacity, 0, NULL); res->mspaces[mspace_type].mspace_start = io_pages_virt; res->mspaces[mspace_type].mspace_end = io_pages_virt + capacity; } static void InitRes(PDev *pdev) { UINT32 i; pdev->Res.dynamic = EngAllocMem(FL_ZERO_MEMORY, sizeof(DevResDynamic), ALLOC_TAG); if (!pdev->Res.dynamic) { PANIC(pdev, "Res dynamic allocation failed\n"); } pdev->Res.surfaces_used = EngAllocMem(FL_ZERO_MEMORY, sizeof(UINT8) * pdev->n_surfaces, ALLOC_TAG); if (!pdev->Res.surfaces_used) { PANIC(pdev, "Res surfaces_used allocation failed\n"); } pdev->Res.free_outputs = 0; InitMspace(&pdev->Res, MSPACE_TYPE_DEVRAM, pdev->io_pages_virt, pdev->num_io_pages * PAGE_SIZE); InitMspace(&pdev->Res, MSPACE_TYPE_VRAM, pdev->fb, pdev->fb_size); pdev->Res.update_id = *pdev->dev_update_id; RtlZeroMemory(pdev->Res.image_key_lookup, sizeof(pdev->Res.image_key_lookup)); RtlZeroMemory(pdev->Res.dynamic->cache_image_pool, sizeof(pdev->Res.dynamic->cache_image_pool)); RingInit(&pdev->Res.dynamic->cache_image_lru); for (i = 0; i < IMAGE_POOL_SIZE; i++) { RingAdd(pdev, &pdev->Res.dynamic->cache_image_lru, &pdev->Res.dynamic->cache_image_pool[i].lru_link); } RtlZeroMemory(pdev->Res.image_cache, sizeof(pdev->Res.image_cache)); RtlZeroMemory(pdev->Res.cursor_cache, sizeof(pdev->Res.cursor_cache)); RingInit(&pdev->Res.dynamic->cursors_lru); pdev->Res.num_cursors = 0; pdev->Res.last_cursor_id = 0; RtlZeroMemory(pdev->Res.palette_cache, sizeof(pdev->Res.palette_cache)); RingInit(&pdev->Res.dynamic->palette_lru); pdev->Res.num_palettes = 0; pdev->Res.driver = pdev->driver; ONDBG(pdev->Res.num_outputs = 0); ONDBG(pdev->Res.num_path_pages = 0); ONDBG(pdev->Res.num_rects_pages = 0); ONDBG(pdev->Res.num_bits_pages = 0); ONDBG(pdev->Res.num_buf_pages = 0); ONDBG(pdev->Res.num_glyphs_pages = 0); ONDBG(pdev->Res.num_cursor_pages = 0); #ifdef CALL_TEST pdev->Res.count_calls = TRUE; pdev->Res.total_calls = 0; for (i = 0; i < NUM_CALL_COUNTERS; i++) { pdev->Res.call_counters[i] = 0; } #endif } void InitResources(PDev *pdev) { UINT32 i; UINT32 id; DevRes *new_global_res; RtlZeroMemory(pdev->update_trace_items, sizeof(pdev->update_trace_items)); RingInit(&pdev->update_trace); for (i = 0; i < NUM_UPDATE_TRACE_ITEMS; i++) { RingAdd(pdev, &pdev->update_trace, &pdev->update_trace_items[i].link); } EngAcquireSemaphore(res_sem); id = pdev->dev_id; if (num_global_res > id) { if (!global_res[id].dynamic) { InitRes(pdev); } else { pdev->Res = global_res[id]; } EngReleaseSemaphore(res_sem); return; } new_global_res = EngAllocMem(FL_ZERO_MEMORY, (id + 1) * sizeof(DevRes), ALLOC_TAG); if (!new_global_res) { PANIC(pdev, "new_global_res malloc failed\n"); } for (i = 0; i < num_global_res; ++i) { new_global_res[i] = global_res[i]; } if (global_res) { EngFreeMem(global_res); } num_global_res = id + 1; global_res = new_global_res; InitRes(pdev); EngReleaseSemaphore(res_sem); } void SyncResources(PDev *pdev) { UINT32 id; DevRes *res; EngAcquireSemaphore(res_sem); id = pdev->dev_id; res = &global_res[id]; *res = pdev->Res; EngReleaseSemaphore(res_sem); } static QXLDrawable *GetDrawable(PDev *pdev) { QXLOutput *output; output = (QXLOutput *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(QXLOutput) + sizeof(QXLDrawable)); output->num_res = 0; ((QXLDrawable *)output->data)->release_info.id = (UINT64)output; DEBUG_PRINT((pdev, 9, "%s 0x%x\n", __FUNCTION__, output)); ONDBG(pdev->Res.num_outputs++); //todo: atomic return(QXLDrawable *)output->data; } QXLDrawable *Drawable(PDev *pdev, UINT8 type, RECTL *area, CLIPOBJ *clip, UINT32 surface_id) { QXLDrawable *drawable; ASSERT(pdev, pdev && area); drawable = GetDrawable(pdev); drawable->surface_id = surface_id; drawable->type = type; drawable->effect = QXL_EFFECT_BLEND; drawable->self_bitmap = 0; drawable->mm_time = *pdev->mm_clock; drawable->surfaces_dest[0] = -1; drawable->surfaces_dest[1] = - 1; drawable->surfaces_dest[2] = -1; CopyRect(&drawable->bbox, area); if (!SetClip(pdev, clip, drawable)) { DEBUG_PRINT((pdev, 0, "%s: set clip filed\n", __FUNCTION__)); ReleaseOutput(pdev, drawable->release_info.id); drawable = NULL; } return drawable; } void PushDrawable(PDev *pdev, QXLDrawable *drawable) { QXLCommand *cmd; WaitForCmdRing(pdev); cmd = SPICE_RING_PROD_ITEM(pdev->cmd_ring); cmd->type = QXL_CMD_DRAW; cmd->data = PA(pdev, drawable, pdev->main_mem_slot); PUSH_CMD(pdev); } static QXLSurfaceCmd *GetSurfaceCmd(PDev *pdev) { QXLOutput *output; output = (QXLOutput *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(QXLOutput) + sizeof(QXLSurfaceCmd)); output->num_res = 0; ((QXLSurfaceCmd *)output->data)->release_info.id = (UINT64)output; DEBUG_PRINT((pdev, 9, "%s 0x%x\n", __FUNCTION__, output)); ONDBG(pdev->Res.num_outputs++); //todo: atomic return(QXLSurfaceCmd *)output->data; } QXLSurfaceCmd *SurfaceCmd(PDev *pdev, UINT8 type, UINT32 surface_id) { QXLSurfaceCmd *surface_cmd; ASSERT(pdev, pdev && area); surface_cmd = GetSurfaceCmd(pdev); surface_cmd->surface_id = surface_id; surface_cmd->type = type; surface_cmd->flags = 0; return surface_cmd; } void PushSurfaceCmd(PDev *pdev, QXLSurfaceCmd *surface_cmd) { QXLCommand *cmd; WaitForCmdRing(pdev); cmd = SPICE_RING_PROD_ITEM(pdev->cmd_ring); cmd->type = QXL_CMD_SURFACE; cmd->data = PA(pdev, surface_cmd, pdev->main_mem_slot); PUSH_CMD(pdev); } _inline void GetSurfaceMemory(PDev *pdev, UINT32 x, UINT32 y, UINT32 depth, UINT32 *stride, UINT8 **base_mem, QXLPHYSICAL *phys_mem, UINT8 allocation_type) { DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); switch (allocation_type) { case DEVICE_BITMAP_ALLOCATION_TYPE_SURF0: ASSERT(pdev, x * y * depth /8 <= pdev->primary_memory_size); *base_mem = pdev->primary_memory_start; *phys_mem = PA(pdev, *base_mem, pdev->main_mem_slot); *stride = x * depth / 8; break; case DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM: *base_mem = AllocMem(pdev, MSPACE_TYPE_DEVRAM, x * y * depth / 8); *phys_mem = PA(pdev, *base_mem, pdev->main_mem_slot); *stride = x * depth / 8; break; case DEVICE_BITMAP_ALLOCATION_TYPE_VRAM: { *base_mem = __AllocMem(pdev, MSPACE_TYPE_VRAM, x * y * depth / 8, FALSE, SURFACE_ALLOC_RELEASE_BUNCH_SIZE); *phys_mem = PA(pdev, (PVOID)((UINT64)*base_mem), pdev->vram_mem_slot); *stride = x * depth / 8; break; } default: PANIC(pdev, "No allocation type"); } } void QXLGetSurface(PDev *pdev, QXLPHYSICAL *surface_phys, UINT32 x, UINT32 y, UINT32 depth, UINT32 *stride, UINT8 **base_mem, UINT8 allocation_type) { GetSurfaceMemory(pdev, x, y, depth, stride, base_mem, surface_phys, allocation_type); } void QXLDelSurface(PDev *pdev, UINT8 *base_mem, UINT8 allocation_type) { if (allocation_type == DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM) { FreeMem(pdev, MSPACE_TYPE_DEVRAM, base_mem); } else if (allocation_type == DEVICE_BITMAP_ALLOCATION_TYPE_VRAM) { // this wasn't there in the original code FreeMem(pdev, MSPACE_TYPE_VRAM, base_mem); } } typedef struct InternalDelSurface { UINT32 surface_id; UINT8 allocation_type; } InternalDelSurface; static void FreeDelSurface(PDev *pdev, Resource *res) { InternalDelSurface *internal; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); internal = (InternalDelSurface *)res->res; switch (internal->allocation_type) { case DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM: FreeMem(pdev, MSPACE_TYPE_DEVRAM, pdev->surfaces_info[internal->surface_id].draw_area.base_mem); break; case DEVICE_BITMAP_ALLOCATION_TYPE_VRAM: FreeMem(pdev, MSPACE_TYPE_VRAM, pdev->surfaces_info[internal->surface_id].draw_area.base_mem); break; default: PANIC(pdev, "bad allocation type"); } FreeSurface(pdev, internal->surface_id); FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); } #define SURFACEDEL_ALLOC_BASE (sizeof(Resource) + sizeof(InternalDelSurface)) void QXLGetDelSurface(PDev *pdev, QXLSurfaceCmd *surface, UINT32 surface_id, UINT8 allocation_type) { Resource *surface_res; InternalDelSurface *internal; size_t alloc_size; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); alloc_size = SURFACEDEL_ALLOC_BASE; surface_res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, alloc_size); surface_res->refs = 1; surface_res->free = FreeDelSurface; internal = (InternalDelSurface *)surface_res->res; internal->surface_id = surface_id; internal->allocation_type = allocation_type; SurfaceAddRes(pdev, surface, surface_res); RELEASE_RES(pdev, surface_res); } static void FreePath(PDev *pdev, Resource *res) { QXLPHYSICAL chunk_phys; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); chunk_phys = ((QXLPath *)res->res)->chunk.next_chunk; while (chunk_phys) { QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys, pdev->main_mem_slot); chunk_phys = chunk->next_chunk; FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); ONDBG(pdev->Res.num_path_pages--); } FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); ONDBG(pdev->Res.num_path_pages--); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); } #define NEW_DATA_CHUNK(page_counter, size) { \ void *ptr = AllocMem(pdev, MSPACE_TYPE_DEVRAM, size + sizeof(QXLDataChunk)); \ ONDBG((*(page_counter))++); \ chunk->next_chunk = PA(pdev, ptr, pdev->main_mem_slot); \ ((QXLDataChunk *)ptr)->prev_chunk = PA(pdev, chunk, pdev->main_mem_slot); \ chunk = (QXLDataChunk *)ptr; \ chunk->data_size = 0; \ chunk->next_chunk = 0; \ now = chunk->data; \ end = now + size; \ } #ifdef DBG #define GetPathCommon __GetPathCommon #else #define GetPathCommon(pdev, path, chunk_ptr, now_ptr, end_ptr, data_size, page_counter)\ __GetPathCommon(pdev, path, chunk_ptr, now_ptr, end_ptr, data_size, NULL) #endif #define PATH_PREALLOC_PONTS 20 #define PATH_MAX_ALLOC_PONTS 128 #define PATH_ALLOC_SIZE (sizeof(Resource) + sizeof(QXLPath) + sizeof(QXLPathSeg) +\ sizeof(POINTFIX) * PATH_PREALLOC_PONTS) static void __GetPathCommon(PDev *pdev, PATHOBJ *path, QXLDataChunk **chunk_ptr, UINT8 **now_ptr, UINT8 **end_ptr, UINT32 *data_size, int *page_counter) { QXLDataChunk *chunk = *chunk_ptr; UINT8 *now = *now_ptr; UINT8 *end = *end_ptr; PATHDATA data; int more; DEBUG_PRINT((pdev, 15, "%s\n", __FUNCTION__)); PATHOBJ_vEnumStart(path); do { int pt_buf_size; UINT8 *pt_buf; QXLPathSeg *seg; more = PATHOBJ_bEnum(path, &data); if (data.count == 0) { break; } if (end - now < sizeof(QXLPathSeg)) { size_t alloc_size = MIN(data.count << 3, sizeof(POINTFIX) * PATH_MAX_ALLOC_PONTS); alloc_size += sizeof(QXLPathSeg); NEW_DATA_CHUNK(page_counter, alloc_size); } seg = (QXLPathSeg*)now; seg->flags = data.flags; seg->count = data.count; now = (UINT8 *)seg->points; chunk->data_size += sizeof(*seg); *data_size += sizeof(*seg); pt_buf_size = data.count << 3; pt_buf = (UINT8 *)data.pptfx; do { int cp_size; if (end == now ) { size_t alloc_size = MIN(pt_buf_size, sizeof(POINTFIX) * PATH_MAX_ALLOC_PONTS); NEW_DATA_CHUNK(page_counter, alloc_size); } cp_size = MIN(end - now, pt_buf_size); memcpy(now, pt_buf, cp_size); chunk->data_size += cp_size; *data_size += cp_size; now += cp_size; pt_buf += cp_size; pt_buf_size -= cp_size; } while (pt_buf_size); } while (more); *chunk_ptr = chunk; *now_ptr = now; *end_ptr = end; DEBUG_PRINT((pdev, 17, "%s: done\n", __FUNCTION__)); } static Resource *__GetPath(PDev *pdev, PATHOBJ *path) { Resource *res; QXLPath *qxl_path; QXLDataChunk *chunk; PATHDATA data; UINT8 *now; UINT8 *end; int more; ASSERT(pdev, QXL_PATH_BEGIN == PD_BEGINSUBPATH && QXL_PATH_END == PD_ENDSUBPATH && QXL_PATH_CLOSE == PD_CLOSEFIGURE && QXL_PATH_BEZIER == PD_BEZIERS); DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, PATH_ALLOC_SIZE); ONDBG(pdev->Res.num_path_pages++); res->refs = 1; res->free = FreePath; qxl_path = (QXLPath *)res->res; qxl_path->data_size = 0; chunk = &qxl_path->chunk; chunk->data_size = 0; chunk->prev_chunk = 0; chunk->next_chunk = 0; now = chunk->data; end = (UINT8 *)res + PATH_ALLOC_SIZE; GetPathCommon(pdev, path, &chunk, &now, &end, &qxl_path->data_size, &pdev->Res.num_path_pages); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); return res; } BOOL QXLGetPath(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *path_phys, PATHOBJ *path) { Resource *path_res; ASSERT(pdev, pdev && drawable && path_phys && path); DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); path_res = __GetPath(pdev, path); *path_phys = PA(pdev, path_res->res, pdev->main_mem_slot); DrawableAddRes(pdev, drawable, path_res); RELEASE_RES(pdev, path_res); return TRUE; } static void FreeClipRects(PDev *pdev, Resource *res) { QXLPHYSICAL chunk_phys; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); chunk_phys = ((QXLClipRects *)res->res)->chunk.next_chunk; while (chunk_phys) { QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys, pdev->main_mem_slot); chunk_phys = chunk->next_chunk; FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); ONDBG(pdev->Res.num_rects_pages--); } FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); ONDBG(pdev->Res.num_rects_pages--); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); } #define RECTS_NUM_PREALLOC 8 #define RECTS_ALLOC_SIZE (sizeof(Resource) + sizeof(QXLClipRects) + \ sizeof(QXLRect) * RECTS_NUM_PREALLOC) #define RECTS_NUM_ALLOC 20 #define RECTS_CHUNK_ALLOC_SIZE (sizeof(QXLDataChunk) + sizeof(QXLRect) * RECTS_NUM_ALLOC) static Resource *GetClipRects(PDev *pdev, CLIPOBJ *clip) { Resource *res; QXLClipRects *rects; QXLDataChunk *chunk; QXLRect *dest; QXLRect *dest_end; int more; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, RECTS_ALLOC_SIZE); ONDBG(pdev->Res.num_rects_pages++); res->refs = 1; res->free = FreeClipRects; rects = (QXLClipRects *)res->res; rects->num_rects = 0; chunk = &rects->chunk; chunk->data_size = 0; chunk->prev_chunk = 0; chunk->next_chunk = 0; dest = (QXLRect *)chunk->data; dest_end = dest + ((RECTS_ALLOC_SIZE - sizeof(Resource) - sizeof(QXLClipRects)) >> 4); CLIPOBJ_cEnumStart(clip, TRUE, CT_RECTANGLES, CD_RIGHTDOWN, 0); do { RECTL *now; RECTL *end; struct { ULONG count; RECTL rects[20]; } buf; more = CLIPOBJ_bEnum(clip, sizeof(buf), (ULONG *)&buf); rects->num_rects += buf.count; for (now = buf.rects, end = now + buf.count; now < end; now++, dest++) { if (dest == dest_end) { void *page = AllocMem(pdev, MSPACE_TYPE_DEVRAM, RECTS_CHUNK_ALLOC_SIZE); ONDBG(pdev->Res.num_rects_pages++); chunk->next_chunk = PA(pdev, page, pdev->main_mem_slot); ((QXLDataChunk *)page)->prev_chunk = PA(pdev, chunk, pdev->main_mem_slot); chunk = (QXLDataChunk *)page; chunk->data_size = 0; chunk->next_chunk = 0; dest = (QXLRect *)chunk->data; dest_end = dest + RECTS_NUM_ALLOC; } CopyRect(dest, now); chunk->data_size += sizeof(QXLRect); } } while (more); DEBUG_PRINT((pdev, 13, "%s: done, num_rects %d\n", __FUNCTION__, rects->num_rects)); return res; } static BOOL SetClip(PDev *pdev, CLIPOBJ *clip, QXLDrawable *drawable) { Resource *rects_res; DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); if (clip == NULL) { drawable->clip.type = SPICE_CLIP_TYPE_NONE; DEBUG_PRINT((pdev, 10, "%s: QXL_CLIP_TYPE_NONE\n", __FUNCTION__)); return TRUE; } if (clip->iDComplexity == DC_RECT) { QXLClipRects *rects; rects_res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(Resource) + sizeof(QXLClipRects) + sizeof(QXLRect)); rects_res->refs = 1; rects_res->free = FreeClipRects; rects = (QXLClipRects *)rects_res->res; rects->num_rects = 1; rects->chunk.data_size = sizeof(QXLRect); rects->chunk.prev_chunk = 0; rects->chunk.next_chunk = 0; CopyRect((QXLRect *)rects->chunk.data, &clip->rclBounds); } else { rects_res = GetClipRects(pdev, clip); } DrawableAddRes(pdev, drawable, rects_res); RELEASE_RES(pdev, rects_res); drawable->clip.type = SPICE_CLIP_TYPE_RECTS; drawable->clip.data = PA(pdev, rects_res->res, pdev->main_mem_slot); DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); return TRUE; } static _inline void fast_memcpy_aligment(void *dest, const void *src, size_t len) { _asm { mov ecx, len mov esi, src mov edi, dest cmp ecx, 128 jb try_to_copy64 prefetchnta [esi] copy_128: prefetchnta [esi + 64] movdqa xmm0, [esi] movdqa xmm1, [esi + 16] movdqa xmm2, [esi + 32] movdqa xmm3, [esi + 48] prefetchnta [esi + 128] movntdq [edi], xmm0 movntdq [edi + 16], xmm1 movntdq [edi + 32], xmm2 movntdq [edi + 48], xmm3 movdqa xmm0, [esi + 64] movdqa xmm1, [esi + 80] movdqa xmm2, [esi + 96] movdqa xmm3, [esi + 112] movntdq [edi + 64], xmm0 movntdq [edi + 80], xmm1 movntdq [edi + 96], xmm2 movntdq [edi + 112], xmm3 add edi, 128 add esi, 128 sub ecx, 128 cmp ecx, 128 jae copy_128 try_to_copy64: cmp ecx, 64 jb try_to_copy32 movdqa xmm0, [esi] movdqa xmm1, [esi + 16] movdqa xmm2, [esi + 32] movdqa xmm3, [esi + 48] movntdq [edi], xmm0 movntdq [edi + 16], xmm1 movntdq [edi + 32], xmm2 movntdq [edi + 48], xmm3 add edi, 64 add esi, 64 sub ecx, 64 prefetchnta [esi] try_to_copy32: cmp ecx, 32 jb try_to_copy16 movdqa xmm0, [esi] movdqa xmm1, [esi + 16] movntdq [edi], xmm0 movntdq [edi + 16], xmm1 add edi, 32 add esi, 32 sub ecx, 32 try_to_copy16: cmp ecx, 16 jb try_to_copy4 movdqa xmm0, [esi] movntdq [edi], xmm0 add edi, 16 add esi, 16 sub ecx, 16 try_to_copy4: cmp ecx, 4 jb try_to_copy_1 movsd sub ecx, 4 jmp try_to_copy4 try_to_copy_1: rep movsb sfence } } static _inline void fast_memcpy_unaligment(void *dest, const void *src, size_t len) { _asm { mov ecx, len mov esi, src mov edi, dest cmp ecx, 128 jb try_to_copy64 prefetchnta [esi] copy_128: prefetchnta [esi + 64] movdqu xmm0, [esi] movdqu xmm1, [esi + 16] movdqu xmm2, [esi + 32] movdqu xmm3, [esi + 48] prefetchnta [esi + 128] movntdq [edi], xmm0 movntdq [edi + 16], xmm1 movntdq [edi + 32], xmm2 movntdq [edi + 48], xmm3 movdqu xmm0, [esi + 64] movdqu xmm1, [esi + 80] movdqu xmm2, [esi + 96] movdqu xmm3, [esi + 112] movntdq [edi + 64], xmm0 movntdq [edi + 80], xmm1 movntdq [edi + 96], xmm2 movntdq [edi + 112], xmm3 add edi, 128 add esi, 128 sub ecx, 128 cmp ecx, 128 jae copy_128 try_to_copy64: cmp ecx, 64 jb try_to_copy32 movdqu xmm0, [esi] movdqu xmm1, [esi + 16] movdqu xmm2, [esi + 32] movdqu xmm3, [esi + 48] movntdq [edi], xmm0 movntdq [edi + 16], xmm1 movntdq [edi + 32], xmm2 movntdq [edi + 48], xmm3 add edi, 64 add esi, 64 sub ecx, 64 prefetchnta [esi] try_to_copy32: cmp ecx, 32 jb try_to_copy16 movdqu xmm0, [esi] movdqu xmm1, [esi + 16] movntdq [edi], xmm0 movntdq [edi + 16], xmm1 add edi, 32 add esi, 32 sub ecx, 32 try_to_copy16: cmp ecx, 16 jb try_to_copy4 movdqu xmm0, [esi] movntdq [edi], xmm0 add edi, 16 add esi, 16 sub ecx, 16 try_to_copy4: cmp ecx, 4 jb try_to_copy_1 movsd sub ecx, 4 jmp try_to_copy4 try_to_copy_1: rep movsb sfence } } #ifdef DBG #define PutBytesAlign __PutBytesAlign #define PutBytes(pdev, chunk, now, end, src, size, page_counter, alloc_size, use_sse)\ __PutBytesAlign(pdev, chunk, now, end, src, size, page_counter, alloc_size, 1, use_sse) #else #define PutBytesAlign(pdev, chunk, now, end, src, size, page_counter, alloc_size, alignment, use_sse)\ __PutBytesAlign(pdev, chunk, now, end, src, size, NULL, alloc_size, alignment, use_sse) #define PutBytes(pdev, chunk, now, end, src, size, page_counter, alloc_size, use_sse)\ __PutBytesAlign(pdev, chunk, now, end, src, size, NULL, alloc_size, 1, use_sse) #endif #define BITS_BUF_MAX (64 * 1024) static void __PutBytesAlign(PDev *pdev, QXLDataChunk **chunk_ptr, UINT8 **now_ptr, UINT8 **end_ptr, UINT8 *src, int size, int *page_counter, size_t alloc_size, uint32_t alignment, BOOL use_sse) { QXLDataChunk *chunk = *chunk_ptr; UINT8 *now = *now_ptr; UINT8 *end = *end_ptr; int offset; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); while (size) { int cp_size = MIN(end - now, size); if (!cp_size) { size_t aligned_size; ASSERT(pdev, alloc_size > 0); ASSERT(pdev, BITS_BUF_MAX > alignment); aligned_size = MIN(alloc_size + alignment - 1, BITS_BUF_MAX); aligned_size -= aligned_size % alignment; NEW_DATA_CHUNK(page_counter, aligned_size); cp_size = MIN(end - now, size); } if (use_sse) { offset = (size_t)now & SSE_MASK; if (offset) { offset = SSE_ALIGN - offset; if (offset >= cp_size) { RtlCopyMemory(now, src, cp_size); src += cp_size; now += cp_size; chunk->data_size += cp_size; size -= cp_size; continue; } RtlCopyMemory(now, src, offset); now += offset; src += offset; size -= offset; cp_size -= offset; chunk->data_size += offset; } if (((size_t)src & SSE_MASK) == 0) { fast_memcpy_aligment(now, src, cp_size); } else { fast_memcpy_unaligment(now, src, cp_size); } } else { RtlCopyMemory(now, src, cp_size); } src += cp_size; now += cp_size; chunk->data_size += cp_size; size -= cp_size; } *chunk_ptr = chunk; *now_ptr = now; *end_ptr = end; DEBUG_PRINT((pdev, 14, "%s: done\n", __FUNCTION__)); } typedef struct InternalImage { CacheImage *cache; QXLImage image; } InternalImage; #define HSURF_HASH_VAL(h) (((unsigned long)h >> 4) ^ ((unsigned long)(h) >> 8) ^ \ ((unsigned long)(h) >> 16) ^ ((unsigned long)(h) >> 24)) #define IMAGE_KEY_HASH_VAL(hsurf) (HSURF_HASH_VAL(hsurf) & IMAGE_KEY_HASH_MASK) void ImageKeyPut(PDev *pdev, HSURF hsurf, UINT64 unique, UINT32 key) { ImageKey *image_key = &pdev->Res.image_key_lookup[IMAGE_KEY_HASH_VAL(hsurf)]; if (!unique) { return; } image_key->hsurf = hsurf; image_key->unique = unique; image_key->key = key; } BOOL ImageKeyGet(PDev *pdev, HSURF hsurf, UINT64 unique, UINT32 *key) { ImageKey *image_key; if (!unique) { return FALSE; } image_key = &pdev->Res.image_key_lookup[IMAGE_KEY_HASH_VAL(hsurf)]; if (image_key->hsurf == hsurf && image_key->unique == unique) { *key = image_key->key; return TRUE; } return FALSE; } #define IMAGE_HASH_VAL(hsurf) (HSURF_HASH_VAL(hsurf) & IMAGE_HASH_MASK) static CacheImage *ImageCacheGetByKey(PDev *pdev, UINT32 key, BOOL check_rest, UINT8 format, UINT32 width, UINT32 height) { CacheImage *cache_image = pdev->Res.image_cache[IMAGE_HASH_VAL(key)]; while (cache_image) { if (cache_image->key == key && (!check_rest || (cache_image->format == format && cache_image->width == width && cache_image->height == height))) { return cache_image; } cache_image = cache_image->next; } return NULL; } static void ImageCacheAdd(PDev *pdev, CacheImage *cache_image) { int key = IMAGE_HASH_VAL(cache_image->key); cache_image->next = pdev->Res.image_cache[key]; cache_image->hits = 1; pdev->Res.image_cache[key] = cache_image; } static void ImageCacheRemove(PDev *pdev, CacheImage *cache_image) { CacheImage **cache_img; if (!cache_image->hits) { return; } cache_img = &pdev->Res.image_cache[IMAGE_HASH_VAL(cache_image->key)]; while (*cache_img) { if ((*cache_img)->key == cache_image->key) { *cache_img = cache_image->next; return; } cache_img = &(*cache_img)->next; } } static CacheImage *AllocCacheImage(PDev* pdev) { RingItem *item; while (!(item = RingGetTail(pdev, &pdev->Res.dynamic->cache_image_lru))) { int notify; if (pdev->Res.free_outputs) { pdev->Res.free_outputs = ReleaseOutput(pdev, pdev->Res.free_outputs); continue; } WaitForReleaseRing(pdev); pdev->Res.free_outputs = *SPICE_RING_CONS_ITEM(pdev->release_ring); SPICE_RING_POP(pdev->release_ring, notify); } RingRemove(pdev, item); return CONTAINEROF(item, CacheImage, lru_link); } #define IMAGE_HASH_INIT_VAL(width, height, format) \ ((UINT32)((width) & 0x1FFF) | ((UINT32)((height) & 0x1FFF) << 13) |\ ((UINT32)(format) << 26)) static _inline void SetImageId(InternalImage *internal, BOOL cache_me, LONG width, LONG height, UINT8 format, UINT32 key) { UINT32 image_info = IMAGE_HASH_INIT_VAL(width, height, format); if (cache_me) { QXL_SET_IMAGE_ID(&internal->image, ((UINT32)QXL_IMAGE_GROUP_DRIVER << 30) | image_info, key); internal->image.descriptor.flags = QXL_IMAGE_CACHE; } else { QXL_SET_IMAGE_ID(&internal->image, ((UINT32)QXL_IMAGE_GROUP_DRIVER_DONT_CACHE << 30) | image_info, key); internal->image.descriptor.flags = 0; } } typedef struct InternalPalette { UINT32 refs; struct InternalPalette *next; RingItem lru_link; QXLPalette palette; } InternalPalette; #define PALETTE_HASH_VAL(unique) ((int)(unique) & PALETTE_HASH_NASKE) static _inline void ReleasePalette(PDev *pdev, InternalPalette *palette) { ASSERT(pdev, palette); DEBUG_PRINT((pdev, 15, "%s\n", __FUNCTION__)); if (--palette->refs == 0) { FreeMem(pdev, MSPACE_TYPE_DEVRAM, palette); } } static _inline void PaletteCacheRemove(PDev *pdev, InternalPalette *palette) { InternalPalette **internal; DEBUG_PRINT((pdev, 15, "%s\n", __FUNCTION__)); ASSERT(pdev, palette->palette.unique); internal = &pdev->Res.palette_cache[PALETTE_HASH_VAL(palette->palette.unique)]; while (*internal) { if ((*internal)->palette.unique == palette->palette.unique) { *internal = palette->next; RingRemove(pdev, &palette->lru_link); ReleasePalette(pdev, palette); pdev->Res.num_palettes--; DEBUG_PRINT((pdev, 16, "%s: done\n", __FUNCTION__)); return; } internal = &(*internal)->next; } ASSERT(pdev, FALSE); } static _inline InternalPalette *PaletteCacheGet(PDev *pdev, UINT32 unique) { InternalPalette *now; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); if (!unique) { return NULL; } now = pdev->Res.palette_cache[PALETTE_HASH_VAL(unique)]; while (now) { if (now->palette.unique == unique) { RingRemove(pdev, &now->lru_link); RingAdd(pdev, &pdev->Res.dynamic->palette_lru, &now->lru_link); now->refs++; DEBUG_PRINT((pdev, 13, "%s: found\n", __FUNCTION__)); return now; } now = now->next; } DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); return NULL; } static _inline void PaletteCacheAdd(PDev *pdev, InternalPalette *palette) { int key; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); if (!palette->palette.unique) { DEBUG_PRINT((pdev, 13, "%s: not unique\n", __FUNCTION__)); return; } if (pdev->Res.num_palettes == PALETTE_CACHE_SIZE) { ASSERT(pdev, RingGetTail(pdev, &pdev->Res.dynamic->palette_lru)); PaletteCacheRemove(pdev, CONTAINEROF(RingGetTail(pdev, &pdev->Res.dynamic->palette_lru), InternalPalette, lru_link)); } key = PALETTE_HASH_VAL(palette->palette.unique); palette->next = pdev->Res.palette_cache[key]; pdev->Res.palette_cache[key] = palette; RingAdd(pdev, &pdev->Res.dynamic->palette_lru, &palette->lru_link); palette->refs++; pdev->Res.num_palettes++; DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); } static _inline void GetPallette(PDev *pdev, QXLBitmap *bitmap, XLATEOBJ *color_trans) { InternalPalette *internal; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); if (!color_trans || !(color_trans->flXlate & XO_TABLE)) { bitmap->palette = 0; return; } if ((internal = PaletteCacheGet(pdev, color_trans->iUniq))) { DEBUG_PRINT((pdev, 12, "%s: from cache\n", __FUNCTION__)); bitmap->palette = PA(pdev, &internal->palette, pdev->main_mem_slot); return; } internal = (InternalPalette *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(InternalPalette) + (color_trans->cEntries << 2)); internal->refs = 1; RingItemInit(&internal->lru_link); bitmap->palette = PA(pdev, &internal->palette, pdev->main_mem_slot); internal->palette.unique = color_trans->iUniq; internal->palette.num_ents = (UINT16)color_trans->cEntries; RtlCopyMemory(internal->palette.ents, color_trans->pulXlate, color_trans->cEntries << 2); PaletteCacheAdd(pdev, internal); DEBUG_PRINT((pdev, 12, "%s: done\n", __FUNCTION__)); } static void FreeQuicImage(PDev *pdev, Resource *res) // todo: defer { InternalImage *internal; QXLPHYSICAL chunk_phys; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); internal = (InternalImage *)res->res; if (internal->cache) { RingAdd(pdev, &pdev->Res.dynamic->cache_image_lru, &internal->cache->lru_link); internal->cache->image = NULL; } chunk_phys = ((QXLDataChunk *)internal->image.quic.data)->next_chunk; while (chunk_phys) { QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys, pdev->main_mem_slot); chunk_phys = chunk->next_chunk; FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); ONDBG(pdev->Res.num_bits_pages--); } FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); ONDBG(pdev->Res.num_bits_pages--); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); } static _inline QuicImageType GetQuicImageType(UINT8 format) { switch (format) { case SPICE_BITMAP_FMT_32BIT: return QUIC_IMAGE_TYPE_RGB32; case SPICE_BITMAP_FMT_16BIT: return QUIC_IMAGE_TYPE_RGB16; case SPICE_BITMAP_FMT_RGBA: return QUIC_IMAGE_TYPE_RGBA; case SPICE_BITMAP_FMT_24BIT: return QUIC_IMAGE_TYPE_RGB24; default: return QUIC_IMAGE_TYPE_INVALID; }; } #define QUIC_ALLOC_BASE (sizeof(Resource) + sizeof(InternalImage) + sizeof(QXLDataChunk)) #define QUIC_BUF_MAX (64 * 1024) #define QUIC_BUF_MIN 1024 struct QuicData { QuicUsrContext user; PDev *pdev; QuicContext *quic; QXLDataChunk *chunk; int chunk_io_words; int prev_chunks_io_words; int rows; int raw_row_size; }; static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed) { QuicData *usr_data = (QuicData *)usr; PDev *pdev = usr_data->pdev; QXLDataChunk *new_chank; int alloc_size; int more; ASSERT(pdev, usr_data->rows >= rows_completed); more = (rows_completed - usr_data->rows) * usr_data->raw_row_size; alloc_size = MIN(MAX(more >> 4, QUIC_BUF_MIN), QUIC_BUF_MAX); new_chank = AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(QXLDataChunk) + alloc_size); new_chank->data_size = 0; new_chank->prev_chunk = PA(pdev, usr_data->chunk, pdev->main_mem_slot); new_chank->next_chunk = 0; usr_data->prev_chunks_io_words += usr_data->chunk_io_words; usr_data->chunk->data_size = usr_data->chunk_io_words << 2; usr_data->chunk->next_chunk = PA(pdev, new_chank, pdev->main_mem_slot); usr_data->chunk = new_chank; usr_data->chunk_io_words = alloc_size >> 2; ONDBG(pdev->Res.num_bits_pages++); *io_ptr = (UINT32 *)new_chank->data; return usr_data->chunk_io_words; } static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines) { return 0; } static _inline Resource *GetQuicImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans, BOOL cache_me, LONG width, LONG height, UINT8 format, UINT8 *src, UINT32 line_size, UINT32 key) { Resource *image_res; InternalImage *internal; QuicImageType type; size_t alloc_size; int data_size; QuicData *quic_data; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); ASSERT(pdev, pdev->quic_data); if (!*pdev->compression_level) { return NULL; } if ((type = GetQuicImageType(format)) == QUIC_IMAGE_TYPE_INVALID) { DEBUG_PRINT((pdev, 13, "%s: unsupported\n", __FUNCTION__)); return NULL; } quic_data = pdev->quic_data; alloc_size = MIN(QUIC_ALLOC_BASE + (height * line_size >> 4), QUIC_ALLOC_BASE + QUIC_BUF_MAX); alloc_size = MAX(alloc_size, QUIC_ALLOC_BASE + QUIC_BUF_MIN); image_res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, alloc_size); ONDBG(pdev->Res.num_bits_pages++); image_res->refs = 1; image_res->free = FreeQuicImage; internal = (InternalImage *)image_res->res; SetImageId(internal, cache_me, width, height, format, key); internal->image.descriptor.type = SPICE_IMAGE_TYPE_QUIC; internal->image.descriptor.width = width; internal->image.descriptor.height = height; quic_data->chunk = (QXLDataChunk *)internal->image.quic.data; quic_data->chunk->data_size = 0; quic_data->chunk->prev_chunk = 0; quic_data->chunk->next_chunk = 0; quic_data->prev_chunks_io_words = 0; quic_data->chunk_io_words = ((UINT8 *)image_res + alloc_size - quic_data->chunk->data) >> 2; quic_data->rows = height; quic_data->raw_row_size = line_size; ASSERT(pdev, quic_data->chunk_io_words > 0); data_size = quic_encode(quic_data->quic, type, width, height, src, height, surf->lDelta, (UINT32 *)quic_data->chunk->data, quic_data->chunk_io_words); if (data_size == QUIC_ERROR) { FreeQuicImage(pdev, image_res); DEBUG_PRINT((pdev, 13, "%s: error\n", __FUNCTION__)); return NULL; } quic_data->chunk->data_size = (data_size - quic_data->prev_chunks_io_words) << 2; internal->image.quic.data_size = data_size << 2; DEBUG_PRINT((pdev, 13, "%s: done. row size %u quic size %u \n", __FUNCTION__, line_size * height, data_size << 2)); return image_res; } static void FreeBitmapImage(PDev *pdev, Resource *res) // todo: defer { InternalImage *internal; QXLPHYSICAL chunk_phys; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); internal = (InternalImage *)res->res; if (internal->cache) { RingAdd(pdev, &pdev->Res.dynamic->cache_image_lru, &internal->cache->lru_link); internal->cache->image = NULL; } if (internal->image.bitmap.palette) { QXLPalette *palette = (QXLPalette *)VA(pdev, internal->image.bitmap.palette, pdev->main_mem_slot); ReleasePalette(pdev, CONTAINEROF(palette, InternalPalette, palette)); } chunk_phys = ((QXLDataChunk *)(&internal->image.bitmap + 1))->next_chunk; while (chunk_phys) { QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys, pdev->main_mem_slot); chunk_phys = chunk->next_chunk; FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); ONDBG(pdev->Res.num_bits_pages--); } FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); ONDBG(pdev->Res.num_bits_pages--); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); } static _inline void RestoreFPU(PDev *pdev) { void *align_addr = (void *)ALIGN((size_t)(&pdev->FPUSave), SSE_ALIGN); _asm { mov esi, align_addr movdqa xmm0, [esi] movdqa xmm1, [esi + 16] movdqa xmm2, [esi + 32] movdqa xmm3, [esi + 48] } } static _inline void SaveFPU(PDev *pdev) { void *align_addr = (void *)ALIGN((size_t)(&pdev->FPUSave), SSE_ALIGN); _asm { mov edi, align_addr movdqa [edi], xmm0 movdqa [edi + 16], xmm1 movdqa [edi + 32], xmm2 movdqa [edi + 48], xmm3 } } static void FreeSurfaceImage(PDev *pdev, Resource *res) { DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); } #define BITMAP_ALLOC_BASE (sizeof(Resource) + sizeof(InternalImage) + sizeof(QXLDataChunk)) static _inline Resource *GetBitmapImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans, BOOL cache_me, LONG width, LONG height, UINT8 format, UINT8 *src, UINT32 line_size, UINT32 key) { Resource *image_res; InternalImage *internal; size_t alloc_size; QXLDataChunk *chunk; UINT8 *src_end; UINT8 *dest; UINT8 *dest_end; BOOL use_sse = FALSE; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); ASSERT(pdev, width > 0 && height > 0); ASSERT(pdev, BITS_BUF_MAX > line_size); alloc_size = BITMAP_ALLOC_BASE + BITS_BUF_MAX - BITS_BUF_MAX % line_size; alloc_size = MIN(BITMAP_ALLOC_BASE + height * line_size, alloc_size); image_res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, alloc_size); ONDBG(pdev->Res.num_bits_pages++); image_res->refs = 1; image_res->free = FreeBitmapImage; internal = (InternalImage *)image_res->res; SetImageId(internal, cache_me, width, height, format, key); internal->image.descriptor.type = SPICE_IMAGE_TYPE_BITMAP; chunk = (QXLDataChunk *)(&internal->image.bitmap + 1); chunk->data_size = 0; chunk->prev_chunk = 0; chunk->next_chunk = 0; internal->image.bitmap.data = PA(pdev, chunk, pdev->main_mem_slot); internal->image.bitmap.flags = 0; internal->image.descriptor.width = internal->image.bitmap.x = width; internal->image.descriptor.height = internal->image.bitmap.y = height; internal->image.bitmap.format = format; internal->image.bitmap.stride = line_size; src_end = src - surf->lDelta; src += surf->lDelta * (height - 1); dest = chunk->data; dest_end = (UINT8 *)image_res + alloc_size; alloc_size = height * line_size; if (have_sse2 && alloc_size >= 1024) { use_sse = TRUE; SaveFPU(pdev); } for (; src != src_end; src -= surf->lDelta, alloc_size -= line_size) { PutBytesAlign(pdev, &chunk, &dest, &dest_end, src, line_size, &pdev->Res.num_bits_pages, alloc_size, line_size, TRUE); } if (use_sse) { RestoreFPU(pdev); } GetPallette(pdev, &internal->image.bitmap, color_trans); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); return image_res; } #define ADAPTIVE_HASH static _inline UINT32 GetHash(UINT8 *src, INT32 width, INT32 height, UINT8 format, int high_bits_set, UINT32 line_size, LONG stride, XLATEOBJ *color_trans) { UINT32 hash_value = IMAGE_HASH_INIT_VAL(width, height, format); UINT8 *row_buf = src; UINT8 last_byte = 0; UINT8 reminder; UINT32 i; int row; if (color_trans && color_trans->flXlate == XO_TABLE) { hash_value = murmurhash2a(color_trans->pulXlate, sizeof(*color_trans->pulXlate) * color_trans->cEntries, hash_value); } if (format == SPICE_BITMAP_FMT_32BIT && stride == line_size) { hash_value = murmurhash2ajump3((UINT32 *)row_buf, width * height, hash_value); } else { for (row = 0; row < height; row++) { #ifdef ADAPTIVE_HASH if (format == SPICE_BITMAP_FMT_32BIT) { hash_value = murmurhash2ajump3((UINT32 *)row_buf, width, hash_value); } else { if (format == SPICE_BITMAP_FMT_4BIT_BE && (width & 0x1)) { last_byte = row_buf[line_size - 1] & 0xF0; } else if (format == SPICE_BITMAP_FMT_1BIT_BE && (reminder = width & 0x7)) { last_byte = row_buf[line_size - 1] & ~((1 << (8 - reminder)) - 1); } if (last_byte) { hash_value = murmurhash2a(row_buf, line_size - 1, hash_value); hash_value = murmurhash2a(&last_byte, 1, hash_value); } else { hash_value = murmurhash2a(row_buf, line_size, hash_value); } } #else hash_value = murmurhash2a(row_buf, line_size, hash_value); #endif row_buf += stride; } } if (high_bits_set) { hash_value ^= 1; } return hash_value; } static _inline UINT32 GetFormatLineSize(INT32 width, ULONG bitmap_format, UINT8 *format) { switch (bitmap_format) { case BMF_32BPP: *format = SPICE_BITMAP_FMT_32BIT; return width << 2; case BMF_24BPP: *format = SPICE_BITMAP_FMT_24BIT; return width * 3; case BMF_16BPP: *format = SPICE_BITMAP_FMT_16BIT; return width << 1; case BMF_8BPP: *format = SPICE_BITMAP_FMT_8BIT; return width; case BMF_4BPP: *format = SPICE_BITMAP_FMT_4BIT_BE; return ALIGN(width, 2) >> 1; case BMF_1BPP: *format = SPICE_BITMAP_FMT_1BIT_BE; return ALIGN(width, 8) >> 3; default: return 0; } } static BOOL ChachSizeTest(PDev *pdev, SURFOBJ *surf) { BOOL ret = (UINT32)surf->sizlBitmap.cx * surf->sizlBitmap.cy <= pdev->max_bitmap_size; if (!ret) { DEBUG_PRINT((pdev, 1, "%s: cache size test failed x %d y %d max\n", __FUNCTION__, surf->sizlBitmap.cx, surf->sizlBitmap.cy, pdev->max_bitmap_size)); } return ret; } static _inline UINT64 get_unique(SURFOBJ *surf, XLATEOBJ *color_trans) { int pallette; ULONG pallette_unique; pallette = color_trans && (color_trans->flXlate & XO_TABLE); pallette_unique = pallette ? color_trans->iUniq : 0; // NOTE: GDI sometimes gives many instances of the exactly same SURFOBJ (hsurf & iUniq), // but with (fjBitmap & BMF_DONTCACHE). This opposed to what documented in the MSDN. if (!surf->iUniq || (surf->fjBitmap & BMF_DONTCACHE) || (pallette && !pallette_unique)) { return 0; } else { return (surf->iUniq | ((UINT64)pallette_unique << 32)); } } BOOL CheckIfCacheImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans) { CacheImage *cache_image; UINT64 gdi_unique; UINT32 key; UINT8 format; gdi_unique = get_unique(surf, color_trans); if (!ImageKeyGet(pdev, surf->hsurf, gdi_unique, &key)) { return FALSE; } switch (surf->iBitmapFormat) { case BMF_32BPP: format = SPICE_BITMAP_FMT_32BIT; break; case BMF_24BPP: format = SPICE_BITMAP_FMT_24BIT; break; case BMF_16BPP: format = SPICE_BITMAP_FMT_16BIT; break; case BMF_8BPP: format = SPICE_BITMAP_FMT_8BIT; break; case BMF_4BPP: format = SPICE_BITMAP_FMT_4BIT_BE; break; case BMF_1BPP: format = SPICE_BITMAP_FMT_1BIT_BE; } if ((cache_image = ImageCacheGetByKey(pdev, key, TRUE, format, surf->sizlBitmap.cx, surf->sizlBitmap.cy))) { return TRUE; } return FALSE; } static CacheImage *GetChachImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans, int high_bits_set, UINT32 *hash_key) { CacheImage *cache_image; UINT64 gdi_unique; UINT32 key; UINT8 format; UINT32 line_size; gdi_unique = get_unique(surf, color_trans); if (!(line_size = GetFormatLineSize(surf->sizlBitmap.cx, surf->iBitmapFormat, &format))) { DEBUG_PRINT((pdev, 0, "%s: bitmap format err\n", __FUNCTION__)); return FALSE; } if (!ImageKeyGet(pdev, surf->hsurf, gdi_unique, &key)) { key = GetHash(surf->pvScan0, surf->sizlBitmap.cx, surf->sizlBitmap.cy, format, high_bits_set, line_size, surf->lDelta, color_trans); ImageKeyPut(pdev, surf->hsurf, gdi_unique, key); DEBUG_PRINT((pdev, 11, "%s: ImageKeyPut %u\n", __FUNCTION__, key)); } else { DEBUG_PRINT((pdev, 11, "%s: ImageKeyGet %u\n", __FUNCTION__, key)); } if (hash_key) { *hash_key = key; } if ((cache_image = ImageCacheGetByKey(pdev, key, TRUE, format, surf->sizlBitmap.cx, surf->sizlBitmap.cy))) { cache_image->hits++; DEBUG_PRINT((pdev, 11, "%s: ImageCacheGetByKey %u hits %u\n", __FUNCTION__, key, cache_image->hits)); return cache_image; } if (ChachSizeTest(pdev, surf)) { CacheImage *cache_image = AllocCacheImage(pdev); ImageCacheRemove(pdev, cache_image); cache_image->key = key; cache_image->image = NULL; cache_image->format = format; cache_image->width = surf->sizlBitmap.cx; cache_image->height = surf->sizlBitmap.cy; ImageCacheAdd(pdev, cache_image); RingAdd(pdev, &pdev->Res.dynamic->cache_image_lru, &cache_image->lru_link); DEBUG_PRINT((pdev, 11, "%s: ImageCacheAdd %u\n", __FUNCTION__, key)); } return NULL; } static HSEMAPHORE image_id_sem = NULL; static _inline UINT32 get_image_serial() { static UINT32 image_id = 0; // move to dev mem and use InterlockedIncrement UINT32 ret = 0; EngAcquireSemaphore(image_id_sem); ret = ++image_id; EngReleaseSemaphore(image_id_sem); return ret; } static int rgb32_data_has_alpha(int width, int height, int stride, UINT8 *data, int *all_set_out) { UINT32 *line, *end, alpha; int has_alpha; has_alpha = FALSE; while (height-- > 0) { line = (UINT32 *)data; end = line + width; data += stride; while (line != end) { alpha = *line & 0xff000000U; if (alpha != 0) { has_alpha = TRUE; if (alpha != 0xff000000U) { *all_set_out = FALSE; return TRUE; } } line++; } } *all_set_out = has_alpha; return has_alpha; } BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SURFOBJ *surf, QXLRect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache, INT32 *surface_dest) { Resource *image_res; InternalImage *internal; CacheImage *cache_image; UINT32 key; UINT8 format; UINT32 line_size; UINT8 *src; int high_bits_set; INT32 width = area->right - area->left; INT32 height = area->bottom - area->top; ASSERT(pdev, !hash_key || use_cache); DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); if (surf->iType != STYPE_BITMAP) { UINT32 alloc_size; DEBUG_PRINT((pdev, 9, "%s: copy from device\n", __FUNCTION__)); alloc_size = sizeof(Resource) + sizeof(InternalImage); image_res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, alloc_size); ONDBG(pdev->num_bits_pages++); image_res->refs = 1; image_res->free = FreeSurfaceImage; internal = (InternalImage *)image_res->res; SetImageId(internal, FALSE, 0, 0, 0, 0); internal->image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE; internal->image.descriptor.width = 0; internal->image.descriptor.height = 0; *surface_dest = internal->image.surface_image.surface_id = GetSurfaceId(surf); *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); DrawableAddRes(pdev, drawable, image_res); RELEASE_RES(pdev, image_res); return TRUE; } if (area->left < 0 || area->right > surf->sizlBitmap.cx || area->top < 0 || area->bottom > surf->sizlBitmap.cy) { DEBUG_PRINT((pdev, 0, "%s: bad dimensions\n", __FUNCTION__)); return FALSE; } high_bits_set = FALSE; if (surf->iBitmapFormat == BMF_32BPP) { if (rgb32_data_has_alpha(width, height, surf->lDelta, (UINT8 *)surf->pvScan0 + area->left * 4, &high_bits_set) && !high_bits_set) { return QXLGetAlphaBitmap(pdev, drawable, image_phys, surf, area, surface_dest); } } DEBUG_PRINT((pdev, 11, "%s: iUniq=%x DONTCACHE=%x w=%d h=%d cx=%d cy=%d " "hsurf=%x ctiUniq=%x XO_TABLE=%u format=%u\n", __FUNCTION__, surf->iUniq, surf->fjBitmap & BMF_DONTCACHE, width, height, surf->sizlBitmap.cx, surf->sizlBitmap.cy, surf->hsurf, color_trans ? color_trans->iUniq : 0, color_trans ? !!(color_trans->flXlate & XO_TABLE) : 0, surf->iBitmapFormat)); if (use_cache) { cache_image = GetChachImage(pdev, surf, color_trans, high_bits_set, hash_key); if (cache_image && cache_image->image) { DEBUG_PRINT((pdev, 11, "%s: cached image found %u\n", __FUNCTION__, cache_image->key)); internal = cache_image->image; *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); DrawableAddRes(pdev, drawable, image_res); return TRUE; } } else { cache_image = NULL; } if (cache_image) { key = cache_image->key; width = surf->sizlBitmap.cx; height = surf->sizlBitmap.cy; src = surf->pvScan0; } else { int scan0_offset; int dx; key = get_image_serial(); switch (surf->iBitmapFormat) { case BMF_32BPP: dx = 0; scan0_offset = area->left << 2; break; case BMF_24BPP: dx = 0; scan0_offset = area->left * 3; break; case BMF_16BPP: dx = 0; scan0_offset = area->left << 1; break; case BMF_8BPP: dx = 0; scan0_offset = area->left; break; case BMF_4BPP: dx = area->left & 0x01; scan0_offset = (area->left & ~0x01) >> 1; break; case BMF_1BPP: dx = area->left & 0x07; scan0_offset = (area->left & ~0x07) >> 3; break; default: DEBUG_PRINT((pdev, 0, "%s: bitmap format err\n", __FUNCTION__)); return FALSE; } width = width + dx; src = (UINT8 *)surf->pvScan0 + area->top * surf->lDelta + scan0_offset; area->left = dx; area->right = width; area->top = 0; area->bottom = height; } if (!(line_size = GetFormatLineSize(width, surf->iBitmapFormat, &format))) { DEBUG_PRINT((pdev, 0, "%s: bitmap format err\n", __FUNCTION__)); return FALSE; } if (!(image_res = GetQuicImage(pdev, surf, color_trans, !!cache_image, width, height, format, src, line_size, key))) { image_res = GetBitmapImage(pdev, surf, color_trans, !!cache_image, width, height, format, src, line_size, key); } internal = (InternalImage *)image_res->res; if (high_bits_set) { internal->image.descriptor.flags |= QXL_IMAGE_HIGH_BITS_SET; } if ((internal->cache = cache_image)) { DEBUG_PRINT((pdev, 11, "%s: cache_me %u\n", __FUNCTION__, key)); cache_image->image = internal; if (RingItemIsLinked(&cache_image->lru_link)) { RingRemove(pdev, &cache_image->lru_link); } } *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); DrawableAddRes(pdev, drawable, image_res); RELEASE_RES(pdev, image_res); return TRUE; } BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SURFOBJ *surf, QXLRect *area, INT32 *surface_dest) { Resource *image_res; InternalImage *internal; CacheImage *cache_image; UINT64 gdi_unique; UINT32 key; UINT8 *src; INT32 width = area->right - area->left; INT32 height = area->bottom - area->top; DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); ASSERT(pdev, area->left >= 0 && area->right <= surf->sizlBitmap.cx && area->top >= 0 && area->bottom <= surf->sizlBitmap.cy); DEBUG_PRINT((pdev, 11, "%s: iUniq=%x DONTCACHE=%x w=%d h=%d cx=%d cy=%d " "hsurf=%x format=%u\n", __FUNCTION__, surf->iUniq, surf->fjBitmap & BMF_DONTCACHE, width, height, surf->sizlBitmap.cx, surf->sizlBitmap.cy, surf->hsurf, surf->iBitmapFormat)); if (surf->iType != STYPE_BITMAP) { UINT32 alloc_size; DEBUG_PRINT((pdev, 9, "%s: copy from device\n", __FUNCTION__)); alloc_size = sizeof(Resource) + sizeof(InternalImage); image_res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, alloc_size); ONDBG(pdev->num_bits_pages++); image_res->refs = 1; image_res->free = FreeSurfaceImage; internal = (InternalImage *)image_res->res; SetImageId(internal, FALSE, 0, 0, 0, 0); internal->image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE; internal->image.descriptor.width = 0; internal->image.descriptor.height = 0; *surface_dest = internal->image.surface_image.surface_id = GetSurfaceId(surf); *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); DrawableAddRes(pdev, drawable, image_res); RELEASE_RES(pdev, image_res); return TRUE; } ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP && surf->iType == STYPE_BITMAP); //todo: use GetChachImage // NOTE: Same BMF_DONTCACHE issue as in QXLGetBitmap if (!surf->iUniq || (surf->fjBitmap & BMF_DONTCACHE)) { gdi_unique = 0; } else { gdi_unique = surf->iUniq; } if (!ImageKeyGet(pdev, surf->hsurf, gdi_unique, &key)) { key = GetHash(surf->pvScan0, surf->sizlBitmap.cx, surf->sizlBitmap.cy, SPICE_BITMAP_FMT_RGBA, FALSE, surf->sizlBitmap.cx << 2, surf->lDelta, NULL); ImageKeyPut(pdev, surf->hsurf, gdi_unique, key); DEBUG_PRINT((pdev, 11, "%s: ImageKeyPut %u\n", __FUNCTION__, key)); } else { DEBUG_PRINT((pdev, 11, "%s: ImageKeyGet %u\n", __FUNCTION__, key)); } if (cache_image = ImageCacheGetByKey(pdev, key, TRUE, SPICE_BITMAP_FMT_RGBA, surf->sizlBitmap.cx, surf->sizlBitmap.cy)) { DEBUG_PRINT((pdev, 11, "%s: ImageCacheGetByKey %u hits %u\n", __FUNCTION__, key, cache_image->hits)); cache_image->hits++; if (internal = cache_image->image) { DEBUG_PRINT((pdev, 11, "%s: cached image found %u\n", __FUNCTION__, key)); *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); DrawableAddRes(pdev, drawable, image_res); return TRUE; } } else if (ChachSizeTest(pdev, surf)) { CacheImage *cache_image = AllocCacheImage(pdev); ImageCacheRemove(pdev, cache_image); cache_image->key = key; cache_image->image = NULL; cache_image->format = SPICE_BITMAP_FMT_RGBA; cache_image->width = surf->sizlBitmap.cx; cache_image->height = surf->sizlBitmap.cy; ImageCacheAdd(pdev, cache_image); RingAdd(pdev, &pdev->Res.dynamic->cache_image_lru, &cache_image->lru_link); DEBUG_PRINT((pdev, 11, "%s: ImageCacheAdd %u\n", __FUNCTION__, key)); } if (cache_image) { width = surf->sizlBitmap.cx; height = surf->sizlBitmap.cy; src = surf->pvScan0; } else { src = (UINT8 *)surf->pvScan0 + area->top * surf->lDelta + (area->left << 2); area->left = 0; area->right = width; area->top = 0; area->bottom = height; } if (!(image_res = GetQuicImage(pdev, surf, NULL, !!cache_image, width, height, SPICE_BITMAP_FMT_RGBA, src, width << 2, key))) { image_res = GetBitmapImage(pdev, surf, NULL, !!cache_image, width, height, SPICE_BITMAP_FMT_RGBA, src, width << 2, key); } internal = (InternalImage *)image_res->res; if ((internal->cache = cache_image)) { DEBUG_PRINT((pdev, 11, "%s: cache_me %u\n", __FUNCTION__, key)); cache_image->image = internal; if (RingItemIsLinked(&cache_image->lru_link)) { RingRemove(pdev, &cache_image->lru_link); } } *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); DrawableAddRes(pdev, drawable, image_res); RELEASE_RES(pdev, image_res); return TRUE; } BOOL QXLGetBitsFromCache(PDev *pdev, QXLDrawable *drawable, UINT32 hash_key, QXLPHYSICAL *image_phys) { InternalImage *internal; CacheImage *cache_image; Resource *image_res; if ((cache_image = ImageCacheGetByKey(pdev, hash_key, FALSE, 0, 0, 0)) && (internal = cache_image->image)) { cache_image->hits++; *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); DrawableAddRes(pdev, drawable, image_res); return TRUE; } return FALSE; } BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, QXLQMask *qxl_mask, SURFOBJ *mask, POINTL *pos, BOOL invers, LONG width, LONG height, INT32 *surface_dest) { QXLRect area; if (!mask) { qxl_mask->bitmap = 0; return TRUE; } ASSERT(pdev, pos && qxl_mask && drawable); if (mask->iBitmapFormat != BMF_1BPP) { DEBUG_PRINT((pdev, 0, "%s: bitmap format err\n", __FUNCTION__)); return FALSE; } qxl_mask->flags = invers ? SPICE_MASK_FLAGS_INVERS : 0; area.left = pos->x; area.right = area.left + width; area.top = pos->y; area.bottom = area.top + height; if (QXLGetBitmap(pdev, drawable, &qxl_mask->bitmap, mask, &area, NULL, NULL, TRUE, surface_dest)) { qxl_mask->pos.x = area.left; qxl_mask->pos.y = area.top; return TRUE; } return FALSE; } static void FreeBuf(PDev *pdev, Resource *res) { ONDBG(pdev->Res.num_buf_pages--); FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); } UINT8 *QXLGetBuf(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *buf_phys, UINT32 size) { Resource *buf_res; DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); if (size > PAGE_SIZE - sizeof(Resource)) { DEBUG_PRINT((pdev, 0, "%s: size err\n", __FUNCTION__)); return NULL; } buf_res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(Resource) + size); ONDBG(pdev->Res.num_buf_pages++); buf_res->refs = 1; buf_res->free = FreeBuf; *buf_phys = PA(pdev, buf_res->res, pdev->main_mem_slot); DrawableAddRes(pdev, drawable, buf_res); RELEASE_RES(pdev, buf_res); return buf_res->res; } #ifdef UPDATE_CMD void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id) { QXLCommand *cmd; QXLOutput *output; QXLUpdateCmd *updat_cmd; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); output = (QXLOutput *)AllocMem(pdev, sizeof(QXLOutput) + sizeof(QXLUpdateCmd)); output->num_res = 0; updat_cmd = (QXLUpdateCmd *)output->data; updat_cmd->release_info.id = (UINT64)output; ONDBG(pdev->Res.num_outputs++); //todo: atomic CopyRect(&updat_cmd->area, area); updat_cmd->update_id = ++pdev->Res.update_id; updat_cmd->surface_id = surface_id; WaitForCmdRing(pdev); cmd = SPICE_RING_PROD_ITEM(pdev->cmd_ring); cmd->type = QXL_CMD_UPDATE; cmd->data = PA(pdev, updat_cmd, pdev->main_mem_slot); PUSH_CMD(pdev); do { #ifdef DBG { LARGE_INTEGER timeout; // 1 => 100 nanoseconds timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s #if (WINVER < 0x0501) pdev->WaitForEvent(pdev->display_event, &timeout); #else EngWaitForSingleObject(pdev->display_event, &timeout); #endif //(WINVER < 0x0501) if (*pdev->dev_update_id != pdev->Res.update_id) { DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); } } #else #if (WINVER < 0x0501) pdev->WaitForEvent(pdev->display_event, NULL); #else EngWaitForSingleObject(pdev->display_event, NULL); #endif //(WINVER < 0x0501) #endif // DEBUG mb(); } while (*pdev->dev_update_id != pdev->Res.update_id); } #else void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id) { DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); CopyRect(pdev->update_area, area); *pdev->update_surface = surface_id; WRITE_PORT_UCHAR(pdev->update_area_port, 0); } #endif static _inline void add_rast_glyphs(PDev *pdev, QXLString *str, ULONG count, GLYPHPOS *glyps, QXLDataChunk **chunk_ptr, UINT8 **now_ptr, UINT8 **end_ptr, int bpp, POINTL *delta, QXLPoint **str_pos) { GLYPHPOS *glyps_end = glyps + count; QXLDataChunk *chunk = *chunk_ptr; UINT8 *now = *now_ptr; UINT8 *end = *end_ptr; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); for (; glyps < glyps_end; glyps++) { QXLRasterGlyph *glyph; UINT8 *line; UINT8 *end_line; UINT32 stride; if (end - now < sizeof(*glyph)) { NEW_DATA_CHUNK(&pdev->Res.num_glyphs_pages, PAGE_SIZE); } glyph = (QXLRasterGlyph *)now; if (delta) { if (*str_pos) { glyph->render_pos.x = (*str_pos)->x + delta->x; glyph->render_pos.y = (*str_pos)->y + delta->y; } else { glyph->render_pos.x = glyps->ptl.x; glyph->render_pos.y = glyps->ptl.y; } *str_pos = (QXLPoint *)&glyph->render_pos; } else { glyph->render_pos.x = glyps->ptl.x; glyph->render_pos.y = glyps->ptl.y; } glyph->glyph_origin.x = glyps->pgdf->pgb->ptlOrigin.x; glyph->glyph_origin.y = glyps->pgdf->pgb->ptlOrigin.y; glyph->width = (UINT16)glyps->pgdf->pgb->sizlBitmap.cx; glyph->height = (UINT16)glyps->pgdf->pgb->sizlBitmap.cy; now += sizeof(*glyph); chunk->data_size += sizeof(*glyph); str->data_size += sizeof(*glyph); if (!glyph->height) { continue; } stride = ALIGN(glyph->width * bpp, 8) >> 3; end_line = (UINT8 *)glyps->pgdf->pgb->aj - stride; line = (UINT8 *)glyps->pgdf->pgb->aj + stride * (glyph->height - 1); for (; line != end_line; line -= stride) { UINT8 *bits_pos = line; UINT8 *bits_end = bits_pos + stride; for (; bits_pos != bits_end; bits_pos++) { UINT8 val; int i; if (end - now < sizeof(*bits_pos)) { NEW_DATA_CHUNK(&pdev->Res.num_glyphs_pages, PAGE_SIZE); } *(UINT8 *)now = *bits_pos; now += sizeof(*bits_pos); chunk->data_size += sizeof(*bits_pos); str->data_size += sizeof(*bits_pos); } } } *chunk_ptr = chunk; *now_ptr = now; *end_ptr = end; DEBUG_PRINT((pdev, 14, "%s: done\n", __FUNCTION__)); } static _inline BOOL add_glyphs(PDev *pdev, QXLString *str, ULONG count, GLYPHPOS *glyps, QXLDataChunk **chunk, UINT8 **now, UINT8 **end, POINTL *delta, QXLPoint **str_pos) { if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) { add_rast_glyphs(pdev, str, count, glyps, chunk, now, end, 1, delta, str_pos); } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) { add_rast_glyphs(pdev, str, count, glyps, chunk, now, end, 4, delta, str_pos); } return TRUE; } static void FreeSring(PDev *pdev, Resource *res) { QXLPHYSICAL chunk_phys; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); chunk_phys = ((QXLString *)res->res)->chunk.next_chunk; while (chunk_phys) { QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys, pdev->main_mem_slot); chunk_phys = chunk->next_chunk; FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); ONDBG(pdev->Res.num_glyphs_pages--); } FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); ONDBG(pdev->Res.num_glyphs_pages--); DEBUG_PRINT((pdev, 14, "%s: done\n", __FUNCTION__)); } #define TEXT_ALLOC_SIZE sizeof(Resource) + sizeof(QXLString) + 512 BOOL QXLGetStr(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *str_phys, FONTOBJ *font, STROBJ *str) { Resource *str_res; QXLString *qxl_str; QXLDataChunk *chunk; UINT8 *now; UINT8 *end; BOOL more; static int id_QXLGetStr = 0; POINTL delta; POINTL *delta_ptr; QXLPoint *str_pos; DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); str_res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, TEXT_ALLOC_SIZE); ONDBG(pdev->Res.num_glyphs_pages++); str_res->refs = 1; str_res->free = FreeSring; qxl_str = (QXLString *)str_res->res; qxl_str->data_size = 0; qxl_str->length = (UINT16)str->cGlyphs; qxl_str->flags = 0; if (font->flFontType & FO_TYPE_RASTER) { qxl_str->flags = (font->flFontType & FO_GRAY16) ? SPICE_STRING_FLAGS_RASTER_A4 : SPICE_STRING_FLAGS_RASTER_A1; } chunk = &qxl_str->chunk; chunk->data_size = 0; chunk->prev_chunk = 0; chunk->next_chunk = 0; now = chunk->data; end = (UINT8 *)str_res + TEXT_ALLOC_SIZE; if (str->ulCharInc) { str_pos = NULL; if (str->flAccel & SO_VERTICAL) { delta.x = 0; delta.y = (str->flAccel & SO_REVERSED) ? -(LONG)str->ulCharInc : str->ulCharInc; } else { delta.x = (str->flAccel & SO_REVERSED) ? -(LONG)str->ulCharInc : str->ulCharInc; delta.y = 0; } delta_ptr = δ } else { delta_ptr = NULL; } STROBJ_vEnumStart(str); do { ULONG count; GLYPHPOS *glyps; if (str->pgp) { count = str->cGlyphs; glyps = str->pgp; more = FALSE; } else { more = STROBJ_bEnum(str, &count, &glyps); if (more == DDI_ERROR) { goto error; } } if (!add_glyphs(pdev, qxl_str, count, glyps, &chunk, &now, &end, delta_ptr, &str_pos)) { goto error; } } while (more); *str_phys = PA(pdev, str_res->res, pdev->main_mem_slot); DrawableAddRes(pdev, drawable, str_res); RELEASE_RES(pdev, str_res); DEBUG_PRINT((pdev, 10, "%s: done size %u\n", __FUNCTION__, qxl_str->data_size)); return TRUE; error: FreeSring(pdev, str_res); DEBUG_PRINT((pdev, 10, "%s: error\n", __FUNCTION__)); return FALSE; } QXLCursorCmd *CursorCmd(PDev *pdev) { QXLCursorCmd *cursor_cmd; QXLOutput *output; DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); output = (QXLOutput *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(QXLOutput) + sizeof(QXLCursorCmd)); output->num_res = 0; cursor_cmd = (QXLCursorCmd *)output->data; cursor_cmd->release_info.id = (UINT64)output; ONDBG(pdev->Res.num_outputs++); //todo: atomic DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); return cursor_cmd; } void PushCursorCmd(PDev *pdev, QXLCursorCmd *cursor_cmd) { QXLCommand *cmd; DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); WaitForCursorRing(pdev); cmd = SPICE_RING_PROD_ITEM(pdev->cursor_ring); cmd->type = QXL_CMD_CURSOR; cmd->data = PA(pdev, cursor_cmd, pdev->main_mem_slot); PUSH_CURSOR_CMD(pdev); DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); } typedef struct InternalCursor { struct InternalCursor *next; RingItem lru_link; HSURF hsurf; ULONG unique; QXLCursor cursor; } InternalCursor; #define CURSOR_HASH_VAL(hsurf) (HSURF_HASH_VAL(hsurf) & CURSOR_HASH_NASKE) static void CursorCacheRemove(PDev *pdev, InternalCursor *cursor) { InternalCursor **internal; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); ASSERT(pdev, cursor->unique); internal = &pdev->Res.cursor_cache[CURSOR_HASH_VAL(cursor->hsurf)]; while (*internal) { if ((*internal)->hsurf == cursor->hsurf) { if ((*internal) == cursor) { *internal = cursor->next; RingRemove(pdev, &cursor->lru_link); RELEASE_RES(pdev, (Resource *)((UINT8 *)cursor - sizeof(Resource))); pdev->Res.num_cursors--; return; } DEBUG_PRINT((pdev, 0, "%s: unexpected\n", __FUNCTION__)); } internal = &(*internal)->next; } ASSERT(pdev, FALSE); } static void CursorCacheAdd(PDev *pdev, InternalCursor *cursor) { int key; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); if (!cursor->unique) { return; } if (pdev->Res.num_cursors == CURSOR_CACHE_SIZE) { ASSERT(pdev, RingGetTail(pdev, &pdev->Res.dynamic->cursors_lru)); CursorCacheRemove(pdev, CONTAINEROF(RingGetTail(pdev, &pdev->Res.dynamic->cursors_lru), InternalCursor, lru_link)); } key = CURSOR_HASH_VAL(cursor->hsurf); cursor->next = pdev->Res.cursor_cache[key]; pdev->Res.cursor_cache[key] = cursor; RingAdd(pdev, &pdev->Res.dynamic->cursors_lru, &cursor->lru_link); GET_RES((Resource *)((UINT8 *)cursor - sizeof(Resource))); pdev->Res.num_cursors++; } static InternalCursor *CursorCacheGet(PDev *pdev, HSURF hsurf, UINT32 unique) { InternalCursor **internal; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); if (!unique) { return NULL; } internal = &pdev->Res.cursor_cache[CURSOR_HASH_VAL(hsurf)]; while (*internal) { InternalCursor *now = *internal; if (now->hsurf == hsurf) { if (now->unique == unique) { RingRemove(pdev, &now->lru_link); RingAdd(pdev, &pdev->Res.dynamic->cursors_lru, &now->lru_link); return now; } CursorCacheRemove(pdev, now); break; } internal = &now->next; } return NULL; } static void FreeCursor(PDev *pdev, Resource *res) { QXLPHYSICAL chunk_phys; DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); chunk_phys = ((InternalCursor *)res->res)->cursor.chunk.next_chunk; while (chunk_phys) { QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys, pdev->main_mem_slot); chunk_phys = chunk->next_chunk; FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); ONDBG(pdev->Res.num_cursor_pages--); } FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); ONDBG(pdev->Res.num_cursor_pages--); DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); } typedef struct NewCursorInfo { QXLCursor *cursor; QXLDataChunk *chunk; UINT8 *now; UINT8 *end; } NewCursorInfo; #define CURSOR_ALLOC_SIZE (PAGE_SIZE << 1) static BOOL GetCursorCommon(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf, UINT16 type, NewCursorInfo *info, BOOL *in_cach) { InternalCursor *internal; QXLCursor *cursor; Resource *res; ULONG unique; UINT8 *src; UINT8 *src_end; int line_size; HSURF bitmap = 0; SURFOBJ *local_surf = surf; DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); *in_cach = FALSE; unique = (surf->fjBitmap & BMF_DONTCACHE) ? 0 : surf->iUniq; if ((internal = CursorCacheGet(pdev, surf->hsurf, unique))) { res = (Resource *)((UINT8 *)internal - sizeof(Resource)); CursorCmdAddRes(pdev, cmd, res); cmd->u.set.shape = PA(pdev, &internal->cursor, pdev->main_mem_slot); *in_cach = TRUE; return TRUE; } if (surf->iType != STYPE_BITMAP) { RECTL dest_rect; POINTL src_pos; ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP || surf->iBitmapFormat == BMF_16BPP); /* copying the surface to a bitmap */ bitmap = (HSURF)EngCreateBitmap(surf->sizlBitmap, surf->lDelta, surf->iBitmapFormat, 0, NULL); if (!bitmap) { DEBUG_PRINT((pdev, 0, "%s: EngCreateBitmap failed\n", __FUNCTION__)); return FALSE; } if (!EngAssociateSurface(bitmap, pdev->eng, 0)) { DEBUG_PRINT((pdev, 0, "%s: EngAssociateSurface failed\n", __FUNCTION__)); goto error; } if (!(local_surf = EngLockSurface(bitmap))) { DEBUG_PRINT((pdev, 0, "%s: EngLockSurface failed\n", __FUNCTION__)); goto error; } dest_rect.top = 0; dest_rect.left = 0; dest_rect.bottom = surf->sizlBitmap.cy; dest_rect.right = surf->sizlBitmap.cx; src_pos.x = 0; src_pos.y = 0; if (!BitBltFromDev(pdev, surf, local_surf, NULL, NULL, NULL, &dest_rect, src_pos, NULL, NULL, NULL, 0xcccc)) { goto error; } } ASSERT(pdev, sizeof(Resource) + sizeof(InternalCursor) < CURSOR_ALLOC_SIZE); res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, CURSOR_ALLOC_SIZE); ONDBG(pdev->Res.num_cursor_pages++); res->refs = 1; res->free = FreeCursor; internal = (InternalCursor *)res->res; internal->hsurf = surf->hsurf; internal->unique = unique; RingItemInit(&internal->lru_link); cursor = info->cursor = &internal->cursor; cursor->header.type = type; cursor->header.unique = unique ? ++pdev->Res.last_cursor_id : 0; cursor->header.width = (UINT16)local_surf->sizlBitmap.cx; cursor->header.height = (type == SPICE_CURSOR_TYPE_MONO) ? (UINT16)local_surf->sizlBitmap.cy >> 1 : (UINT16)local_surf->sizlBitmap.cy; cursor->header.hot_spot_x = (UINT16)hot_x; cursor->header.hot_spot_y = (UINT16)hot_y; cursor->data_size = 0; info->chunk = &cursor->chunk; info->chunk->data_size = 0; info->chunk->prev_chunk = 0; info->chunk->next_chunk = 0; info->now = info->chunk->data; info->end = (UINT8 *)res + CURSOR_ALLOC_SIZE; switch (type) { case SPICE_CURSOR_TYPE_ALPHA: case SPICE_CURSOR_TYPE_COLOR32: line_size = cursor->header.width << 2; break; case SPICE_CURSOR_TYPE_MONO: line_size = ALIGN(cursor->header.width, 8) >> 3; break; case SPICE_CURSOR_TYPE_COLOR4: line_size = ALIGN(cursor->header.width, 2) >> 1; break; case SPICE_CURSOR_TYPE_COLOR8: line_size = cursor->header.width; break; case SPICE_CURSOR_TYPE_COLOR16: line_size = cursor->header.width << 1; break; case SPICE_CURSOR_TYPE_COLOR24: line_size = cursor->header.width * 3; break; } cursor->data_size = line_size * local_surf->sizlBitmap.cy; src = local_surf->pvScan0; src_end = src + (local_surf->lDelta * local_surf->sizlBitmap.cy); for (; src != src_end; src += local_surf->lDelta) { PutBytes(pdev, &info->chunk, &info->now, &info->end, src, line_size, &pdev->Res.num_cursor_pages, PAGE_SIZE, FALSE); } CursorCacheAdd(pdev, internal); CursorCmdAddRes(pdev, cmd, res); RELEASE_RES(pdev, res); cmd->u.set.shape = PA(pdev, &internal->cursor, pdev->main_mem_slot); DEBUG_PRINT((pdev, 11, "%s: done, data_size %u\n", __FUNCTION__, cursor->data_size)); if (local_surf != surf) { EngUnlockSurface(local_surf); EngDeleteSurface(bitmap); } return TRUE; error: if (bitmap) { ASSERT(pdev, local_surf != surf); EngDeleteSurface(bitmap); } return FALSE; } BOOL GetAlphaCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf) { NewCursorInfo info; BOOL ret; BOOL in_cache; ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP); ASSERT(pdev, surf->sizlBitmap.cx > 0 && surf->sizlBitmap.cy > 0); DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); ret = GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, SPICE_CURSOR_TYPE_ALPHA, &info, &in_cache); DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); return ret; } BOOL GetMonoCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf) { NewCursorInfo info; BOOL ret; BOOL in_cache; ASSERT(pdev, surf->iBitmapFormat == BMF_1BPP); ASSERT(pdev, surf->sizlBitmap.cy > 0 && (surf->sizlBitmap.cy & 1) == 0); ASSERT(pdev, surf->sizlBitmap.cx > 0); DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); ret = GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, SPICE_CURSOR_TYPE_MONO, &info, &in_cache); DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); return ret; } BOOL GetColorCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf, SURFOBJ *mask, XLATEOBJ *color_trans) { NewCursorInfo info; UINT16 type; BOOL in_cache; DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); ASSERT(pdev, surf && mask); ASSERT(pdev, surf->sizlBitmap.cx > 0 && surf->sizlBitmap.cy); if ( mask->sizlBitmap.cx != surf->sizlBitmap.cx || mask->sizlBitmap.cy != surf->sizlBitmap.cy * 2 ) { DEBUG_PRINT((pdev, 0, "%s: err mask size, surf(%d, %d) mask(%d, %d)\n", __FUNCTION__, surf->sizlBitmap.cx, surf->sizlBitmap.cy, mask->sizlBitmap.cx, mask->sizlBitmap.cy)); return FALSE; } switch (surf->iBitmapFormat) { case BMF_32BPP: type = SPICE_CURSOR_TYPE_COLOR32; break; case BMF_24BPP: type = SPICE_CURSOR_TYPE_COLOR24; break; case BMF_16BPP: type = SPICE_CURSOR_TYPE_COLOR16; break; case BMF_8BPP: type = SPICE_CURSOR_TYPE_COLOR8; break; case BMF_4BPP: type = SPICE_CURSOR_TYPE_COLOR4; break; default: DEBUG_PRINT((pdev, 0, "%s: unexpected format\n", __FUNCTION__)); return FALSE; } if (!GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, type, &info, &in_cache)) { return FALSE; } if (!in_cache) { int line_size; UINT8 *src; UINT8 *src_end; if (type == SPICE_CURSOR_TYPE_COLOR8) { DEBUG_PRINT((pdev, 8, "%s: SPICE_CURSOR_TYPE_COLOR8\n", __FUNCTION__)); ASSERT(pdev, color_trans); ASSERT(pdev, color_trans->pulXlate); ASSERT(pdev, color_trans->flXlate & XO_TABLE); ASSERT(pdev, color_trans->cEntries == 256); if (pdev->bitmap_format == BMF_32BPP) { PutBytes(pdev, &info.chunk, &info.now, &info.end, (UINT8 *)color_trans->pulXlate, 256 << 2, &pdev->Res.num_cursor_pages, PAGE_SIZE, FALSE); } else { int i; for (i = 0; i < 256; i++) { UINT32 ent = _16bppTo32bpp(color_trans->pulXlate[i]); PutBytes(pdev, &info.chunk, &info.now, &info.end, (UINT8 *)&ent, 4, &pdev->Res.num_cursor_pages, PAGE_SIZE, FALSE); } } info.cursor->data_size += 256 << 2; } else if (type == SPICE_CURSOR_TYPE_COLOR4) { ASSERT(pdev, color_trans); ASSERT(pdev, color_trans->pulXlate); ASSERT(pdev, color_trans->flXlate & XO_TABLE); ASSERT(pdev, color_trans->cEntries == 16); if (pdev->bitmap_format == BMF_32BPP) { PutBytes(pdev, &info.chunk, &info.now, &info.end, (UINT8 *)color_trans->pulXlate, 16 << 2, &pdev->Res.num_cursor_pages, PAGE_SIZE, FALSE); } else { int i; for (i = 0; i < 16; i++) { UINT32 ent = _16bppTo32bpp(color_trans->pulXlate[i]); PutBytes(pdev, &info.chunk, &info.now, &info.end, (UINT8 *)&ent, 4, &pdev->Res.num_cursor_pages, PAGE_SIZE, FALSE); } } info.cursor->data_size += 16 << 2; } ASSERT(pdev, mask->iBitmapFormat == BMF_1BPP); ASSERT(pdev, mask->iType == STYPE_BITMAP); line_size = ALIGN(mask->sizlBitmap.cx, 8) >> 3; info.cursor->data_size += line_size * surf->sizlBitmap.cy; src = mask->pvScan0; src_end = src + (mask->lDelta * surf->sizlBitmap.cy); for (; src != src_end; src += mask->lDelta) { PutBytes(pdev, &info.chunk, &info.now, &info.end, src, line_size, &pdev->Res.num_cursor_pages, PAGE_SIZE, FALSE); } } DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); return TRUE; } BOOL GetTransparentCursor(PDev *pdev, QXLCursorCmd *cmd) { Resource *res; InternalCursor *internal; QXLCursor *cursor; DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); ASSERT(pdev, sizeof(Resource) + sizeof(InternalCursor) < PAGE_SIZE); res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(Resource) + sizeof(InternalCursor)); ONDBG(pdev->Res.num_cursor_pages++); res->refs = 1; res->free = FreeCursor; internal = (InternalCursor *)res->res; internal->hsurf = NULL; internal->unique = 0; RingItemInit(&internal->lru_link); cursor = &internal->cursor; cursor->header.type = SPICE_CURSOR_TYPE_MONO; cursor->header.unique = 0; cursor->header.width = 0; cursor->header.height = 0; cursor->header.hot_spot_x = 0; cursor->header.hot_spot_y = 0; cursor->data_size = 0; cursor->chunk.data_size = 0; CursorCmdAddRes(pdev, cmd, res); RELEASE_RES(pdev, res); cmd->u.set.shape = PA(pdev, &internal->cursor, pdev->main_mem_slot); DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); return TRUE; } static void quic_usr_error(QuicUsrContext *usr, const char *format, ...) { QuicData *quic_data = (QuicData *)usr; va_list ap; va_start(ap, format); DebugPrintV(quic_data->pdev, format, ap); va_end(ap); EngDebugBreak(); } static void quic_usr_warn(QuicUsrContext *usr, const char *format, ...) { QuicData *quic_data = (QuicData *)usr; va_list ap; va_start(ap, format); DebugPrintV(quic_data->pdev, format, ap); va_end(ap); } static void *quic_usr_malloc(QuicUsrContext *usr, int size) { return EngAllocMem(0, size, ALLOC_TAG); } static void quic_usr_free(QuicUsrContext *usr, void *ptr) { EngFreeMem(ptr); } BOOL ResInit(PDev *pdev) { QuicData *usr_data; if (!(usr_data = EngAllocMem(FL_ZERO_MEMORY, sizeof(QuicData), ALLOC_TAG))) { return FALSE; } usr_data->user.error = quic_usr_error; usr_data->user.warn = quic_usr_warn; usr_data->user.info = quic_usr_warn; usr_data->user.malloc = quic_usr_malloc; usr_data->user.free = quic_usr_free; usr_data->user.more_space = quic_usr_more_space; usr_data->user.more_lines = quic_usr_more_lines; usr_data->pdev = pdev; if (!(usr_data->quic = quic_create(&usr_data->user))) { EngFreeMem(usr_data); return FALSE; } pdev->quic_data = usr_data; return TRUE; } void ResDestroy(PDev *pdev) { QuicData *usr_data = pdev->quic_data; quic_destroy(usr_data->quic); EngFreeMem(usr_data); } void ResInitGlobals() { image_id_sem = EngCreateSemaphore(); if (!image_id_sem) { EngDebugBreak(); } quic_init(); } void ResDestroyGlobals() { EngDeleteSemaphore(image_id_sem); image_id_sem = NULL; } void CheckAndSetSSE2() { _asm { mov eax, 0x0000001 cpuid and edx, 0x4000000 mov have_sse2, edx } if (have_sse2) { have_sse2 = TRUE; } } void ResetAllDevices() { UINT32 i; EngAcquireSemaphore(res_sem); for (i = 0; i < num_global_res; i++) { if (global_res[i].driver) { DWORD length; if (EngDeviceIoControl(global_res[i].driver, IOCTL_VIDEO_RESET_DEVICE, NULL, 0, NULL, 0, &length)) { DEBUG_PRINT((NULL, 0, "%s: reset to device failed 0x%lx\n", __FUNCTION__, global_res[i].driver)); } } } EngReleaseSemaphore(res_sem); }