summaryrefslogtreecommitdiff
path: root/display/surface.c
blob: 8422cd36f32580158c025b490d36daed0e9a0ac5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/*
   Copyright (C) 2009 Red Hat, Inc.

   This software is licensed under the GNU General Public License,
   version 2 (GPLv2) (see COPYING for details), subject to the
   following clarification.

   With respect to binaries built using the Microsoft(R) Windows
   Driver Kit (WDK), GPLv2 does not extend to any code contained in or
   derived from the WDK ("WDK Code").  As to WDK Code, by using or
   distributing such binaries you agree to be bound by the Microsoft
   Software License Terms for the WDK.  All WDK Code is considered by
   the GPLv2 licensors to qualify for the special exception stated in
   section 3 of GPLv2 (commonly known as the system library
   exception).

   There is NO WARRANTY for this software, express or implied,
   including the implied warranties of NON-INFRINGEMENT, TITLE,
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include "stddef.h"

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include "os_dep.h"

#include "winerror.h"
#include "windef.h"
#include "wingdi.h"
#include "winddi.h"
#include "devioctl.h"
#include "ntddvdeo.h"

#include "qxldd.h"
#include "utils.h"
#include "mspace.h"
#include "res.h"
#include "surface.h"

static BOOL CreateDrawArea(PDev *pdev, UINT8 *base_mem, ULONG format, UINT32 cx, UINT32 cy,
                           UINT32 stride, UINT32 surface_id)
{
    SIZEL  size;
    DrawArea *drawarea;

    size.cx = cx;
    size.cy = cy;

    drawarea = &GetSurfaceInfo(pdev, surface_id)->draw_area;

    if (!(drawarea->bitmap = (HSURF)EngCreateBitmap(size, stride, format, 0, base_mem))) {
        DEBUG_PRINT((pdev, 0, "%s: EngCreateBitmap failed\n", __FUNCTION__));
        return FALSE;
    }

    if (!EngAssociateSurface(drawarea->bitmap, pdev->eng, 0)) {
        DEBUG_PRINT((pdev, 0, "%s: EngAssociateSurface failed\n", __FUNCTION__));
        goto error;
    }

    if (!(drawarea->surf_obj = EngLockSurface(drawarea->bitmap))) {
        DEBUG_PRINT((pdev, 0, "%s: EngLockSurface failed\n", __FUNCTION__));
        goto error;
    }

    drawarea->base_mem = base_mem;

    return TRUE;
error:
    EngDeleteSurface(drawarea->bitmap);
    return FALSE;
}

static VOID FreeDrawArea(DrawArea *drawarea)
{
    if (drawarea->surf_obj) {
        EngUnlockSurface(drawarea->surf_obj);
        EngDeleteSurface(drawarea->bitmap);
        drawarea->surf_obj = NULL;
    }
}

static void BitmapFormatToDepthAndSurfaceFormat(ULONG format, UINT32 *depth, UINT32 *surface_format)
{
    switch (format) {
        case BMF_16BPP:
            *surface_format = SPICE_SURFACE_FMT_16_555;
            *depth = 16;
            break;
        case BMF_24BPP:
        case BMF_32BPP:
            *surface_format = SPICE_SURFACE_FMT_32_xRGB;
            *depth = 32;
            break;
        default:
            *depth = 0;
            break;
    };
}

static UINT8 *CreateSurfaceHelper(PDev *pdev, UINT32 surface_id,
                                  UINT32 cx, UINT32 cy, ULONG format,
                                  UINT8 allocation_type,
                                  INT32 *stride, UINT32 *surface_format,
                                  QXLPHYSICAL *phys_mem)
{
    UINT32 depth;
    SurfaceInfo *surface_info = GetSurfaceInfo(pdev, surface_id);
    UINT8 *base_mem;
    int size;

    BitmapFormatToDepthAndSurfaceFormat(format, &depth, surface_format);
    ASSERT(pdev, depth != 0);
    ASSERT(pdev, stride);
    QXLGetSurface(pdev, phys_mem, cx, cy, depth, stride, &base_mem, allocation_type);
    DEBUG_PRINT((pdev, 3,
        "%s: %d, pm %0lX, fmt %d, d %d, s (%d, %d) st %d\n",
        __FUNCTION__, surface_id, (uint64_t)*phys_mem, *surface_format,
        depth, cx, cy, *stride));
    size = abs(*stride) * cy;
    if (!base_mem) {
        DEBUG_PRINT((pdev, 0, "%s: %p: %d: QXLGetSurface failed (%d bytes alloc)\n",
            __FUNCTION__, pdev, surface_id, size));
        return NULL;
    }
    if (!CreateDrawArea(pdev, base_mem, surface_info->bitmap_format, cx, cy, *stride, surface_id)) {
        DEBUG_PRINT((pdev, 0, "%s: %p: CreateDrawArea failed (%d)\n",
            __FUNCTION__, pdev, surface_id, size));
        // TODO: Why did it fail? nothing in the MSDN
        QXLDelSurface(pdev, base_mem, allocation_type);
        return NULL;
    }
    return base_mem;
}

static void SendSurfaceCreateCommand(PDev *pdev, UINT32 surface_id, SIZEL size,
                                     UINT32 surface_format, INT32 stride, QXLPHYSICAL phys_mem,
                                     int keep_data)
{
    QXLSurfaceCmd *surface;

    surface = SurfaceCmd(pdev, QXL_SURFACE_CMD_CREATE, surface_id);
    if (keep_data) {
        surface->flags |= QXL_SURF_FLAG_KEEP_DATA;
    }
    surface->u.surface_create.format = surface_format;
    surface->u.surface_create.width = size.cx;
    surface->u.surface_create.height = size.cy;
    surface->u.surface_create.stride = stride;
    surface->u.surface_create.data = phys_mem;
    PushSurfaceCmd(pdev, surface);
}

HBITMAP CreateDeviceBitmap(PDev *pdev, SIZEL size, ULONG format, QXLPHYSICAL *phys_mem,
                           UINT8 **base_mem, UINT32 surface_id, UINT8 allocation_type)
{
    UINT32 surface_format, depth;
    HBITMAP hbitmap;
    INT32 stride;
    SurfaceInfo *surface_info;

    DEBUG_PRINT((pdev, 9, "%s: %p: %d, (%dx%d), %d\n", __FUNCTION__, pdev, surface_id,
                size.cx, size.cy, format));
    surface_info = GetSurfaceInfo(pdev, surface_id);

    if (!(hbitmap = EngCreateDeviceBitmap((DHSURF)surface_info, size, format))) {
        DEBUG_PRINT((pdev, 0, "%s: EngCreateDeviceBitmap failed, pdev 0x%lx, surface_id=%d\n",
                    __FUNCTION__, pdev, surface_id));
        goto out_error1;
    }

    if (!EngAssociateSurface((HSURF)hbitmap, pdev->eng, QXL_SURFACE_HOOKS)) {
        DEBUG_PRINT((pdev, 0, "%s: EngAssociateSurface failed\n", __FUNCTION__));
        goto out_error2;
    }
    surface_info->u.pdev = pdev;
    surface_info->hbitmap = hbitmap;
    surface_info->copy = NULL;
    surface_info->size = size;
    surface_info->bitmap_format = format;
    if ((*base_mem = CreateSurfaceHelper(pdev, surface_id, size.cx, size.cy, format,
                                         allocation_type, &stride, &surface_format,
                                         phys_mem)) == NULL) {
        DEBUG_PRINT((pdev, 0, "%s: failed, pdev 0x%lx, surface_id=%d\n",
                    __FUNCTION__, pdev, surface_id));
        goto out_error2;
    }

    if (allocation_type != DEVICE_BITMAP_ALLOCATION_TYPE_SURF0) {
        SendSurfaceCreateCommand(pdev, surface_id, size, surface_format, -stride, *phys_mem, 0);
    }

    return hbitmap;
out_error2:
    EngDeleteSurface((HSURF)hbitmap);
out_error1:
    return 0;
}

VOID DeleteDeviceBitmap(PDev *pdev, UINT32 surface_id, UINT8 allocation_type)
{
    DrawArea *drawarea;

    drawarea = &GetSurfaceInfo(pdev,surface_id)->draw_area;

    FreeDrawArea(drawarea);

    if (allocation_type != DEVICE_BITMAP_ALLOCATION_TYPE_SURF0 &&
        pdev->Res->surfaces_info[surface_id].draw_area.base_mem != NULL) {

        if (allocation_type == DEVICE_BITMAP_ALLOCATION_TYPE_RAM) {
            /* server side this surface is already destroyed, just free it here */
            ASSERT(pdev, pdev->Res->surfaces_info[surface_id].draw_area.base_mem ==
                pdev->Res->surfaces_info[surface_id].copy);
            QXLDelSurface(pdev, pdev->Res->surfaces_info[surface_id].draw_area.base_mem,
                allocation_type);
            FreeSurfaceInfo(pdev, surface_id);
        } else {
            QXLSurfaceCmd *surface_cmd;
            surface_cmd = SurfaceCmd(pdev, QXL_SURFACE_CMD_DESTROY, surface_id);
            QXLGetDelSurface(pdev, surface_cmd, surface_id, allocation_type);
            PushSurfaceCmd(pdev, surface_cmd);
        }
    }
}

static void CleanupSurfaceInfo(PDev *pdev, UINT32 surface_id, UINT8 allocation_type)
{
    SurfaceInfo *surface_info = GetSurfaceInfo(pdev, surface_id);

    FreeDrawArea(&surface_info->draw_area);
    if (surface_info->draw_area.base_mem != NULL) {
        QXLDelSurface(pdev, surface_info->draw_area.base_mem, allocation_type);
    }
}

BOOL MoveSurfaceToVideoRam(PDev *pdev, UINT32 surface_id)
{
    QXLSurfaceCmd *surface;
    UINT32 surface_format;
    UINT32 depth;
    int count_used = 0;
    int size;
    INT32 stride = 0;
    QXLPHYSICAL phys_mem;
    SurfaceInfo *surface_info = GetSurfaceInfo(pdev, surface_id);
    UINT32 cx = surface_info->size.cx;
    UINT32 cy = surface_info->size.cy;
    UINT8 *base_mem;

    DEBUG_PRINT((pdev, 3, "%s: %d\n", __FUNCTION__, surface_id));
    if ((base_mem = CreateSurfaceHelper(pdev, surface_id, cx, cy, surface_info->bitmap_format,
                                        DEVICE_BITMAP_ALLOCATION_TYPE_VRAM,
                                        &stride, &surface_format, &phys_mem)) == NULL) {
        DEBUG_PRINT((pdev, 0, "%s: %p: %d: failed\n", __FUNCTION__, pdev, surface_id));
        return FALSE;
    }
    size = abs(stride) * cy;
    if (!EngModifySurface((HSURF)surface_info->hbitmap, pdev->eng, QXL_SURFACE_HOOKS,
        MS_NOTSYSTEMMEMORY, (DHSURF)surface_info, NULL, 0, NULL)) {
        DEBUG_PRINT((pdev, 0, "%s: %p: %d: EngModifySurface failed\n",
            __FUNCTION__, pdev, surface_id));
        CleanupSurfaceInfo(pdev, surface_id, DEVICE_BITMAP_ALLOCATION_TYPE_VRAM);
        return FALSE;
    }
    DEBUG_PRINT((pdev, 3, "%s: stride = %d, phys_mem = %0lX, base_mem = %p\n",
        __FUNCTION__, -stride, (uint64_t)phys_mem, base_mem));
    DEBUG_PRINT((pdev, 3, "%s: copy %d bytes to %d\n", __FUNCTION__, size, surface_id));
    // Everything allocated, nothing can fail (API wise) from this point
    RtlCopyMemory(base_mem, surface_info->copy, size);
    EngFreeMem(surface_info->copy);
    surface_info->copy = NULL;
    SendSurfaceCreateCommand(pdev, surface_id, surface_info->size, surface_format,
                             -stride, phys_mem, 1);
    return TRUE;
}

/* when we return from S3 we need to resend all the surface creation commands.
 * Actually moving the memory vram<->guest is not strictly neccessary since vram
 * is not reset during the suspend, so contents are not lost */
int MoveAllSurfacesToVideoRam(PDev *pdev)
{
    UINT32 surface_id;
    SurfaceInfo *surface_info;

    /* brute force implementation - alternative is to keep an updated used_surfaces list */
    DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__));

    for (surface_id = 1 ; surface_id < pdev->n_surfaces ; ++surface_id) {
        surface_info = GetSurfaceInfo(pdev, surface_id);
        if (!surface_info->draw_area.base_mem) {
            continue;
        }
        if (surface_info->u.pdev != pdev) {
            DEBUG_PRINT((pdev, 3, "%s: %p: not our pdev (%p)\n", __FUNCTION__, pdev,
                         surface_info->u.pdev));
            continue;
        }
        if (surface_info->draw_area.surf_obj) {
            DEBUG_PRINT((pdev, 3, "%s: surface_id = %d, surf_obj not empty\n", __FUNCTION__,
                         surface_id));
            continue;
        }
        if (surface_info->copy == NULL) {
            DEBUG_PRINT((pdev, 3, "%s: %p: %d: no copy buffer, ignored\n", __FUNCTION__,
                         pdev, surface_id));
        }
        if (!MoveSurfaceToVideoRam(pdev, surface_id)) {
            /* Some of the surfaces have not been moved to video ram.
             * they will remain managed by GDI. */
            DEBUG_PRINT((pdev, 0, "%s: %p: %d: failed moving to vram\n", __FUNCTION__,
                         pdev, surface_id));
        }
    }
    return TRUE;
}

BOOL MoveAllSurfacesToRam(PDev *pdev)
{
    UINT32 surface_id;
    SurfaceInfo *surface_info;
    SURFOBJ *surf_obj;
    UINT8 *copy;
    UINT8 *line0;
    int size;
    QXLPHYSICAL phys_mem;

    for (surface_id = 1 ; surface_id < pdev->n_surfaces ; ++surface_id) {
        surface_info = GetSurfaceInfo(pdev, surface_id);
        if (!surface_info->draw_area.base_mem) {
            continue;
        }
        surf_obj = surface_info->draw_area.surf_obj;
        if (!surf_obj) {
            DEBUG_PRINT((pdev, 3, "%s: %d: no surfobj, not copying\n", __FUNCTION__, surface_id));
            continue;
        }
        size = surf_obj->sizlBitmap.cy * abs(surf_obj->lDelta);
        copy = EngAllocMem(0, size, ALLOC_TAG);
        DEBUG_PRINT((pdev, 3, "%s: %d: copying #%d to %p (%d)\n", __FUNCTION__, surface_id, size,
            copy, surf_obj->lDelta));
        RtlCopyMemory(copy, surface_info->draw_area.base_mem, size);
        surface_info->copy = copy;
        line0 = surf_obj->lDelta > 0 ? copy : copy + abs(surf_obj->lDelta) *
                (surf_obj->sizlBitmap.cy - 1);
        if (!EngModifySurface((HSURF)surface_info->hbitmap,
                      pdev->eng,
                      0, /* from the example: used to monitor memory HOOK_COPYBITS | HOOK_BITBLT, */
                      0,                    /* It's system-memory */
                      (DHSURF)surface_info,
                      line0,
                      surf_obj->lDelta,
                      NULL)) {
            /* Send a create messsage for this surface - we previously did a destroy all. */
            EngFreeMem(surface_info->copy);
            surface_info->copy = NULL;
            DEBUG_PRINT((pdev, 0, "%s: %d: EngModifySurface failed, sending create\n",
                         __FUNCTION__, surface_id));
            phys_mem = SurfaceToPhysical(pdev, surface_info->draw_area.base_mem);
            /*
             * TODO: bug. create_command should be send for all surfaces >= surface_id
             *       since they stay in the pci-bar. Alternatively,
             *       don't call destroy_all_surfaces, instead send destroy commands
             *       for all surfaces with id < surface_id.
             */
            SendSurfaceCreateCommand(pdev, surface_id, surf_obj->sizlBitmap,
                                     surface_info->bitmap_format, -surf_obj->lDelta, phys_mem,
                                     /* the surface is still there, tell server not to erase */
                                     1);
            return FALSE;
        }
        QXLDelSurface(pdev, surface_info->draw_area.base_mem, DEVICE_BITMAP_ALLOCATION_TYPE_VRAM);
        surface_info->draw_area.base_mem = copy;
        FreeDrawArea(&surface_info->draw_area);
    }
    return TRUE;
}