xref: /reactos/dll/directx/wine/wined3d/resource.c (revision a6726659)
1 /*
2  * Copyright 2002-2004 Jason Edmeades
3  * Copyright 2003-2004 Raphael Junqueira
4  * Copyright 2004 Christian Costa
5  * Copyright 2005 Oliver Stieber
6  * Copyright 2009-2010 Henri Verbeet for CodeWeavers
7  * Copyright 2006-2008, 2013 Stefan Dösinger for CodeWeavers
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include "config.h"
25 #include "wine/port.h"
26 #include "wined3d_private.h"
27 
28 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
29 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
30 
31 static void resource_check_usage(DWORD usage)
32 {
33     static DWORD handled = WINED3DUSAGE_RENDERTARGET
34             | WINED3DUSAGE_DEPTHSTENCIL
35             | WINED3DUSAGE_WRITEONLY
36             | WINED3DUSAGE_DYNAMIC
37             | WINED3DUSAGE_STATICDECL
38             | WINED3DUSAGE_OVERLAY
39             | WINED3DUSAGE_SCRATCH
40             | WINED3DUSAGE_PRIVATE
41             | WINED3DUSAGE_LEGACY_CUBEMAP
42             | WINED3DUSAGE_TEXTURE;
43 
44     /* WINED3DUSAGE_WRITEONLY is supposed to result in write-combined mappings
45      * being returned. OpenGL doesn't give us explicit control over that, but
46      * the hints and access flags we set for typical access patterns on
47      * dynamic resources should in theory have the same effect on the OpenGL
48      * driver. */
49 
50     if (usage & ~handled)
51     {
52         FIXME("Unhandled usage flags %#x.\n", usage & ~handled);
53         handled |= usage;
54     }
55     if ((usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_WRITEONLY)) == WINED3DUSAGE_DYNAMIC)
56         WARN_(d3d_perf)("WINED3DUSAGE_DYNAMIC used without WINED3DUSAGE_WRITEONLY.\n");
57 }
58 
59 HRESULT resource_init(struct wined3d_resource *resource, struct wined3d_device *device,
60         enum wined3d_resource_type type, const struct wined3d_format *format,
61         enum wined3d_multisample_type multisample_type, unsigned int multisample_quality,
62         unsigned int usage, unsigned int access, unsigned int width, unsigned int height, unsigned int depth,
63         unsigned int size, void *parent, const struct wined3d_parent_ops *parent_ops,
64         const struct wined3d_resource_ops *resource_ops)
65 {
66     enum wined3d_gl_resource_type base_type = WINED3D_GL_RES_TYPE_COUNT;
67     enum wined3d_gl_resource_type gl_type = WINED3D_GL_RES_TYPE_COUNT;
68     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
69     BOOL tex_2d_ok = FALSE;
70     unsigned int i;
71 
72     static const struct
73     {
74         enum wined3d_resource_type type;
75         DWORD cube_usage;
76         enum wined3d_gl_resource_type gl_type;
77     }
78     resource_types[] =
79     {
80         {WINED3D_RTYPE_BUFFER,      0,                              WINED3D_GL_RES_TYPE_BUFFER},
81         {WINED3D_RTYPE_TEXTURE_1D,  0,                              WINED3D_GL_RES_TYPE_TEX_1D},
82         {WINED3D_RTYPE_TEXTURE_2D,  0,                              WINED3D_GL_RES_TYPE_TEX_2D},
83         {WINED3D_RTYPE_TEXTURE_2D,  0,                              WINED3D_GL_RES_TYPE_TEX_RECT},
84         {WINED3D_RTYPE_TEXTURE_2D,  0,                              WINED3D_GL_RES_TYPE_RB},
85         {WINED3D_RTYPE_TEXTURE_2D,  WINED3DUSAGE_LEGACY_CUBEMAP,    WINED3D_GL_RES_TYPE_TEX_CUBE},
86         {WINED3D_RTYPE_TEXTURE_3D,  0,                              WINED3D_GL_RES_TYPE_TEX_3D},
87     };
88 
89     resource_check_usage(usage);
90 
91     if (usage & WINED3DUSAGE_SCRATCH && access & WINED3D_RESOURCE_ACCESS_GPU)
92     {
93         ERR("Trying to create a scratch resource with access flags %s.\n",
94                 wined3d_debug_resource_access(access));
95         return WINED3DERR_INVALIDCALL;
96     }
97 
98     if (!size)
99         ERR("Attempting to create a zero-sized resource.\n");
100 
101     for (i = 0; i < ARRAY_SIZE(resource_types); ++i)
102     {
103         if (resource_types[i].type != type
104                 || resource_types[i].cube_usage != (usage & WINED3DUSAGE_LEGACY_CUBEMAP))
105             continue;
106 
107         gl_type = resource_types[i].gl_type;
108         if (base_type == WINED3D_GL_RES_TYPE_COUNT)
109             base_type = gl_type;
110 
111         if ((usage & WINED3DUSAGE_RENDERTARGET) && !(format->flags[gl_type] & WINED3DFMT_FLAG_RENDERTARGET))
112         {
113             WARN("Format %s cannot be used for render targets.\n", debug_d3dformat(format->id));
114             continue;
115         }
116         if ((usage & WINED3DUSAGE_DEPTHSTENCIL)
117                 && !(format->flags[gl_type] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
118         {
119             WARN("Format %s cannot be used for depth/stencil buffers.\n", debug_d3dformat(format->id));
120             continue;
121         }
122         if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
123                 && usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)
124                 && !(format->flags[gl_type] & WINED3DFMT_FLAG_FBO_ATTACHABLE))
125         {
126             WARN("Render target or depth stencil is not FBO attachable.\n");
127             continue;
128         }
129         if ((usage & WINED3DUSAGE_TEXTURE) && !(format->flags[gl_type] & WINED3DFMT_FLAG_TEXTURE))
130         {
131             WARN("Format %s cannot be used for texturing.\n", debug_d3dformat(format->id));
132             continue;
133         }
134         if (((width & (width - 1)) || (height & (height - 1)))
135                 && !gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO]
136                 && !gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
137                 && gl_type == WINED3D_GL_RES_TYPE_TEX_2D)
138         {
139             TRACE("Skipping 2D texture type to try texture rectangle.\n");
140             tex_2d_ok = TRUE;
141             continue;
142         }
143         break;
144     }
145 
146     if (base_type != WINED3D_GL_RES_TYPE_COUNT && i == ARRAY_SIZE(resource_types))
147     {
148         if (tex_2d_ok)
149         {
150             /* Non power of 2 texture and rectangle textures or renderbuffers do not work.
151              * Use 2D textures, the texture code will pad to a power of 2 size. */
152             gl_type = WINED3D_GL_RES_TYPE_TEX_2D;
153         }
154         else if (usage & WINED3DUSAGE_SCRATCH)
155         {
156             /* Needed for proper format information. */
157             gl_type = base_type;
158         }
159         else
160         {
161             WARN("Did not find a suitable GL resource type for resource type %s.\n",
162                     debug_d3dresourcetype(type));
163             return WINED3DERR_INVALIDCALL;
164         }
165     }
166 
167     if (base_type != WINED3D_GL_RES_TYPE_COUNT
168             && (format->flags[base_type] & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BLOCKS_NO_VERIFY))
169             == WINED3DFMT_FLAG_BLOCKS)
170     {
171         UINT width_mask = format->block_width - 1;
172         UINT height_mask = format->block_height - 1;
173         if (width & width_mask || height & height_mask)
174             return WINED3DERR_INVALIDCALL;
175     }
176 
177     resource->ref = 1;
178     resource->device = device;
179     resource->type = type;
180     resource->gl_type = gl_type;
181     resource->format = format;
182     if (gl_type < WINED3D_GL_RES_TYPE_COUNT)
183         resource->format_flags = format->flags[gl_type];
184     resource->multisample_type = multisample_type;
185     resource->multisample_quality = multisample_quality;
186     resource->usage = usage;
187     resource->access = access;
188     resource->width = width;
189     resource->height = height;
190     resource->depth = depth;
191     resource->size = size;
192     resource->priority = 0;
193     resource->parent = parent;
194     resource->parent_ops = parent_ops;
195     resource->resource_ops = resource_ops;
196     resource->map_binding = WINED3D_LOCATION_SYSMEM;
197     resource->heap_memory = NULL;
198 
199     if (!(usage & WINED3DUSAGE_PRIVATE))
200     {
201         /* Check that we have enough video ram left */
202         if (!(access & WINED3D_RESOURCE_ACCESS_CPU) && device->wined3d->flags & WINED3D_VIDMEM_ACCOUNTING)
203         {
204             if (size > wined3d_device_get_available_texture_mem(device))
205             {
206                 ERR("Out of adapter memory.\n");
207                 return WINED3DERR_OUTOFVIDEOMEMORY;
208             }
209             adapter_adjust_memory(device->adapter, size);
210         }
211 
212         device_resource_add(device, resource);
213     }
214 
215     return WINED3D_OK;
216 }
217 
218 static void wined3d_resource_destroy_object(void *object)
219 {
220     struct wined3d_resource *resource = object;
221 
222     wined3d_resource_free_sysmem(resource);
223     context_resource_released(resource->device, resource);
224     wined3d_resource_release(resource);
225 }
226 
227 void resource_cleanup(struct wined3d_resource *resource)
228 {
229     const struct wined3d *d3d = resource->device->wined3d;
230 
231     TRACE("Cleaning up resource %p.\n", resource);
232 
233     if (!(resource->usage & WINED3DUSAGE_PRIVATE))
234     {
235         if (!(resource->access & WINED3D_RESOURCE_ACCESS_CPU) && d3d->flags & WINED3D_VIDMEM_ACCOUNTING)
236         {
237             TRACE("Decrementing device memory pool by %u.\n", resource->size);
238             adapter_adjust_memory(resource->device->adapter, (INT64)0 - resource->size);
239         }
240 
241         device_resource_released(resource->device, resource);
242     }
243     wined3d_resource_acquire(resource);
244     wined3d_cs_destroy_object(resource->device->cs, wined3d_resource_destroy_object, resource);
245 }
246 
247 void resource_unload(struct wined3d_resource *resource)
248 {
249     if (resource->map_count)
250         ERR("Resource %p is being unloaded while mapped.\n", resource);
251 }
252 
253 DWORD CDECL wined3d_resource_set_priority(struct wined3d_resource *resource, DWORD priority)
254 {
255     DWORD prev;
256 
257     if (!wined3d_resource_access_is_managed(resource->access))
258     {
259         WARN("Called on non-managed resource %p, ignoring.\n", resource);
260         return 0;
261     }
262 
263     prev = resource->priority;
264     resource->priority = priority;
265     TRACE("resource %p, new priority %u, returning old priority %u.\n", resource, priority, prev);
266     return prev;
267 }
268 
269 DWORD CDECL wined3d_resource_get_priority(const struct wined3d_resource *resource)
270 {
271     TRACE("resource %p, returning %u.\n", resource, resource->priority);
272     return resource->priority;
273 }
274 
275 void * CDECL wined3d_resource_get_parent(const struct wined3d_resource *resource)
276 {
277     return resource->parent;
278 }
279 
280 void CDECL wined3d_resource_set_parent(struct wined3d_resource *resource, void *parent)
281 {
282     resource->parent = parent;
283 }
284 
285 void CDECL wined3d_resource_get_desc(const struct wined3d_resource *resource, struct wined3d_resource_desc *desc)
286 {
287     desc->resource_type = resource->type;
288     desc->format = resource->format->id;
289     desc->multisample_type = resource->multisample_type;
290     desc->multisample_quality = resource->multisample_quality;
291     desc->usage = resource->usage;
292     desc->access = resource->access;
293     desc->width = resource->width;
294     desc->height = resource->height;
295     desc->depth = resource->depth;
296     desc->size = resource->size;
297 }
298 
299 static DWORD wined3d_resource_sanitise_map_flags(const struct wined3d_resource *resource, DWORD flags)
300 {
301     /* Not all flags make sense together, but Windows never returns an error.
302      * Catch the cases that could cause issues. */
303     if (flags & WINED3D_MAP_READ)
304     {
305         if (flags & WINED3D_MAP_DISCARD)
306         {
307             WARN("WINED3D_MAP_READ combined with WINED3D_MAP_DISCARD, ignoring flags.\n");
308             return flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE);
309         }
310         if (flags & WINED3D_MAP_NOOVERWRITE)
311         {
312             WARN("WINED3D_MAP_READ combined with WINED3D_MAP_NOOVERWRITE, ignoring flags.\n");
313             return flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE);
314         }
315     }
316     else if (flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE))
317     {
318         if (!(resource->usage & WINED3DUSAGE_DYNAMIC))
319         {
320             WARN("DISCARD or NOOVERWRITE map on non-dynamic buffer, ignoring.\n");
321             return flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE);
322         }
323         if ((flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE))
324                 == (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE))
325         {
326             WARN("WINED3D_MAP_NOOVERWRITE used with WINED3D_MAP_DISCARD, ignoring WINED3D_MAP_DISCARD.\n");
327             flags &= ~WINED3D_MAP_DISCARD;
328         }
329     }
330 
331     return flags;
332 }
333 
334 HRESULT CDECL wined3d_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
335         struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
336 {
337     TRACE("resource %p, sub_resource_idx %u, map_desc %p, box %s, flags %#x.\n",
338             resource, sub_resource_idx, map_desc, debug_box(box), flags);
339 
340     if (!(flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE)))
341     {
342         WARN("No read/write flags specified.\n");
343         return E_INVALIDARG;
344     }
345 
346     if ((flags & WINED3D_MAP_READ) && !(resource->access & WINED3D_RESOURCE_ACCESS_MAP_R))
347     {
348         WARN("Resource does not have MAP_R access.\n");
349         return E_INVALIDARG;
350     }
351 
352     if ((flags & WINED3D_MAP_WRITE) && !(resource->access & WINED3D_RESOURCE_ACCESS_MAP_W))
353     {
354         WARN("Resource does not have MAP_W access.\n");
355         return E_INVALIDARG;
356     }
357 
358     flags = wined3d_resource_sanitise_map_flags(resource, flags);
359     wined3d_resource_wait_idle(resource);
360 
361     return wined3d_cs_map(resource->device->cs, resource, sub_resource_idx, map_desc, box, flags);
362 }
363 
364 HRESULT CDECL wined3d_resource_map_info(struct wined3d_resource *resource, unsigned int sub_resource_idx,
365         struct wined3d_map_info *info, DWORD flags)
366 {
367     TRACE("resource %p, sub_resource_idx %u.\n", resource, sub_resource_idx);
368 
369     return resource->resource_ops->resource_map_info(resource, sub_resource_idx, info, flags);
370 }
371 
372 HRESULT CDECL wined3d_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
373 {
374     TRACE("resource %p, sub_resource_idx %u.\n", resource, sub_resource_idx);
375 
376     return wined3d_cs_unmap(resource->device->cs, resource, sub_resource_idx);
377 }
378 
379 UINT CDECL wined3d_resource_update_info(struct wined3d_resource *resource, unsigned int sub_resource_idx,
380         const struct wined3d_box *box, unsigned int row_pitch, unsigned int depth_pitch)
381 {
382     unsigned int width, height, depth;
383     struct wined3d_box b;
384     UINT data_size;
385 
386     TRACE("resource %p, sub_resource_idx %u, box %s, row_pitch %u, depth_pitch %u.\n",
387             resource, sub_resource_idx, debug_box(box), row_pitch, depth_pitch);
388 
389     if (resource->type == WINED3D_RTYPE_BUFFER)
390     {
391         if (sub_resource_idx > 0)
392         {
393             WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx);
394             return 0;
395         }
396 
397         width = resource->size;
398         height = 1;
399         depth = 1;
400     }
401     else if (resource->type == WINED3D_RTYPE_TEXTURE_1D ||
402             resource->type == WINED3D_RTYPE_TEXTURE_2D || resource->type == WINED3D_RTYPE_TEXTURE_3D)
403     {
404         struct wined3d_texture *texture = texture_from_resource(resource);
405         unsigned int level;
406 
407         if (sub_resource_idx >= texture->level_count * texture->layer_count)
408         {
409             WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx);
410             return 0;
411         }
412 
413         level = sub_resource_idx % texture->level_count;
414         width = wined3d_texture_get_level_width(texture, level);
415         height = wined3d_texture_get_level_height(texture, level);
416         depth = wined3d_texture_get_level_depth(texture, level);
417     }
418     else
419     {
420         FIXME("Not implemented for %s resources.\n", debug_d3dresourcetype(resource->type));
421         return 0;
422     }
423 
424     if (!box)
425     {
426         wined3d_box_set(&b, 0, 0, width, height, 0, depth);
427         box = &b;
428     }
429     else if (box->left >= box->right || box->right > width
430             || box->top >= box->bottom || box->bottom > height
431             || box->front >= box->back || box->back > depth)
432     {
433         WARN("Invalid box %s specified.\n", debug_box(box));
434         return 0;
435     }
436 
437     if (resource->format_flags & WINED3DFMT_FLAG_BLOCKS)
438     {
439         if (resource->type != WINED3D_RTYPE_TEXTURE_2D)
440         {
441             FIXME("Calculation of block formats not implemented for %s resources.\n", debug_d3dresourcetype(resource->type));
442             return 0;
443         }
444 
445         height  = (box->bottom - box->top  + resource->format->block_height - 1) / resource->format->block_height;
446         width   = (box->right  - box->left + resource->format->block_width  - 1) / resource->format->block_width;
447         return (height - 1) * row_pitch + width * resource->format->block_byte_count;
448     }
449 
450     data_size = 0;
451     switch (resource->type)
452     {
453         case WINED3D_RTYPE_TEXTURE_3D:
454             data_size += (box->back - box->front - 1) * depth_pitch;
455             /* fall-through */
456         case WINED3D_RTYPE_TEXTURE_2D:
457             data_size += (box->bottom - box->top - 1) * row_pitch;
458             /* fall-through */
459         case WINED3D_RTYPE_TEXTURE_1D:
460             data_size += (box->right - box->left) * resource->format->byte_count;
461             break;
462         case WINED3D_RTYPE_BUFFER:
463             data_size = box->right - box->left;
464             break;
465         case WINED3D_RTYPE_NONE:
466             break;
467     }
468 
469     return data_size;
470 }
471 
472 void CDECL wined3d_resource_preload(struct wined3d_resource *resource)
473 {
474     wined3d_cs_emit_preload_resource(resource->device->cs, resource);
475 }
476 
477 BOOL wined3d_resource_allocate_sysmem(struct wined3d_resource *resource)
478 {
479     void **p;
480     SIZE_T align = RESOURCE_ALIGNMENT - 1 + sizeof(*p);
481     void *mem;
482 
483     if (!(mem = heap_alloc_zero(resource->size + align)))
484     {
485         ERR("Failed to allocate system memory.\n");
486         return FALSE;
487     }
488 
489     p = (void **)(((ULONG_PTR)mem + align) & ~(RESOURCE_ALIGNMENT - 1)) - 1;
490     *p = mem;
491 
492     resource->heap_memory = ++p;
493 
494     return TRUE;
495 }
496 
497 void wined3d_resource_free_sysmem(struct wined3d_resource *resource)
498 {
499     void **p = resource->heap_memory;
500 
501     if (!p)
502         return;
503 
504     heap_free(*(--p));
505     resource->heap_memory = NULL;
506 }
507 
508 GLbitfield wined3d_resource_gl_map_flags(DWORD d3d_flags)
509 {
510     GLbitfield ret = 0;
511 
512     if (d3d_flags & WINED3D_MAP_WRITE)
513         ret |= GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
514     if (d3d_flags & WINED3D_MAP_READ)
515         ret |= GL_MAP_READ_BIT;
516 
517     if (d3d_flags & WINED3D_MAP_DISCARD)
518         ret |= GL_MAP_INVALIDATE_BUFFER_BIT;
519     if (d3d_flags & WINED3D_MAP_NOOVERWRITE)
520         ret |= GL_MAP_UNSYNCHRONIZED_BIT;
521 
522     return ret;
523 }
524 
525 GLenum wined3d_resource_gl_legacy_map_flags(DWORD d3d_flags)
526 {
527     switch (d3d_flags & (WINED3D_MAP_READ | WINED3D_MAP_WRITE))
528     {
529         case WINED3D_MAP_READ:
530             return GL_READ_ONLY_ARB;
531 
532         case WINED3D_MAP_WRITE:
533             return GL_WRITE_ONLY_ARB;
534 
535         default:
536             return GL_READ_WRITE_ARB;
537     }
538 }
539 
540 BOOL wined3d_resource_is_offscreen(struct wined3d_resource *resource)
541 {
542     struct wined3d_swapchain *swapchain;
543 
544     /* Only 2D texture resources can be onscreen. */
545     if (resource->type != WINED3D_RTYPE_TEXTURE_2D)
546         return TRUE;
547 
548     /* Not on a swapchain - must be offscreen */
549     if (!(swapchain = texture_from_resource(resource)->swapchain))
550         return TRUE;
551 
552     /* The front buffer is always onscreen */
553     if (resource == &swapchain->front_buffer->resource)
554         return FALSE;
555 
556     /* If the swapchain is rendered to an FBO, the backbuffer is
557      * offscreen, otherwise onscreen */
558     return swapchain->render_to_fbo;
559 }
560 
561 void wined3d_resource_update_draw_binding(struct wined3d_resource *resource)
562 {
563     if (!wined3d_resource_is_offscreen(resource) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
564     {
565         resource->draw_binding = WINED3D_LOCATION_DRAWABLE;
566     }
567     else if (resource->multisample_type)
568     {
569         const struct wined3d_gl_info *gl_info = &resource->device->adapter->gl_info;
570         if (gl_info->supported[ARB_TEXTURE_MULTISAMPLE])
571             resource->draw_binding = WINED3D_LOCATION_TEXTURE_RGB;
572         else
573             resource->draw_binding = WINED3D_LOCATION_RB_MULTISAMPLE;
574     }
575     else if (resource->gl_type == WINED3D_GL_RES_TYPE_RB)
576     {
577         resource->draw_binding = WINED3D_LOCATION_RB_RESOLVED;
578     }
579     else
580     {
581         resource->draw_binding = WINED3D_LOCATION_TEXTURE_RGB;
582     }
583 }
584