1/*
2 * Copyright 2011 Joakim Sindholt <opensource@zhasha.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE. */
22
23#include "iunknown.h"
24#include "surface9.h"
25#include "device9.h"
26
27/* for marking dirty */
28#include "basetexture9.h"
29#include "texture9.h"
30#include "cubetexture9.h"
31
32#include "nine_helpers.h"
33#include "nine_pipe.h"
34#include "nine_dump.h"
35#include "nine_memory_helper.h"
36#include "nine_state.h"
37
38#include "pipe/p_context.h"
39#include "pipe/p_screen.h"
40#include "pipe/p_state.h"
41
42#include "util/u_math.h"
43#include "util/u_inlines.h"
44#include "util/u_surface.h"
45
46#define DBG_CHANNEL DBG_SURFACE
47
48static void
49NineSurface9_CreatePipeSurfaces( struct NineSurface9 *This );
50
51HRESULT
52NineSurface9_ctor( struct NineSurface9 *This,
53                   struct NineUnknownParams *pParams,
54                   struct NineUnknown *pContainer,
55                   struct pipe_resource *pResource,
56                   struct nine_allocation *user_buffer,
57                   uint8_t TextureType,
58                   unsigned Level,
59                   unsigned Layer,
60                   D3DSURFACE_DESC *pDesc )
61{
62    HRESULT hr;
63    bool allocate = !pContainer && pDesc->Format != D3DFMT_NULL;
64    D3DMULTISAMPLE_TYPE multisample_type;
65
66    DBG("This=%p pDevice=%p pResource=%p Level=%u Layer=%u pDesc=%p\n",
67        This, pParams->device, pResource, Level, Layer, pDesc);
68
69    /* Mark this as a special surface held by another internal resource. */
70    pParams->container = pContainer;
71    This->base.base.device = pParams->device; /* Early fill this field in case of failure */
72    /* Make sure there's a Desc */
73    assert(pDesc);
74
75    assert(allocate || pResource || user_buffer ||
76           pDesc->Format == D3DFMT_NULL);
77    assert(!allocate || (!pResource && !user_buffer));
78    assert(!pResource || !user_buffer);
79    assert(!user_buffer || pDesc->Pool != D3DPOOL_DEFAULT);
80    assert(!pResource || pDesc->Pool == D3DPOOL_DEFAULT);
81    /* Allocation only from create_zs_or_rt_surface with params 0 0 0 */
82    assert(!allocate || (Level == 0 && Layer == 0 && TextureType == 0));
83
84    This->data = user_buffer;
85
86    multisample_type = pDesc->MultiSampleType;
87
88    /* Map MultiSampleQuality to MultiSampleType */
89    hr = d3dmultisample_type_check(pParams->device->screen,
90                                   pDesc->Format,
91                                   &multisample_type,
92                                   pDesc->MultiSampleQuality,
93                                   NULL);
94    if (FAILED(hr)) {
95        return hr;
96    }
97
98    /* TODO: this is (except width and height) duplicate from
99     * container info (in the pContainer case). Some refactoring is
100     * needed to avoid duplication */
101    This->base.info.screen = pParams->device->screen;
102    This->base.info.target = PIPE_TEXTURE_2D;
103    This->base.info.width0 = pDesc->Width;
104    This->base.info.height0 = pDesc->Height;
105    This->base.info.depth0 = 1;
106    This->base.info.last_level = 0;
107    This->base.info.array_size = 1;
108    This->base.info.nr_samples = multisample_type;
109    This->base.info.nr_storage_samples = multisample_type;
110    This->base.info.usage = PIPE_USAGE_DEFAULT;
111    This->base.info.bind = PIPE_BIND_SAMPLER_VIEW; /* StretchRect */
112
113    if (pDesc->Usage & D3DUSAGE_RENDERTARGET) {
114        This->base.info.bind |= PIPE_BIND_RENDER_TARGET;
115    } else if (pDesc->Usage & D3DUSAGE_DEPTHSTENCIL) {
116        if (!depth_stencil_format(pDesc->Format))
117            return D3DERR_INVALIDCALL;
118        This->base.info.bind = d3d9_get_pipe_depth_format_bindings(pDesc->Format);
119        if (TextureType)
120            This->base.info.bind |= PIPE_BIND_SAMPLER_VIEW;
121    }
122
123    This->base.info.flags = 0;
124    This->base.info.format = d3d9_to_pipe_format_checked(This->base.info.screen,
125                                                         pDesc->Format,
126                                                         This->base.info.target,
127                                                         This->base.info.nr_samples,
128                                                         This->base.info.bind,
129                                                         FALSE,
130                                                         pDesc->Pool == D3DPOOL_SCRATCH);
131
132    if (This->base.info.format == PIPE_FORMAT_NONE && pDesc->Format != D3DFMT_NULL)
133        return D3DERR_INVALIDCALL;
134
135    if (allocate && compressed_format(pDesc->Format)) {
136        const unsigned w = util_format_get_blockwidth(This->base.info.format);
137        const unsigned h = util_format_get_blockheight(This->base.info.format);
138
139        /* Note: In the !allocate case, the test could fail (lower levels of a texture) */
140        user_assert(!(pDesc->Width % w) && !(pDesc->Height % h), D3DERR_INVALIDCALL);
141    }
142
143    /* Get true format */
144    This->format_internal = d3d9_to_pipe_format_checked(This->base.info.screen,
145                                                         pDesc->Format,
146                                                         This->base.info.target,
147                                                         This->base.info.nr_samples,
148                                                         This->base.info.bind,
149                                                         FALSE,
150                                                         TRUE);
151    if (This->base.info.format != This->format_internal ||
152        /* DYNAMIC Textures requires same stride as ram buffers.
153         * The workaround stores a copy in RAM for locks. It eats more virtual space,
154         * but that is compensated by the use of shmem */
155        (pParams->device->workarounds.dynamic_texture_workaround &&
156         pDesc->Pool == D3DPOOL_DEFAULT && pDesc->Usage & D3DUSAGE_DYNAMIC)) {
157        This->data_internal = nine_allocate(pParams->device->allocator,
158            nine_format_get_level_alloc_size(This->format_internal,
159                                             pDesc->Width,
160                                             pDesc->Height,
161                                             0));
162        if (!This->data_internal)
163            return E_OUTOFMEMORY;
164        This->stride_internal = nine_format_get_stride(This->format_internal,
165                                                         pDesc->Width);
166    }
167
168    if ((allocate && pDesc->Pool != D3DPOOL_DEFAULT) || pDesc->Format == D3DFMT_NULL) {
169        /* Ram buffer with no parent. Has to allocate the resource itself */
170        assert(!user_buffer);
171        This->data = nine_allocate(pParams->device->allocator,
172            nine_format_get_level_alloc_size(This->base.info.format,
173                                             pDesc->Width,
174                                             pDesc->Height,
175                                             0));
176        if (!This->data)
177            return E_OUTOFMEMORY;
178    }
179
180    hr = NineResource9_ctor(&This->base, pParams, pResource,
181                            allocate && (pDesc->Pool == D3DPOOL_DEFAULT),
182                            D3DRTYPE_SURFACE, pDesc->Pool, pDesc->Usage);
183
184    if (FAILED(hr))
185        return hr;
186
187    This->transfer = NULL;
188
189    This->texture = TextureType;
190    This->level = Level;
191    This->level_actual = Level;
192    This->layer = Layer;
193    This->desc = *pDesc;
194
195    This->stride = nine_format_get_stride(This->base.info.format, pDesc->Width);
196
197    if (This->base.resource && (pDesc->Usage & D3DUSAGE_DYNAMIC))
198        This->base.resource->flags |= NINE_RESOURCE_FLAG_LOCKABLE;
199
200    if (This->base.resource && (pDesc->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)))
201        NineSurface9_CreatePipeSurfaces(This);
202
203    /* TODO: investigate what else exactly needs to be cleared */
204    if (This->base.resource && (pDesc->Usage & D3DUSAGE_RENDERTARGET))
205        nine_context_clear_render_target(pParams->device, This, 0, 0, 0, pDesc->Width, pDesc->Height);
206
207    NineSurface9_Dump(This);
208
209    return D3D_OK;
210}
211
212void
213NineSurface9_dtor( struct NineSurface9 *This )
214{
215    bool is_worker = nine_context_is_worker(This->base.base.device);
216    DBG("This=%p\n", This);
217
218    if (This->transfer) {
219        struct pipe_context *pipe = nine_context_get_pipe_multithread(This->base.base.device);
220        pipe->texture_unmap(pipe, This->transfer);
221        This->transfer = NULL;
222    }
223
224    /* Note: Following condition cannot happen currently, since we
225     * refcount the surface in the functions increasing
226     * pending_uploads_counter. */
227    if (p_atomic_read(&This->pending_uploads_counter))
228        nine_csmt_process(This->base.base.device);
229
230    pipe_surface_reference(&This->surface[0], NULL);
231    pipe_surface_reference(&This->surface[1], NULL);
232
233    if (!is_worker && This->lock_count && (This->data_internal || This->data)) {
234        /* For is_worker nine_free_worker will handle it */
235        nine_pointer_strongrelease(This->base.base.device->allocator,
236                                   This->data_internal ? This->data_internal : This->data);
237    }
238
239    /* Release system memory when we have to manage it (no parent) */
240    if (This->data) {
241        if (is_worker)
242            nine_free_worker(This->base.base.device->allocator, This->data);
243        else
244            nine_free(This->base.base.device->allocator, This->data);
245    }
246    if (This->data_internal) {
247        if (is_worker)
248            nine_free_worker(This->base.base.device->allocator, This->data_internal);
249        else
250            nine_free(This->base.base.device->allocator, This->data_internal);
251    }
252    NineResource9_dtor(&This->base);
253}
254
255static void
256NineSurface9_CreatePipeSurfaces( struct NineSurface9 *This )
257{
258    struct pipe_context *pipe;
259    struct pipe_screen *screen = NineDevice9_GetScreen(This->base.base.device);
260    struct pipe_resource *resource = This->base.resource;
261    struct pipe_surface templ;
262    enum pipe_format srgb_format;
263
264    assert(This->desc.Pool == D3DPOOL_DEFAULT);
265    assert(resource);
266
267    srgb_format = util_format_srgb(resource->format);
268    if (srgb_format == PIPE_FORMAT_NONE ||
269        !screen->is_format_supported(screen, srgb_format,
270                                     resource->target, 0, 0, resource->bind))
271        srgb_format = resource->format;
272
273    memset(&templ, 0, sizeof(templ));
274    templ.format = resource->format;
275    templ.u.tex.level = This->level;
276    templ.u.tex.first_layer = This->layer;
277    templ.u.tex.last_layer = This->layer;
278
279    pipe = nine_context_get_pipe_acquire(This->base.base.device);
280
281    This->surface[0] = pipe->create_surface(pipe, resource, &templ);
282
283    memset(&templ, 0, sizeof(templ));
284    templ.format = srgb_format;
285    templ.u.tex.level = This->level;
286    templ.u.tex.first_layer = This->layer;
287    templ.u.tex.last_layer = This->layer;
288
289    This->surface[1] = pipe->create_surface(pipe, resource, &templ);
290
291    nine_context_get_pipe_release(This->base.base.device);
292
293    assert(This->surface[0]); /* TODO: Handle failure */
294    assert(This->surface[1]);
295}
296
297#if defined(DEBUG) || !defined(NDEBUG)
298void
299NineSurface9_Dump( struct NineSurface9 *This )
300{
301    struct NineBaseTexture9 *tex;
302    GUID id = IID_IDirect3DBaseTexture9;
303    REFIID ref = &id;
304
305    DBG("\nNineSurface9(%p->%p/%p): Pool=%s Type=%s Usage=%s\n"
306        "Dims=%ux%u Format=%s Stride=%u Lockable=%i\n"
307        "Level=%u(%u), Layer=%u\n", This, This->base.resource, This->data,
308        nine_D3DPOOL_to_str(This->desc.Pool),
309        nine_D3DRTYPE_to_str(This->desc.Type),
310        nine_D3DUSAGE_to_str(This->desc.Usage),
311        This->desc.Width, This->desc.Height,
312        d3dformat_to_string(This->desc.Format), This->stride,
313        This->base.resource &&
314        (This->base.resource->flags & NINE_RESOURCE_FLAG_LOCKABLE),
315        This->level, This->level_actual, This->layer);
316
317    if (!This->base.base.container)
318        return;
319    NineUnknown_QueryInterface(This->base.base.container, ref, (void **)&tex);
320    if (tex) {
321        NineBaseTexture9_Dump(tex);
322        NineUnknown_Release(NineUnknown(tex));
323    }
324}
325#endif /* DEBUG || !NDEBUG */
326
327HRESULT NINE_WINAPI
328NineSurface9_GetContainer( struct NineSurface9 *This,
329                           REFIID riid,
330                           void **ppContainer )
331{
332    HRESULT hr;
333    char guid_str[64];
334
335    DBG("This=%p riid=%p id=%s ppContainer=%p\n",
336        This, riid, riid ? GUID_sprintf(guid_str, riid) : "", ppContainer);
337
338    (void)guid_str;
339
340    if (!ppContainer) return E_POINTER;
341
342    /* Use device for OffscreenPlainSurface, DepthStencilSurface and RenderTarget */
343    hr = NineUnknown_QueryInterface(NineUnknown(This)->container ?
344                                        NineUnknown(This)->container : &NineUnknown(This)->device->base,
345                                    riid, ppContainer);
346    if (FAILED(hr))
347        DBG("QueryInterface FAILED!\n");
348    return hr;
349}
350
351void
352NineSurface9_MarkContainerDirty( struct NineSurface9 *This )
353{
354    if (This->texture) {
355        struct NineBaseTexture9 *tex =
356            NineBaseTexture9(This->base.base.container);
357        assert(tex);
358        assert(This->texture == D3DRTYPE_TEXTURE ||
359               This->texture == D3DRTYPE_CUBETEXTURE);
360        if (This->base.pool == D3DPOOL_MANAGED)
361            tex->managed.dirty = TRUE;
362        else
363        if (This->base.usage & D3DUSAGE_AUTOGENMIPMAP)
364            tex->dirty_mip = TRUE;
365
366        BASETEX_REGISTER_UPDATE(tex);
367    }
368}
369
370HRESULT NINE_WINAPI
371NineSurface9_GetDesc( struct NineSurface9 *This,
372                      D3DSURFACE_DESC *pDesc )
373{
374    user_assert(pDesc != NULL, E_POINTER);
375    DBG("This=%p pDesc=%p\n", This, pDesc);
376    *pDesc = This->desc;
377    return D3D_OK;
378}
379
380/* Add the dirty rects to the source texture */
381inline void
382NineSurface9_AddDirtyRect( struct NineSurface9 *This,
383                           const struct pipe_box *box )
384{
385    RECT dirty_rect;
386
387    DBG("This=%p box=%p\n", This, box);
388
389    assert (This->base.pool != D3DPOOL_MANAGED ||
390            This->texture == D3DRTYPE_CUBETEXTURE ||
391            This->texture == D3DRTYPE_TEXTURE);
392
393    if (This->base.pool == D3DPOOL_DEFAULT)
394        return;
395
396    /* Add a dirty rect to level 0 of the parent texture */
397    dirty_rect.left = box->x << This->level_actual;
398    dirty_rect.right = dirty_rect.left + (box->width << This->level_actual);
399    dirty_rect.top = box->y << This->level_actual;
400    dirty_rect.bottom = dirty_rect.top + (box->height << This->level_actual);
401
402    if (This->texture == D3DRTYPE_TEXTURE) {
403        struct NineTexture9 *tex =
404            NineTexture9(This->base.base.container);
405
406        NineTexture9_AddDirtyRect(tex, &dirty_rect);
407    } else if (This->texture == D3DRTYPE_CUBETEXTURE) {
408        struct NineCubeTexture9 *ctex =
409            NineCubeTexture9(This->base.base.container);
410
411        NineCubeTexture9_AddDirtyRect(ctex, This->layer, &dirty_rect);
412    }
413}
414
415static inline unsigned
416NineSurface9_GetSystemMemOffset(enum pipe_format format, unsigned stride,
417                                int x, int y)
418{
419    unsigned x_offset = util_format_get_stride(format, x);
420
421    y = util_format_get_nblocksy(format, y);
422
423    return y * stride + x_offset;
424}
425
426HRESULT NINE_WINAPI
427NineSurface9_LockRect( struct NineSurface9 *This,
428                       D3DLOCKED_RECT *pLockedRect,
429                       const RECT *pRect,
430                       DWORD Flags )
431{
432    struct pipe_resource *resource = This->base.resource;
433    struct pipe_context *pipe;
434    struct pipe_box box;
435    unsigned usage;
436
437    DBG("This=%p pLockedRect=%p pRect=%p[%u..%u,%u..%u] Flags=%s\n", This,
438        pLockedRect, pRect,
439        pRect ? pRect->left : 0, pRect ? pRect->right : 0,
440        pRect ? pRect->top : 0, pRect ? pRect->bottom : 0,
441        nine_D3DLOCK_to_str(Flags));
442    NineSurface9_Dump(This);
443
444    /* check if it's already locked */
445    user_assert(This->lock_count == 0, D3DERR_INVALIDCALL);
446
447    /* set pBits to NULL after lock_count check */
448    user_assert(pLockedRect, E_POINTER);
449    pLockedRect->pBits = NULL;
450
451#ifdef NINE_STRICT
452    user_assert(This->base.pool != D3DPOOL_DEFAULT ||
453                (resource && (resource->flags & NINE_RESOURCE_FLAG_LOCKABLE)),
454                D3DERR_INVALIDCALL);
455#endif
456    user_assert(!((Flags & D3DLOCK_DISCARD) && (Flags & D3DLOCK_READONLY)),
457                D3DERR_INVALIDCALL);
458
459    user_assert(This->desc.MultiSampleType == D3DMULTISAMPLE_NONE,
460                D3DERR_INVALIDCALL);
461
462    if (pRect && This->desc.Pool == D3DPOOL_DEFAULT &&
463        util_format_is_compressed(This->base.info.format)) {
464        const unsigned w = util_format_get_blockwidth(This->base.info.format);
465        const unsigned h = util_format_get_blockheight(This->base.info.format);
466        user_assert((pRect->left == 0 && pRect->right == This->desc.Width &&
467                     pRect->top == 0 && pRect->bottom == This->desc.Height) ||
468                    (!(pRect->left % w) && !(pRect->right % w) &&
469                    !(pRect->top % h) && !(pRect->bottom % h)),
470                    D3DERR_INVALIDCALL);
471    }
472
473    if (Flags & D3DLOCK_DISCARD) {
474        usage = PIPE_MAP_WRITE | PIPE_MAP_DISCARD_RANGE;
475    } else {
476        usage = (Flags & D3DLOCK_READONLY) ?
477            PIPE_MAP_READ : PIPE_MAP_READ_WRITE;
478    }
479    if (Flags & D3DLOCK_DONOTWAIT)
480        usage |= PIPE_MAP_DONTBLOCK;
481
482    if (pRect) {
483        /* Windows XP accepts invalid locking rectangles, Windows 7 rejects
484         * them. Use Windows XP behaviour for now. */
485        rect_to_pipe_box(&box, pRect);
486    } else {
487        u_box_origin_2d(This->desc.Width, This->desc.Height, &box);
488    }
489    box.z = This->layer;
490
491    user_warn(This->desc.Format == D3DFMT_NULL);
492
493    if (p_atomic_read(&This->pending_uploads_counter))
494        nine_csmt_process(This->base.base.device);
495
496    if (This->data_internal || This->data) {
497        enum pipe_format format = This->base.info.format;
498        unsigned stride = This->stride;
499        uint8_t *data = nine_get_pointer(This->base.base.device->allocator, This->data_internal ? This->data_internal : This->data);
500        if (This->data_internal) {
501            format = This->format_internal;
502            stride = This->stride_internal;
503        }
504        /* ATI1 and ATI2 need special handling, because of d3d9 bug.
505         * We must advertise to the application as if it is uncompressed
506         * and bpp 8, and the app has a workaround to work with the fact
507         * that it is actually compressed. */
508        if (is_ATI1_ATI2(format)) {
509            pLockedRect->Pitch = This->desc.Width;
510            pLockedRect->pBits = data + box.y * This->desc.Width + box.x;
511        } else {
512            pLockedRect->Pitch = stride;
513            pLockedRect->pBits = data +
514                NineSurface9_GetSystemMemOffset(format,
515                                                stride,
516                                                box.x,
517                                                box.y);
518        }
519        DBG("returning system memory %p\n", pLockedRect->pBits);
520    } else {
521        bool no_refs = !p_atomic_read(&This->base.base.bind) &&
522            !(This->base.base.container && p_atomic_read(&This->base.base.container->bind));
523        DBG("mapping pipe_resource %p (level=%u usage=%x)\n",
524            resource, This->level, usage);
525
526        /* if the object is not bound internally, there can't be any pending
527         * operation with the surface in the queue */
528        if (no_refs)
529            pipe = nine_context_get_pipe_acquire(This->base.base.device);
530        else
531            pipe = NineDevice9_GetPipe(This->base.base.device);
532        pLockedRect->pBits = pipe->texture_map(pipe, resource,
533                                                This->level, usage, &box,
534                                                &This->transfer);
535        if (no_refs)
536            nine_context_get_pipe_release(This->base.base.device);
537        if (!This->transfer) {
538            DBG("texture_map failed\n");
539            if (Flags & D3DLOCK_DONOTWAIT)
540                return D3DERR_WASSTILLDRAWING;
541            return D3DERR_INVALIDCALL;
542        }
543        pLockedRect->Pitch = This->transfer->stride;
544    }
545
546    if (!(Flags & (D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_READONLY))) {
547        NineSurface9_MarkContainerDirty(This);
548        NineSurface9_AddDirtyRect(This, &box);
549    }
550
551    ++This->lock_count;
552    return D3D_OK;
553}
554
555HRESULT NINE_WINAPI
556NineSurface9_UnlockRect( struct NineSurface9 *This )
557{
558    struct pipe_box dst_box, src_box;
559    struct pipe_context *pipe;
560    DBG("This=%p lock_count=%u\n", This, This->lock_count);
561    user_assert(This->lock_count, D3DERR_INVALIDCALL);
562    if (This->transfer) {
563        pipe = nine_context_get_pipe_acquire(This->base.base.device);
564        pipe->texture_unmap(pipe, This->transfer);
565        nine_context_get_pipe_release(This->base.base.device);
566        This->transfer = NULL;
567    }
568    --This->lock_count;
569
570    if (This->data_internal) {
571        nine_pointer_weakrelease(This->base.base.device->allocator, This->data_internal);
572        if (This->data) {
573            (void) util_format_translate(This->base.info.format,
574                                         nine_get_pointer(This->base.base.device->allocator, This->data),
575                                         This->stride,
576                                         0, 0,
577                                         This->format_internal,
578                                         nine_get_pointer(This->base.base.device->allocator, This->data_internal),
579                                         This->stride_internal,
580                                         0, 0,
581                                         This->desc.Width, This->desc.Height);
582            nine_pointer_weakrelease(This->base.base.device->allocator, This->data);
583            nine_pointer_strongrelease(This->base.base.device->allocator, This->data_internal);
584        } else {
585            u_box_2d_zslice(0, 0, This->layer,
586                            This->desc.Width, This->desc.Height, &dst_box);
587            u_box_2d_zslice(0, 0, 0,
588                            This->desc.Width, This->desc.Height, &src_box);
589
590            nine_context_box_upload(This->base.base.device,
591                                    &This->pending_uploads_counter,
592                                    (struct NineUnknown *)This,
593                                    This->base.resource,
594                                    This->level,
595                                    &dst_box,
596                                    This->format_internal,
597                                    nine_get_pointer(This->base.base.device->allocator, This->data_internal),
598                                    This->stride_internal,
599                                    0, /* depth = 1 */
600                                    &src_box);
601            nine_pointer_delayedstrongrelease(This->base.base.device->allocator, This->data_internal, &This->pending_uploads_counter);
602        }
603    } else if (This->data) {
604        nine_pointer_weakrelease(This->base.base.device->allocator, This->data);
605    }
606
607    return D3D_OK;
608}
609
610HRESULT NINE_WINAPI
611NineSurface9_GetDC( struct NineSurface9 *This,
612                    HDC *phdc )
613{
614    STUB(D3DERR_INVALIDCALL);
615}
616
617HRESULT NINE_WINAPI
618NineSurface9_ReleaseDC( struct NineSurface9 *This,
619                        HDC hdc )
620{
621    STUB(D3DERR_INVALIDCALL);
622}
623
624IDirect3DSurface9Vtbl NineSurface9_vtable = {
625    (void *)NineUnknown_QueryInterface,
626    (void *)NineUnknown_AddRef,
627    (void *)NineUnknown_Release,
628    (void *)NineUnknown_GetDevice, /* actually part of Resource9 iface */
629    (void *)NineUnknown_SetPrivateData,
630    (void *)NineUnknown_GetPrivateData,
631    (void *)NineUnknown_FreePrivateData,
632    (void *)NineResource9_SetPriority,
633    (void *)NineResource9_GetPriority,
634    (void *)NineResource9_PreLoad,
635    (void *)NineResource9_GetType,
636    (void *)NineSurface9_GetContainer,
637    (void *)NineSurface9_GetDesc,
638    (void *)NineSurface9_LockRect,
639    (void *)NineSurface9_UnlockRect,
640    (void *)NineSurface9_GetDC,
641    (void *)NineSurface9_ReleaseDC
642};
643
644/* When this function is called, we have already checked
645 * The copy regions fit the surfaces */
646void
647NineSurface9_CopyMemToDefault( struct NineSurface9 *This,
648                               struct NineSurface9 *From,
649                               const POINT *pDestPoint,
650                               const RECT *pSourceRect )
651{
652    struct pipe_resource *r_dst = This->base.resource;
653    struct pipe_box dst_box, src_box;
654    int src_x, src_y, dst_x, dst_y, copy_width, copy_height;
655
656    assert(This->base.pool == D3DPOOL_DEFAULT &&
657           From->base.pool == D3DPOOL_SYSTEMMEM);
658
659    if (pDestPoint) {
660        dst_x = pDestPoint->x;
661        dst_y = pDestPoint->y;
662    } else {
663        dst_x = 0;
664        dst_y = 0;
665    }
666
667    if (pSourceRect) {
668        src_x = pSourceRect->left;
669        src_y = pSourceRect->top;
670        copy_width = pSourceRect->right - pSourceRect->left;
671        copy_height = pSourceRect->bottom - pSourceRect->top;
672    } else {
673        src_x = 0;
674        src_y = 0;
675        copy_width = From->desc.Width;
676        copy_height = From->desc.Height;
677    }
678
679    u_box_2d_zslice(dst_x, dst_y, This->layer,
680                    copy_width, copy_height, &dst_box);
681    u_box_2d_zslice(src_x, src_y, 0,
682                    copy_width, copy_height, &src_box);
683
684    if (This->data_internal) {
685        (void) util_format_translate(This->format_internal,
686                                     nine_get_pointer(This->base.base.device->allocator, This->data_internal),
687                                     This->stride_internal,
688                                     dst_x, dst_y,
689                                     From->base.info.format,
690                                     nine_get_pointer(This->base.base.device->allocator, From->data),
691                                     From->stride,
692                                     src_x, src_y,
693                                     copy_width, copy_height);
694        nine_pointer_weakrelease(This->base.base.device->allocator, From->data);
695        nine_pointer_strongrelease(This->base.base.device->allocator, This->data_internal);
696    }
697
698    nine_context_box_upload(This->base.base.device,
699                            &From->pending_uploads_counter,
700                            (struct NineUnknown *)From,
701                            r_dst,
702                            This->level,
703                            &dst_box,
704                            From->base.info.format,
705                            nine_get_pointer(This->base.base.device->allocator, From->data),
706                            From->stride,
707                            0, /* depth = 1 */
708                            &src_box);
709    nine_pointer_delayedstrongrelease(This->base.base.device->allocator, From->data, &From->pending_uploads_counter);
710
711    if (From->texture == D3DRTYPE_TEXTURE) {
712        struct NineTexture9 *tex =
713            NineTexture9(From->base.base.container);
714        /* D3DPOOL_SYSTEMMEM with buffer content passed
715         * from the user: execute the upload right now.
716         * It is possible it is enough to delay upload
717         * until the surface refcount is 0, but the
718         * bind refcount may not be 0, and thus the dtor
719         * is not executed (and doesn't trigger the
720         * pending_uploads_counter check). */
721        if (!tex->managed_buffer)
722            nine_csmt_process(This->base.base.device);
723    }
724
725    NineSurface9_MarkContainerDirty(This);
726}
727
728void
729NineSurface9_CopyDefaultToMem( struct NineSurface9 *This,
730                               struct NineSurface9 *From )
731{
732    struct pipe_context *pipe;
733    struct pipe_resource *r_src = From->base.resource;
734    struct pipe_transfer *transfer;
735    struct pipe_box src_box;
736    uint8_t *p_dst;
737    const uint8_t *p_src;
738
739    assert(This->base.pool == D3DPOOL_SYSTEMMEM &&
740           From->base.pool == D3DPOOL_DEFAULT);
741
742    assert(This->desc.Width == From->desc.Width);
743    assert(This->desc.Height == From->desc.Height);
744
745    u_box_origin_2d(This->desc.Width, This->desc.Height, &src_box);
746    src_box.z = From->layer;
747
748    if (p_atomic_read(&This->pending_uploads_counter))
749        nine_csmt_process(This->base.base.device);
750
751    pipe = NineDevice9_GetPipe(This->base.base.device);
752    p_src = pipe->texture_map(pipe, r_src, From->level,
753                               PIPE_MAP_READ,
754                               &src_box, &transfer);
755    p_dst = nine_get_pointer(This->base.base.device->allocator, This->data);
756
757    assert (p_src && p_dst);
758
759    util_copy_rect(p_dst, This->base.info.format,
760                   This->stride, 0, 0,
761                   This->desc.Width, This->desc.Height,
762                   p_src,
763                   transfer->stride, 0, 0);
764
765    pipe->texture_unmap(pipe, transfer);
766
767    nine_pointer_weakrelease(This->base.base.device->allocator, This->data);
768}
769
770
771/* Gladly, rendering to a MANAGED surface is not permitted, so we will
772 * never have to do the reverse, i.e. download the surface.
773 */
774HRESULT
775NineSurface9_UploadSelf( struct NineSurface9 *This,
776                         const struct pipe_box *damaged )
777{
778    struct pipe_resource *res = This->base.resource;
779    struct pipe_box box;
780
781    DBG("This=%p damaged=%p\n", This, damaged);
782
783    assert(This->base.pool == D3DPOOL_MANAGED);
784
785    if (damaged) {
786        box = *damaged;
787        box.z = This->layer;
788        box.depth = 1;
789    } else {
790        box.x = 0;
791        box.y = 0;
792        box.z = This->layer;
793        box.width = This->desc.Width;
794        box.height = This->desc.Height;
795        box.depth = 1;
796    }
797
798    nine_context_box_upload(This->base.base.device,
799                            &This->pending_uploads_counter,
800                            (struct NineUnknown *)This,
801                            res,
802                            This->level,
803                            &box,
804                            res->format,
805                            nine_get_pointer(This->base.base.device->allocator, This->data),
806                            This->stride,
807                            0, /* depth = 1 */
808                            &box);
809    nine_pointer_delayedstrongrelease(This->base.base.device->allocator, This->data, &This->pending_uploads_counter);
810
811    return D3D_OK;
812}
813
814/* Currently nine_context uses the NineSurface9
815 * fields when it is render target. Any modification requires
816 * pending commands with the surface to be executed. If the bind
817 * count is 0, there is no pending commands. */
818#define PROCESS_IF_BOUND(surf) \
819    if (surf->base.base.bind) \
820        nine_csmt_process(surf->base.base.device);
821
822void
823NineSurface9_SetResource( struct NineSurface9 *This,
824                          struct pipe_resource *resource, unsigned level )
825{
826    /* No need to call PROCESS_IF_BOUND, because SetResource is used only
827     * for MANAGED textures, and they are not render targets. */
828    assert(This->base.pool == D3DPOOL_MANAGED);
829    This->level = level;
830    pipe_resource_reference(&This->base.resource, resource);
831}
832
833void
834NineSurface9_SetMultiSampleType( struct NineSurface9 *This,
835                                 D3DMULTISAMPLE_TYPE mst )
836{
837    PROCESS_IF_BOUND(This);
838    This->desc.MultiSampleType = mst;
839}
840
841void
842NineSurface9_SetResourceResize( struct NineSurface9 *This,
843                                struct pipe_resource *resource )
844{
845    assert(This->level == 0 && This->level_actual == 0);
846    assert(!This->lock_count);
847    assert(This->desc.Pool == D3DPOOL_DEFAULT);
848    assert(!This->texture);
849
850    PROCESS_IF_BOUND(This);
851    pipe_resource_reference(&This->base.resource, resource);
852
853    This->desc.Width = This->base.info.width0 = resource->width0;
854    This->desc.Height = This->base.info.height0 = resource->height0;
855    This->base.info.nr_samples = resource->nr_samples;
856    This->base.info.nr_storage_samples = resource->nr_storage_samples;
857
858    This->stride = nine_format_get_stride(This->base.info.format,
859                                          This->desc.Width);
860
861    pipe_surface_reference(&This->surface[0], NULL);
862    pipe_surface_reference(&This->surface[1], NULL);
863    NineSurface9_CreatePipeSurfaces(This);
864}
865
866
867static const GUID *NineSurface9_IIDs[] = {
868    &IID_IDirect3DSurface9,
869    &IID_IDirect3DResource9,
870    &IID_IUnknown,
871    NULL
872};
873
874HRESULT
875NineSurface9_new( struct NineDevice9 *pDevice,
876                  struct NineUnknown *pContainer,
877                  struct pipe_resource *pResource,
878                  struct nine_allocation *user_buffer,
879                  uint8_t TextureType,
880                  unsigned Level,
881                  unsigned Layer,
882                  D3DSURFACE_DESC *pDesc,
883                  struct NineSurface9 **ppOut )
884{
885    NINE_DEVICE_CHILD_NEW(Surface9, ppOut, pDevice, /* args */
886                          pContainer, pResource, user_buffer,
887                          TextureType, Level, Layer, pDesc);
888}
889