diff options
Diffstat (limited to 'display/res.c')
-rw-r--r-- | display/res.c | 2433 |
1 files changed, 2433 insertions, 0 deletions
diff --git a/display/res.c b/display/res.c new file mode 100644 index 0000000..13189be --- /dev/null +++ b/display/res.c @@ -0,0 +1,2433 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "os_dep.h" +#include "res.h" +#include "ioaccess.h" +#include "utils.h" +#include "mspace.h" +#include "quic.h" +#include "lookup3.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 PA(pdev, vaddr)\ + ((pdev)->io_pages_phys + ((UINT8*)(vaddr) - (pdev)->io_pages_virt)) + +#define VA(pdev, paddr)\ + ((pdev)->io_pages_virt + ((paddr) - (pdev)->io_pages_phys)) + +#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, void *ptr); +static BOOL SetClip(PDev *pdev, CLIPOBJ *clip, QXLDrawable *drawable); + + +#define PUSH_CMD(pdev) do { \ + int notify; \ + RING_PUSH(pdev->cmd_ring, notify); \ + if (notify) { \ + WRITE_PORT_UCHAR(pdev->notify_cmd_port, 0); \ + } \ +} while (0); + +#define PUSH_CURSOR_CMD(pdev) do { \ + int notify; \ + 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; + + +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, output); + DEBUG_PRINT((pdev, 10, "%s done\n", __FUNCTION__)); + ONDBG(pdev->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 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 (;;) { + 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 (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 (;;) { + 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 (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 (RING_IS_EMPTY(pdev->release_ring)) { + QXLSleep(pdev, 10); + if (!RING_IS_EMPTY(pdev->release_ring)) { + break; + } + WRITE_PORT_UCHAR(pdev->notify_oom_port, 0); + } + 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 (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->num_free_pages, + pdev->num_outputs, + pdev->num_path_pages, + pdev->num_rects_pages, + pdev->num_bits_pages, + pdev->num_buf_pages, + pdev->num_glyphs_pages, + pdev->num_cursor_pages)); +#endif + //oom + WRITE_PORT_UCHAR(pdev->notify_oom_port, 0); + } + } + DEBUG_PRINT((pdev, 16, "%s: 0x%lx, done\n", __FUNCTION__, pdev)); +} + +static void InitMspace(PDev *pdev) +{ + size_t capacity = pdev->num_io_pages * PAGE_SIZE; + pdev->mspace = create_mspace_with_base(pdev->io_pages_virt, capacity, 0, pdev); + pdev->mspace_start = pdev->io_pages_virt; + pdev->mspace_end = pdev->io_pages_virt + capacity; +} + +static void *AllocMem(PDev* pdev, size_t size) +{ + UINT8 *ptr; + + ASSERT(pdev, pdev && pdev->mspace); + DEBUG_PRINT((pdev, 12, "%s: 0x%lx size %u\n", __FUNCTION__, pdev, size)); + + EngAcquireSemaphore(pdev->malloc_sem); + while (!(ptr = mspace_malloc(pdev->mspace, size))) { + int notify; + if (pdev->free_outputs) { + pdev->free_outputs = ReleaseOutput(pdev, pdev->free_outputs); + continue; + } + WaitForReleaseRing(pdev); + pdev->free_outputs = *RING_CONS_ITEM(pdev->release_ring); + RING_POP(pdev->release_ring, notify); + } + EngReleaseSemaphore(pdev->malloc_sem); + ASSERT(pdev, ptr >= pdev->mspace_start && ptr < pdev->mspace_end); + DEBUG_PRINT((pdev, 13, "%s: 0x%lx done 0x%x\n", __FUNCTION__, pdev, ptr)); + return ptr; +} + +static void FreeMem(PDev* pdev, void *ptr) +{ + ASSERT(pdev, pdev && pdev->mspace); + ASSERT(pdev, (UINT8 *)ptr >= pdev->mspace_start && (UINT8 *)ptr < pdev->mspace_end); + mspace_free(pdev->mspace, ptr); +} + +void InitResources(PDev *pdev) +{ + int i; + + pdev->free_outputs = 0; + InitMspace(pdev); + pdev->update_id = *pdev->dev_update_id; + + RtlZeroMemory(pdev->image_key_lookup, sizeof(pdev->image_key_lookup)); + RtlZeroMemory(pdev->cache_image_pool, sizeof(pdev->cache_image_pool)); + RingInit(&pdev->cache_image_lru); + for (i = 0; i < IMAGE_POOL_SIZE; i++) { + RingAdd(pdev, &pdev->cache_image_lru, &pdev->cache_image_pool[i].lru_link); + } + RtlZeroMemory(pdev->image_cache, sizeof(pdev->image_cache)); + + RtlZeroMemory(pdev->cursor_cache, sizeof(pdev->cursor_cache)); + RingInit(&pdev->cursors_lru); + pdev->num_cursors = 0; + pdev->last_cursor_id = 0; + + RtlZeroMemory(pdev->palette_cache, sizeof(pdev->palette_cache)); + RingInit(&pdev->palette_lru); + pdev->num_palettes = 0; + + 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); + } + + ONDBG(pdev->num_outputs = 0); + ONDBG(pdev->num_path_pages = 0); + ONDBG(pdev->num_rects_pages = 0); + ONDBG(pdev->num_bits_pages = 0); + ONDBG(pdev->num_buf_pages = 0); + ONDBG(pdev->num_glyphs_pages = 0); + ONDBG(pdev->num_cursor_pages = 0); + +#ifdef CALL_TEST + pdev->count_calls = TRUE; + pdev->total_calls = 0; + for (i = 0; i < NUM_CALL_COUNTERS; i++) { + pdev->call_counters[i] = 0; + } +#endif +} + +static QXLDrawable *GetDrawable(PDev *pdev) +{ + QXLOutput *output; + + output = (QXLOutput *)AllocMem(pdev, 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->num_outputs++); //todo: atomic + return (QXLDrawable *)output->data; +} + +QXLDrawable *Drawable(PDev *pdev, UINT8 type, RECTL *area, CLIPOBJ *clip) +{ + QXLDrawable *drawable; + + ASSERT(pdev, pdev && area); + + drawable = GetDrawable(pdev); + drawable->type = type; + drawable->effect = QXL_EFFECT_BLEND; + drawable->bitmap_offset = 0; + drawable->mm_time = *pdev->mm_clock; + 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 = RING_PROD_ITEM(pdev->cmd_ring); + cmd->type = QXL_CMD_DRAW; + cmd->data = PA(pdev, drawable); + PUSH_CMD(pdev); +} + +static void FreePath(PDev *pdev, Resource *res) +{ + PHYSICAL 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); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_path_pages--); + } + FreeMem(pdev, res); + ONDBG(pdev->num_path_pages--); + + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + +#define NEW_DATA_CHUNK(page_counter, size) { \ + void *ptr = AllocMem(pdev, size + sizeof(QXLDataChunk)); \ + ONDBG((*(page_counter))++); \ + chunk->next_chunk = PA(pdev, ptr); \ + ((QXLDataChunk *)ptr)->prev_chunk = PA(pdev, chunk); \ + 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(PathSeg) +\ + 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; + PathSeg *seg; + + more = PATHOBJ_bEnum(path, &data); + if (data.count == 0) { + break; + } + + if (end - now < sizeof(PathSeg)) { + size_t alloc_size = MIN(data.count << 3, sizeof(POINTFIX) * PATH_MAX_ALLOC_PONTS); + alloc_size += sizeof(PathSeg); + NEW_DATA_CHUNK(page_counter, alloc_size); + } + seg = (PathSeg*)now; + seg->flags = data.flags; + seg->count = data.count; + now = seg->data; + 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, PATH_ALLOC_SIZE); + ONDBG(pdev->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->num_path_pages); + + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); + return res; +} + +BOOL QXLGetPath(PDev *pdev, QXLDrawable *drawable, PHYSICAL *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); + DrawableAddRes(pdev, drawable, path_res); + RELEASE_RES(pdev, path_res); + return TRUE; +} + + +static void FreeClipRects(PDev *pdev, Resource *res) +{ + PHYSICAL 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); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_rects_pages--); + } + FreeMem(pdev, res); + ONDBG(pdev->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(Rect) * RECTS_NUM_PREALLOC) +#define RECTS_NUM_ALLOC 20 +#define RECTS_CHUNK_ALLOC_SIZE (sizeof(QXLDataChunk) + sizeof(Rect) * RECTS_NUM_ALLOC) + +static Resource *GetClipRects(PDev *pdev, CLIPOBJ *clip) +{ + Resource *res; + QXLClipRects *rects; + QXLDataChunk *chunk; + Rect *dest; + Rect *dest_end; + int more; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + res = (Resource *)AllocMem(pdev, RECTS_ALLOC_SIZE); + ONDBG(pdev->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 = (Rect *)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, RECTS_CHUNK_ALLOC_SIZE); + ONDBG(pdev->num_rects_pages++); + chunk->next_chunk = PA(pdev, page); + ((QXLDataChunk *)page)->prev_chunk = PA(pdev, chunk); + chunk = (QXLDataChunk *)page; + chunk->data_size = 0; + chunk->next_chunk = 0; + dest = (Rect *)chunk->data; + dest_end = dest + RECTS_NUM_ALLOC; + } + CopyRect(dest, now); + chunk->data_size += sizeof(Rect); + } + } 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 = 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, sizeof(Resource) + sizeof(QXLClipRects) + + sizeof(Rect)); + rects_res->refs = 1; + rects_res->free = FreeClipRects; + rects = (QXLClipRects *)rects_res->res; + rects->num_rects = 1; + rects->chunk.data_size = sizeof(Rect); + rects->chunk.prev_chunk = 0; + rects->chunk.next_chunk = 0; + CopyRect((Rect *)rects->chunk.data, &clip->rclBounds); + } else { + + ASSERT(pdev, clip->iDComplexity == DC_COMPLEX); + if (clip->iMode == TC_PATHOBJ) { + Resource *path_res; + PATHOBJ *path = CLIPOBJ_ppoGetPath(clip); + if (!path) { + DEBUG_PRINT((pdev, 0, "%s: get path failed\n", __FUNCTION__)); + return FALSE; + } + + path_res = __GetPath(pdev, path); + EngDeletePath(path); + DrawableAddRes(pdev, drawable, path_res); + RELEASE_RES(pdev, path_res); + drawable->clip.type = CLIP_TYPE_PATH; + drawable->clip.data = PA(pdev, path_res->res); + DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); + return TRUE; + } else { + ASSERT(pdev, clip->iMode == TC_RECTANGLES); + rects_res = GetClipRects(pdev, clip); + } + } + + DrawableAddRes(pdev, drawable, rects_res); + RELEASE_RES(pdev, rects_res); + drawable->clip.type = CLIP_TYPE_RECTS; + drawable->clip.data = PA(pdev, rects_res->res); + DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +#ifdef DBG +#define PutBytesAlign __PutBytesAlign +#define PutBytes(pdev, chunk, now, end, src, size, page_counter, alloc_size)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, page_counter, alloc_size, 1) +#else +#define PutBytesAlign(pdev, chunk, now, end, src, size, page_counter, alloc_size, alignment)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, NULL, alloc_size, alignment) +#define PutBytes(pdev, chunk, now, end, src, size, page_counter, alloc_size)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, NULL, alloc_size, 1) +#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) +{ + QXLDataChunk *chunk = *chunk_ptr; + UINT8 *now = *now_ptr; + UINT8 *end = *end_ptr; + + 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); + } + 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->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->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->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))) { + cache_image->hits++; + 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->image_cache[key]; + cache_image->hits = 1; + pdev->image_cache[key] = cache_image; +} + +static void ImageCacheRemove(PDev *pdev, CacheImage *cache_image) +{ + CacheImage **cache_img; + + if (!cache_image->hits) { + return; + } + cache_img = &pdev->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->cache_image_lru))) { + int notify; + if (pdev->free_outputs) { + pdev->free_outputs = ReleaseOutput(pdev, pdev->free_outputs); + continue; + } + WaitForReleaseRing(pdev); + pdev->free_outputs = *RING_CONS_ITEM(pdev->release_ring); + 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; + Palette 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, 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->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->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->palette_cache[PALETTE_HASH_VAL(unique)]; + while (now) { + if (now->palette.unique == unique) { + RingRemove(pdev, &now->lru_link); + RingAdd(pdev, &pdev->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->num_palettes == PALETTE_CACHE_SIZE) { + ASSERT(pdev, RingGetTail(pdev, &pdev->palette_lru)); + PaletteCacheRemove(pdev, CONTAINEROF(RingGetTail(pdev, &pdev->palette_lru), + InternalPalette, lru_link)); + } + + key = PALETTE_HASH_VAL(palette->palette.unique); + palette->next = pdev->palette_cache[key]; + pdev->palette_cache[key] = palette; + + RingAdd(pdev, &pdev->palette_lru, &palette->lru_link); + palette->refs++; + pdev->num_palettes++; + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + + +static _inline void GetPallette(PDev *pdev, Bitmap *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); + return; + } + + internal = (InternalPalette *)AllocMem(pdev, sizeof(InternalPalette) + + (color_trans->cEntries << 2)); + internal->refs = 1; + RingItemInit(&internal->lru_link); + bitmap->palette = PA(pdev, &internal->palette); + 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; + PHYSICAL chunk_phys; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + internal = (InternalImage *)res->res; + if (internal->cache) { + RingAdd(pdev, &pdev->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); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_bits_pages--); + + } + FreeMem(pdev, res); + ONDBG(pdev->num_bits_pages--); + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + +static _inline QuicImageType GetQuicImageType(UINT8 format) +{ + switch (format) { + case BITMAP_FMT_32BIT: + return QUIC_IMAGE_TYPE_RGB32; + case BITMAP_FMT_16BIT: + return QUIC_IMAGE_TYPE_RGB16; + case BITMAP_FMT_RGBA: + return QUIC_IMAGE_TYPE_RGBA; + case 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, sizeof(QXLDataChunk) + alloc_size); + new_chank->data_size = 0; + new_chank->prev_chunk = PA(pdev, usr_data->chunk); + 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); + usr_data->chunk = new_chank; + + usr_data->chunk_io_words = alloc_size >> 2; + + ONDBG(pdev->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, alloc_size); + ONDBG(pdev->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 = 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; + PHYSICAL chunk_phys; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + internal = (InternalImage *)res->res; + if (internal->cache) { + RingAdd(pdev, &pdev->cache_image_lru, &internal->cache->lru_link); + internal->cache->image = NULL; + } + + if (internal->image.bitmap.palette) { + Palette *palette = (Palette *)VA(pdev, internal->image.bitmap.palette); + 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); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_bits_pages--); + + } + + FreeMem(pdev, res); + ONDBG(pdev->num_bits_pages--); + 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; + + 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, alloc_size); + ONDBG(pdev->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 = 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); + 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; + for (; src != src_end; src -= surf->lDelta, alloc_size -= line_size) { + PutBytesAlign(pdev, &chunk, &dest, &dest_end, src, line_size, &pdev->num_bits_pages, + alloc_size, line_size); + } + + 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, + 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 = hashlittle(color_trans->pulXlate, + sizeof(*color_trans->pulXlate) * color_trans->cEntries, hash_value); + } + for (row = 0; row < height; row++) { +#ifdef ADAPTIVE_HASH + if (format == BITMAP_FMT_32BIT) { + for (i = 0; i < line_size; i += 4) { + hash_value = hashlittle(row_buf + i, 3, hash_value); + } + } else { + if (format == BITMAP_FMT_4BIT_BE && (width & 0x1)) { + last_byte = row_buf[line_size - 1] & 0xF0; + } else if (format == BITMAP_FMT_1BIT_BE && (reminder = width & 0x7)) { + last_byte = row_buf[line_size - 1] & ~((1 << (8 - reminder)) - 1); + } + if (last_byte) { + hash_value = hashlittle(row_buf, line_size - 1, hash_value); + hash_value = hashlittle(&last_byte, 1, hash_value); + } else { + hash_value = hashlittle(row_buf, line_size, hash_value); + } + } +#else + hash_value = hashlittle(row_buf, line_size, hash_value); +#endif + row_buf += stride; + } + return hash_value; +} + +static _inline UINT32 GetFormatLineSize(INT32 width, ULONG bitmap_format, UINT8 *format) +{ + switch (bitmap_format) { + case BMF_32BPP: + *format = BITMAP_FMT_32BIT; + return width << 2; + case BMF_24BPP: + *format = BITMAP_FMT_24BIT; + return width * 3; + case BMF_16BPP: + *format = BITMAP_FMT_16BIT; + return width << 1; + case BMF_8BPP: + *format = BITMAP_FMT_8BIT; + return width; + case BMF_4BPP: + *format = BITMAP_FMT_4BIT_BE; + return ALIGN(width, 2) >> 1; + case BMF_1BPP: + *format = 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 CacheImage *GetChachImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans, UINT32 *hash_key) +{ + CacheImage *cache_image; + ULONG pallette_unique; + UINT64 gdi_unique; + int pallette; + UINT32 key; + UINT8 format; + UINT32 line_size; + + 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)) { + gdi_unique = 0; + } else { + gdi_unique = surf->iUniq | ((UINT64)pallette_unique << 32); + } + + 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, + 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))) { + 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->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; +} + +BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, PHYSICAL *image_phys, SURFOBJ *surf, + Rect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache) +{ + Resource *image_res; + InternalImage *internal; + CacheImage *cache_image; + UINT32 key; + UINT8 format; + UINT32 line_size; + UINT8 *src; + 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) { + DEBUG_PRINT((pdev, 0, "%s: copy from device doing nothing!!!\n", __FUNCTION__)); + return FALSE; + } + + 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; + } + + 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, 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); + 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 ((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); + DrawableAddRes(pdev, drawable, image_res); + RELEASE_RES(pdev, image_res); + return TRUE; +} + +BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, PHYSICAL *image_phys, + SURFOBJ *surf, Rect *area) +{ + 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, surf->iBitmapFormat == BMF_32BPP && surf->iType == STYPE_BITMAP); + 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)); + + //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, BITMAP_FMT_RGBA, + 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, BITMAP_FMT_RGBA, + surf->sizlBitmap.cx, surf->sizlBitmap.cy)) { + DEBUG_PRINT((pdev, 11, "%s: ImageCacheGetByKey %u hits %u\n", __FUNCTION__, + key, 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); + 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 = BITMAP_FMT_RGBA; + cache_image->width = surf->sizlBitmap.cx; + cache_image->height = surf->sizlBitmap.cy; + ImageCacheAdd(pdev, cache_image); + RingAdd(pdev, &pdev->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, + BITMAP_FMT_RGBA, src, width << 2, key))) { + image_res = GetBitmapImage(pdev, surf, NULL, !!cache_image, width, height, + 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); + DrawableAddRes(pdev, drawable, image_res); + RELEASE_RES(pdev, image_res); + return TRUE; +} + +BOOL QXLGetBitsFromCache(PDev *pdev, QXLDrawable *drawable, UINT32 hash_key, PHYSICAL *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)) { + *image_phys = PA(pdev, &internal->image); + image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); + DrawableAddRes(pdev, drawable, image_res); + return TRUE; + } + return FALSE; +} + +BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, QMask *qxl_mask, SURFOBJ *mask, POINTL *pos, + BOOL invers, LONG width, LONG height) +{ + Rect 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 ? MASK_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)) { + 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->num_buf_pages--); + FreeMem(pdev, res); +} + +UINT8 *QXLGetBuf(PDev *pdev, QXLDrawable *drawable, PHYSICAL *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, sizeof(Resource) + size); + ONDBG(pdev->num_buf_pages++); + buf_res->refs = 1; + buf_res->free = FreeBuf; + + *buf_phys = PA(pdev, buf_res->res); + DrawableAddRes(pdev, drawable, buf_res); + RELEASE_RES(pdev, buf_res); + return buf_res->res; +} + +#ifdef UPDATE_CMD +void UpdateArea(PDev *pdev, RECTL *area) +{ + 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->num_outputs++); //todo: atomic + + CopyRect(&updat_cmd->area, area); + updat_cmd->update_id = ++pdev->update_id; + + WaitForCmdRing(pdev); + cmd = RING_PROD_ITEM(pdev->cmd_ring); + cmd->type = QXL_CMD_UPDATE; + cmd->data = PA(pdev, updat_cmd); + 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->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->update_id); +} + +#else + +void UpdateArea(PDev *pdev, RECTL *area) +{ + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + CopyRect(pdev->update_area, area); + 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, Point **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++) { + RasterGlyph *glyph; + UINT8 *line; + UINT8 *end_line; + UINT32 stride; + + if (end - now < sizeof(*glyph)) { + NEW_DATA_CHUNK(&pdev->num_glyphs_pages, PAGE_SIZE); + } + + glyph = (RasterGlyph *)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 = &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->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 void add_vec_glyphs(PDev *pdev, QXLString *str, ULONG count, GLYPHPOS *glyps, + QXLDataChunk **chunk_ptr, UINT8 **now_ptr, UINT8 **end_ptr, + POINTL *delta, Point **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++) { + VectotGlyph *glyph; + + if (end - now < sizeof(*glyph)) { + NEW_DATA_CHUNK(&pdev->num_glyphs_pages, PAGE_SIZE); + } + chunk->data_size += sizeof(*glyph); + str->data_size += sizeof(*glyph); + glyph = (VectotGlyph *)now; + now += sizeof(*glyph); + + 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 = &glyph->render_pos; + } else { + glyph->render_pos.x = glyps->ptl.x; + glyph->render_pos.y = glyps->ptl.y; + } + glyph->data_size = 0; + GetPathCommon(pdev, glyps->pgdf->ppo, &chunk, &now, &end, &glyph->data_size, + &pdev->num_glyphs_pages); + str->data_size += glyph->data_size; + } + *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, + Point **str_pos) +{ + if (str->flags & STRING_RASTER_A1) { + add_rast_glyphs(pdev, str, count, glyps, chunk, now, end, 1, delta, str_pos); + } else if (str->flags & STRING_RASTER_A4) { + add_rast_glyphs(pdev, str, count, glyps, chunk, now, end, 4, delta, str_pos); + } else { + DEBUG_PRINT((pdev, 0, "%s: vector: untested path, doing nothing!!!\n", __FUNCTION__)); + return FALSE; + add_vec_glyphs(pdev, str, count, glyps, chunk, now, end, delta, str_pos); + } + return TRUE; +} + +static void FreeSring(PDev *pdev, Resource *res) +{ + PHYSICAL 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); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_glyphs_pages--); + } + + FreeMem(pdev, res); + ONDBG(pdev->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, PHYSICAL *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; + Point *str_pos; + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + + str_res = (Resource *)AllocMem(pdev, TEXT_ALLOC_SIZE); + ONDBG(pdev->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) ? STRING_RASTER_A4 : + STRING_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); + 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, sizeof(QXLOutput) + sizeof(QXLCursorCmd)); + output->num_res = 0; + cursor_cmd = (QXLCursorCmd *)output->data; + cursor_cmd->release_info.id = (UINT64)output; + ONDBG(pdev->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 = RING_PROD_ITEM(pdev->cursor_ring); + cmd->type = QXL_CMD_CURSOR; + cmd->data = PA(pdev, cursor_cmd); + 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->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->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->num_cursors == CURSOR_CACHE_SIZE) { + ASSERT(pdev, RingGetTail(pdev, &pdev->cursors_lru)); + CursorCacheRemove(pdev, CONTAINEROF(RingGetTail(pdev, &pdev->cursors_lru), + InternalCursor, lru_link)); + } + + key = CURSOR_HASH_VAL(cursor->hsurf); + cursor->next = pdev->cursor_cache[key]; + pdev->cursor_cache[key] = cursor; + + RingAdd(pdev, &pdev->cursors_lru, &cursor->lru_link); + GET_RES((Resource *)((UINT8 *)cursor - sizeof(Resource))); + pdev->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->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->cursors_lru, &now->lru_link); + return now; + } + CursorCacheRemove(pdev, now); + break; + } + internal = &now->next; + } + return NULL; +} + +static void FreeCursor(PDev *pdev, Resource *res) +{ + PHYSICAL 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); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, res); + ONDBG(pdev->num_cursor_pages--); + } + + FreeMem(pdev, res); + ONDBG(pdev->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) +{ + InternalCursor *internal; + QXLCursor *cursor; + Resource *res; + ULONG unique; + UINT8 *src; + UINT8 *src_end; + int line_size; + + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + + 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); + return TRUE; + } + + ASSERT(pdev, sizeof(Resource) + sizeof(InternalCursor) < CURSOR_ALLOC_SIZE); + res = (Resource *)AllocMem(pdev, CURSOR_ALLOC_SIZE); + ONDBG(pdev->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->last_cursor_id : 0; + cursor->header.width = (UINT16)surf->sizlBitmap.cx; + cursor->header.height = (type == CURSOR_TYPE_MONO) ? (UINT16)surf->sizlBitmap.cy >> 1 : + (UINT16)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 CURSOR_TYPE_ALPHA: + case CURSOR_TYPE_COLOR32: + line_size = cursor->header.width << 2; + break; + case CURSOR_TYPE_MONO: + line_size = ALIGN(cursor->header.width, 8) >> 3; + break; + case CURSOR_TYPE_COLOR4: + line_size = ALIGN(cursor->header.width, 2) >> 1; + break; + case CURSOR_TYPE_COLOR8: + line_size = cursor->header.width; + break; + case CURSOR_TYPE_COLOR16: + line_size = cursor->header.width << 1; + break; + case CURSOR_TYPE_COLOR24: + line_size = cursor->header.width * 3; + break; + } + + cursor->data_size = line_size * surf->sizlBitmap.cy; + src = surf->pvScan0; + src_end = src + (surf->lDelta * surf->sizlBitmap.cy); + for (; src != src_end; src += surf->lDelta) { + PutBytes(pdev, &info->chunk, &info->now, &info->end, src, line_size, + &pdev->num_cursor_pages, PAGE_SIZE); + } + + CursorCacheAdd(pdev, internal); + CursorCmdAddRes(pdev, cmd, res); + RELEASE_RES(pdev, res); + cmd->u.set.shape = PA(pdev, &internal->cursor); + DEBUG_PRINT((pdev, 11, "%s: done, data_size %u\n", __FUNCTION__, cursor->data_size)); + return FALSE; +} + +BOOL GetAlphaCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf) +{ + NewCursorInfo info; + + ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP); + ASSERT(pdev, surf->sizlBitmap.cx > 0 && surf->sizlBitmap.cy > 0); + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, CURSOR_TYPE_ALPHA, &info); + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +BOOL GetMonoCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf) +{ + NewCursorInfo info; + + 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__)); + + GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, CURSOR_TYPE_MONO, &info); + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +BOOL GetColorCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf, + SURFOBJ *mask, XLATEOBJ *color_trans) +{ + NewCursorInfo info; + UINT16 type; + + + 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 = CURSOR_TYPE_COLOR32; + break; + case BMF_24BPP: + type = CURSOR_TYPE_COLOR24; + break; + case BMF_16BPP: + type = CURSOR_TYPE_COLOR16; + break; + case BMF_8BPP: + type = CURSOR_TYPE_COLOR8; + break; + case BMF_4BPP: + type = 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)) { + int line_size; + UINT8 *src; + UINT8 *src_end; + + if (type == CURSOR_TYPE_COLOR8) { + + DEBUG_PRINT((pdev, 8, "%s: 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->num_cursor_pages, PAGE_SIZE); + } 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->num_cursor_pages, PAGE_SIZE); + } + } + info.cursor->data_size += 256 << 2; + } else if (type == 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->num_cursor_pages, PAGE_SIZE); + } 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->num_cursor_pages, PAGE_SIZE); + } + } + info.cursor->data_size += 16 << 2; + } + 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->num_cursor_pages, PAGE_SIZE); + } + } + + 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, sizeof(Resource) + sizeof(InternalCursor)); + ONDBG(pdev->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 = 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); + + 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; +} + |