2 * Copyright 2002-2005 Jason Edmeades
3 * Copyright 2002-2005 Raphael Junqueira
4 * Copyright 2004 Christian Costa
5 * Copyright 2005 Oliver Stieber
6 * Copyright 2007-2011, 2013-2014 Stefan Dösinger for CodeWeavers
7 * Copyright 2009-2010 Henri Verbeet for CodeWeavers
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.
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.
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
25 #include "wined3d_private.h"
26 #include "wined3d_gl.h"
27 #include "wined3d_vk.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(d3d
);
31 #define WINED3D_BUFFER_HASDESC 0x01 /* A vertex description has been found. */
32 #define WINED3D_BUFFER_USE_BO 0x02 /* Use a buffer object for this buffer. */
34 #define SB_MIN_SIZE (512 * 1024) /* Minimum size of an allocated streaming buffer. */
36 struct wined3d_buffer_ops
38 BOOL (*buffer_prepare_location
)(struct wined3d_buffer
*buffer
,
39 struct wined3d_context
*context
, unsigned int location
);
40 void (*buffer_unload_location
)(struct wined3d_buffer
*buffer
,
41 struct wined3d_context
*context
, unsigned int location
);
44 static void wined3d_buffer_evict_sysmem(struct wined3d_buffer
*buffer
)
46 if (buffer
->resource
.pin_sysmem
)
48 TRACE("Not evicting system memory for buffer %p.\n", buffer
);
52 TRACE("Evicting system memory for buffer %p.\n", buffer
);
53 wined3d_buffer_invalidate_location(buffer
, WINED3D_LOCATION_SYSMEM
);
54 wined3d_resource_free_sysmem(&buffer
->resource
);
57 static void buffer_invalidate_bo_range(struct wined3d_buffer
*buffer
, unsigned int offset
, unsigned int size
)
59 if (!offset
&& (!size
|| size
== buffer
->resource
.size
))
62 if (offset
> buffer
->resource
.size
|| size
> buffer
->resource
.size
- offset
)
64 WARN("Invalid range specified, invalidating entire buffer.\n");
68 if (!wined3d_array_reserve((void **)&buffer
->dirty_ranges
, &buffer
->dirty_ranges_capacity
,
69 buffer
->dirty_range_count
+ 1, sizeof(*buffer
->dirty_ranges
)))
71 ERR("Failed to allocate dirty ranges array, invalidating entire buffer.\n");
75 buffer
->dirty_ranges
[buffer
->dirty_range_count
].offset
= offset
;
76 buffer
->dirty_ranges
[buffer
->dirty_range_count
].size
= size
;
77 ++buffer
->dirty_range_count
;
81 buffer
->dirty_range_count
= 1;
82 buffer
->dirty_ranges
[0].offset
= 0;
83 buffer
->dirty_ranges
[0].size
= buffer
->resource
.size
;
86 static inline void buffer_clear_dirty_areas(struct wined3d_buffer
*buffer
)
88 buffer
->dirty_range_count
= 0;
91 static BOOL
buffer_is_dirty(const struct wined3d_buffer
*buffer
)
93 return !!buffer
->dirty_range_count
;
96 static BOOL
buffer_is_fully_dirty(const struct wined3d_buffer
*buffer
)
98 return buffer
->dirty_range_count
== 1
99 && !buffer
->dirty_ranges
[0].offset
&& buffer
->dirty_ranges
[0].size
== buffer
->resource
.size
;
102 void wined3d_buffer_validate_location(struct wined3d_buffer
*buffer
, uint32_t location
)
104 TRACE("buffer %p, location %s.\n", buffer
, wined3d_debug_location(location
));
106 if (location
& WINED3D_LOCATION_BUFFER
)
107 buffer_clear_dirty_areas(buffer
);
109 buffer
->locations
|= location
;
111 TRACE("New locations flags are %s.\n", wined3d_debug_location(buffer
->locations
));
114 static void wined3d_buffer_invalidate_range(struct wined3d_buffer
*buffer
, DWORD location
,
115 unsigned int offset
, unsigned int size
)
117 TRACE("buffer %p, location %s, offset %u, size %u.\n",
118 buffer
, wined3d_debug_location(location
), offset
, size
);
120 if ((location
& WINED3D_LOCATION_BUFFER
) && (buffer
->flags
& WINED3D_BUFFER_USE_BO
))
121 buffer_invalidate_bo_range(buffer
, offset
, size
);
123 buffer
->locations
&= ~location
;
125 TRACE("New locations flags are %s.\n", wined3d_debug_location(buffer
->locations
));
127 if (!buffer
->locations
)
128 ERR("Buffer %p does not have any up to date location.\n", buffer
);
131 void wined3d_buffer_invalidate_location(struct wined3d_buffer
*buffer
, uint32_t location
)
133 wined3d_buffer_invalidate_range(buffer
, location
, 0, 0);
136 GLenum
wined3d_buffer_gl_binding_from_bind_flags(const struct wined3d_gl_info
*gl_info
, uint32_t bind_flags
)
139 return GL_PIXEL_UNPACK_BUFFER
;
141 /* We must always return GL_ELEMENT_ARRAY_BUFFER here;
142 * wined3d_device_gl_create_bo() checks the GL binding to see whether we
143 * can suballocate, and we cannot suballocate if this BO might be used for
144 * an index buffer. */
145 if (bind_flags
& WINED3D_BIND_INDEX_BUFFER
)
146 return GL_ELEMENT_ARRAY_BUFFER
;
148 if (bind_flags
& (WINED3D_BIND_SHADER_RESOURCE
| WINED3D_BIND_UNORDERED_ACCESS
)
149 && gl_info
->supported
[ARB_TEXTURE_BUFFER_OBJECT
])
150 return GL_TEXTURE_BUFFER
;
152 if (bind_flags
& WINED3D_BIND_CONSTANT_BUFFER
)
153 return GL_UNIFORM_BUFFER
;
155 if (bind_flags
& WINED3D_BIND_STREAM_OUTPUT
)
156 return GL_TRANSFORM_FEEDBACK_BUFFER
;
158 if (bind_flags
& WINED3D_BIND_INDIRECT_BUFFER
159 && gl_info
->supported
[ARB_DRAW_INDIRECT
])
160 return GL_DRAW_INDIRECT_BUFFER
;
162 if (bind_flags
& ~(WINED3D_BIND_VERTEX_BUFFER
| WINED3D_BIND_INDEX_BUFFER
))
163 FIXME("Unhandled bind flags %#x.\n", bind_flags
);
165 return GL_ARRAY_BUFFER
;
168 /* Context activation is done by the caller. */
169 static void wined3d_buffer_gl_destroy_buffer_object(struct wined3d_buffer_gl
*buffer_gl
,
170 struct wined3d_context_gl
*context_gl
)
172 struct wined3d_resource
*resource
= &buffer_gl
->b
.resource
;
173 struct wined3d_bo_gl
*bo_gl
;
175 if (!buffer_gl
->b
.buffer_object
)
177 bo_gl
= wined3d_bo_gl(buffer_gl
->b
.buffer_object
);
179 if (context_gl
->c
.transform_feedback_active
&& (resource
->bind_flags
& WINED3D_BIND_STREAM_OUTPUT
)
180 && wined3d_context_is_graphics_state_dirty(&context_gl
->c
, STATE_STREAM_OUTPUT
))
182 /* It's illegal to (un)bind GL_TRANSFORM_FEEDBACK_BUFFER while transform
183 * feedback is active. Deleting a buffer implicitly unbinds it, so we
184 * need to end transform feedback here if this buffer was bound.
186 * This should only be possible if STATE_STREAM_OUTPUT is dirty; if we
187 * do a draw call before destroying this buffer then the draw call will
188 * already rebind the GL target. */
189 WARN("Deleting buffer object for buffer %p, disabling transform feedback.\n", buffer_gl
);
190 wined3d_context_gl_end_transform_feedback(context_gl
);
193 if (buffer_gl
->b
.bo_user
.valid
)
195 buffer_gl
->b
.bo_user
.valid
= false;
196 list_remove(&buffer_gl
->b
.bo_user
.entry
);
198 if (!--bo_gl
->b
.refcount
)
200 wined3d_context_gl_destroy_bo(context_gl
, bo_gl
);
203 buffer_gl
->b
.buffer_object
= NULL
;
206 /* Context activation is done by the caller. */
207 static BOOL
wined3d_buffer_gl_create_buffer_object(struct wined3d_buffer_gl
*buffer_gl
,
208 struct wined3d_context_gl
*context_gl
)
210 struct wined3d_device_gl
*device_gl
= wined3d_device_gl(buffer_gl
->b
.resource
.device
);
211 const struct wined3d_gl_info
*gl_info
= context_gl
->gl_info
;
212 GLenum usage
= GL_STATIC_DRAW
;
213 GLbitfield gl_storage_flags
;
214 struct wined3d_bo_gl
*bo
;
215 bool coherent
= true;
219 TRACE("Creating an OpenGL buffer object for wined3d buffer %p with usage %s.\n",
220 buffer_gl
, debug_d3dusage(buffer_gl
->b
.resource
.usage
));
222 if (!(bo
= malloc(sizeof(*bo
))))
225 size
= buffer_gl
->b
.resource
.size
;
226 binding
= wined3d_buffer_gl_binding_from_bind_flags(gl_info
, buffer_gl
->b
.resource
.bind_flags
);
227 if (buffer_gl
->b
.resource
.usage
& WINED3DUSAGE_DYNAMIC
)
229 usage
= GL_STREAM_DRAW_ARB
;
232 gl_storage_flags
= wined3d_resource_gl_storage_flags(&buffer_gl
->b
.resource
);
233 if (!wined3d_device_gl_create_bo(device_gl
, context_gl
, size
, binding
, usage
, coherent
, gl_storage_flags
, bo
))
235 ERR("Failed to create OpenGL buffer object.\n");
236 buffer_gl
->b
.flags
&= ~WINED3D_BUFFER_USE_BO
;
237 buffer_clear_dirty_areas(&buffer_gl
->b
);
242 buffer_gl
->b
.buffer_object
= &bo
->b
;
243 buffer_invalidate_bo_range(&buffer_gl
->b
, 0, 0);
248 ULONG CDECL
wined3d_buffer_incref(struct wined3d_buffer
*buffer
)
250 unsigned int refcount
= InterlockedIncrement(&buffer
->resource
.ref
);
252 TRACE("%p increasing refcount to %u.\n", buffer
, refcount
);
257 BOOL
wined3d_buffer_prepare_location(struct wined3d_buffer
*buffer
,
258 struct wined3d_context
*context
, unsigned int location
)
260 return buffer
->buffer_ops
->buffer_prepare_location(buffer
, context
, location
);
263 static void wined3d_buffer_unload_location(struct wined3d_buffer
*buffer
,
264 struct wined3d_context
*context
, unsigned int location
)
266 buffer
->buffer_ops
->buffer_unload_location(buffer
, context
, location
);
269 BOOL
wined3d_buffer_load_location(struct wined3d_buffer
*buffer
,
270 struct wined3d_context
*context
, uint32_t location
)
272 struct wined3d_bo_address src
, dst
;
273 struct wined3d_range range
;
275 TRACE("buffer %p, context %p, location %s.\n",
276 buffer
, context
, wined3d_debug_location(location
));
278 if (buffer
->locations
& location
)
280 TRACE("Location (%#x) is already up to date.\n", location
);
284 if (!buffer
->locations
)
286 ERR("Buffer %p does not have any up to date location.\n", buffer
);
287 wined3d_buffer_validate_location(buffer
, WINED3D_LOCATION_DISCARDED
);
288 return wined3d_buffer_load_location(buffer
, context
, location
);
291 TRACE("Current buffer location %s.\n", wined3d_debug_location(buffer
->locations
));
293 if (!wined3d_buffer_prepare_location(buffer
, context
, location
))
296 if (buffer
->locations
& WINED3D_LOCATION_DISCARDED
)
298 TRACE("Buffer previously discarded, nothing to do.\n");
299 wined3d_buffer_validate_location(buffer
, location
);
300 wined3d_buffer_invalidate_location(buffer
, WINED3D_LOCATION_DISCARDED
);
306 case WINED3D_LOCATION_SYSMEM
:
307 if (buffer
->locations
& WINED3D_LOCATION_CLEARED
)
309 memset(buffer
->resource
.heap_memory
, 0, buffer
->resource
.size
);
313 dst
.buffer_object
= NULL
;
314 dst
.addr
= buffer
->resource
.heap_memory
;
315 src
.buffer_object
= buffer
->buffer_object
;
318 range
.size
= buffer
->resource
.size
;
319 wined3d_context_copy_bo_address(context
, &dst
, &src
, 1, &range
, WINED3D_MAP_WRITE
);
323 case WINED3D_LOCATION_BUFFER
:
325 uint32_t map_flags
= WINED3D_MAP_WRITE
;
327 if (buffer
->locations
& WINED3D_LOCATION_CLEARED
)
329 /* FIXME: Clear the buffer on the GPU if possible. */
330 if (!wined3d_buffer_prepare_location(buffer
, context
, WINED3D_LOCATION_SYSMEM
))
332 memset(buffer
->resource
.heap_memory
, 0, buffer
->resource
.size
);
335 dst
.buffer_object
= buffer
->buffer_object
;
337 src
.buffer_object
= NULL
;
338 src
.addr
= buffer
->resource
.heap_memory
;
340 if (buffer_is_fully_dirty(buffer
))
341 map_flags
|= WINED3D_MAP_DISCARD
;
343 wined3d_context_copy_bo_address(context
, &dst
, &src
,
344 buffer
->dirty_range_count
, buffer
->dirty_ranges
, map_flags
);
349 ERR("Invalid location %s.\n", wined3d_debug_location(location
));
353 wined3d_buffer_validate_location(buffer
, location
);
354 if (buffer
->resource
.heap_memory
&& location
== WINED3D_LOCATION_BUFFER
355 && !(buffer
->resource
.usage
& WINED3DUSAGE_DYNAMIC
))
356 wined3d_buffer_evict_sysmem(buffer
);
361 /* Context activation is done by the caller. */
362 void *wined3d_buffer_load_sysmem(struct wined3d_buffer
*buffer
, struct wined3d_context
*context
)
364 if (wined3d_buffer_load_location(buffer
, context
, WINED3D_LOCATION_SYSMEM
))
365 buffer
->resource
.pin_sysmem
= 1;
366 return buffer
->resource
.heap_memory
;
369 DWORD
wined3d_buffer_get_memory(struct wined3d_buffer
*buffer
, struct wined3d_context
*context
,
370 struct wined3d_bo_address
*data
)
372 unsigned int locations
= buffer
->locations
;
374 TRACE("buffer %p, context %p, data %p, locations %s.\n",
375 buffer
, context
, data
, wined3d_debug_location(locations
));
377 if (locations
& (WINED3D_LOCATION_DISCARDED
| WINED3D_LOCATION_CLEARED
))
379 locations
= ((buffer
->flags
& WINED3D_BUFFER_USE_BO
) ? WINED3D_LOCATION_BUFFER
: WINED3D_LOCATION_SYSMEM
);
380 if (!wined3d_buffer_load_location(buffer
, context
, locations
))
382 data
->buffer_object
= 0;
387 if (locations
& WINED3D_LOCATION_BUFFER
)
389 data
->buffer_object
= buffer
->buffer_object
;
391 return WINED3D_LOCATION_BUFFER
;
393 if (locations
& WINED3D_LOCATION_SYSMEM
)
395 data
->buffer_object
= 0;
396 data
->addr
= buffer
->resource
.heap_memory
;
397 return WINED3D_LOCATION_SYSMEM
;
400 ERR("Unexpected locations %s.\n", wined3d_debug_location(locations
));
401 data
->buffer_object
= 0;
406 static void buffer_resource_unload(struct wined3d_resource
*resource
)
408 struct wined3d_buffer
*buffer
= buffer_from_resource(resource
);
410 TRACE("buffer %p.\n", buffer
);
412 if (buffer
->buffer_object
)
414 struct wined3d_context
*context
;
416 context
= context_acquire(resource
->device
, NULL
, 0);
418 wined3d_buffer_load_location(buffer
, context
, WINED3D_LOCATION_SYSMEM
);
419 wined3d_buffer_invalidate_location(buffer
, WINED3D_LOCATION_BUFFER
);
420 wined3d_buffer_unload_location(buffer
, context
, WINED3D_LOCATION_BUFFER
);
421 buffer_clear_dirty_areas(buffer
);
423 context_release(context
);
425 buffer
->flags
&= ~WINED3D_BUFFER_HASDESC
;
428 resource_unload(resource
);
431 static void wined3d_buffer_drop_bo(struct wined3d_buffer
*buffer
)
433 buffer
->flags
&= ~WINED3D_BUFFER_USE_BO
;
434 buffer_resource_unload(&buffer
->resource
);
437 static void wined3d_buffer_destroy_object(void *object
)
439 struct wined3d_buffer
*buffer
= object
;
440 struct wined3d_context
*context
;
442 TRACE("buffer %p.\n", buffer
);
444 if (buffer
->buffer_object
)
446 context
= context_acquire(buffer
->resource
.device
, NULL
, 0);
447 wined3d_buffer_unload_location(buffer
, context
, WINED3D_LOCATION_BUFFER
);
448 context_release(context
);
450 free(buffer
->dirty_ranges
);
453 void wined3d_buffer_cleanup(struct wined3d_buffer
*buffer
)
455 wined3d_cs_destroy_object(buffer
->resource
.device
->cs
, wined3d_buffer_destroy_object
, buffer
);
456 resource_cleanup(&buffer
->resource
);
459 ULONG CDECL
wined3d_buffer_decref(struct wined3d_buffer
*buffer
)
461 unsigned int refcount
= InterlockedDecrement(&buffer
->resource
.ref
);
463 TRACE("%p decreasing refcount to %u.\n", buffer
, refcount
);
467 wined3d_mutex_lock();
468 buffer
->resource
.parent_ops
->wined3d_object_destroyed(buffer
->resource
.parent
);
469 buffer
->resource
.device
->adapter
->adapter_ops
->adapter_destroy_buffer(buffer
);
470 wined3d_mutex_unlock();
476 void * CDECL
wined3d_buffer_get_parent(const struct wined3d_buffer
*buffer
)
478 TRACE("buffer %p.\n", buffer
);
480 return buffer
->resource
.parent
;
483 /* Context activation is done by the caller. */
484 void wined3d_buffer_load(struct wined3d_buffer
*buffer
, struct wined3d_context
*context
,
485 const struct wined3d_state
*state
)
487 TRACE("buffer %p.\n", buffer
);
489 if (buffer
->resource
.map_count
&& buffer
->map_ptr
)
491 FIXME("Buffer is mapped through buffer object, not loading.\n");
494 else if (buffer
->resource
.map_count
)
496 WARN("Loading mapped buffer.\n");
499 if (!(buffer
->flags
& WINED3D_BUFFER_USE_BO
))
502 if (!wined3d_buffer_prepare_location(buffer
, context
, WINED3D_LOCATION_BUFFER
))
504 ERR("Failed to prepare buffer location.\n");
508 /* Reading the declaration makes only sense if we have valid state information
509 * (i.e., if this function is called during draws). */
511 buffer
->flags
|= WINED3D_BUFFER_HASDESC
;
513 if (!(buffer
->flags
& WINED3D_BUFFER_HASDESC
&& buffer_is_dirty(buffer
)))
516 if (!wined3d_buffer_load_location(buffer
, context
, WINED3D_LOCATION_BUFFER
))
517 ERR("Failed to load buffer location.\n");
520 struct wined3d_resource
* CDECL
wined3d_buffer_get_resource(struct wined3d_buffer
*buffer
)
522 TRACE("buffer %p.\n", buffer
);
524 return &buffer
->resource
;
527 static HRESULT
buffer_resource_sub_resource_get_desc(struct wined3d_resource
*resource
,
528 unsigned int sub_resource_idx
, struct wined3d_sub_resource_desc
*desc
)
530 if (sub_resource_idx
)
532 WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx
);
536 desc
->format
= WINED3DFMT_R8_UNORM
;
537 desc
->multisample_type
= WINED3D_MULTISAMPLE_NONE
;
538 desc
->multisample_quality
= 0;
539 desc
->usage
= resource
->usage
;
540 desc
->bind_flags
= resource
->bind_flags
;
541 desc
->access
= resource
->access
;
542 desc
->width
= resource
->size
;
545 desc
->size
= resource
->size
;
549 static void buffer_resource_sub_resource_get_map_pitch(struct wined3d_resource
*resource
,
550 unsigned int sub_resource_idx
, unsigned int *row_pitch
, unsigned int *slice_pitch
)
552 *row_pitch
= *slice_pitch
= resource
->size
;
555 static HRESULT
buffer_resource_sub_resource_map(struct wined3d_resource
*resource
, unsigned int sub_resource_idx
,
556 void **map_ptr
, const struct wined3d_box
*box
, uint32_t flags
)
558 struct wined3d_buffer
*buffer
= buffer_from_resource(resource
);
559 unsigned int offset
, size
, dirty_offset
, dirty_size
;
560 struct wined3d_device
*device
= resource
->device
;
561 struct wined3d_context
*context
;
562 struct wined3d_bo_address addr
;
566 TRACE("resource %p, sub_resource_idx %u, map_ptr %p, box %s, flags %#x.\n",
567 resource
, sub_resource_idx
, map_ptr
, debug_box(box
), flags
);
569 dirty_offset
= offset
= box
->left
;
570 dirty_size
= size
= box
->right
- box
->left
;
572 count
= ++resource
->map_count
;
574 /* DISCARD invalidates the entire buffer, regardless of the specified
575 * offset and size. Some applications also depend on the entire buffer
576 * being uploaded in that case. Two such applications are Port Royale
577 * and Darkstar One. */
578 if (flags
& WINED3D_MAP_DISCARD
)
584 if (((flags
& WINED3D_MAP_WRITE
) && !(flags
& (WINED3D_MAP_NOOVERWRITE
| WINED3D_MAP_DISCARD
)))
585 || (!(flags
& WINED3D_MAP_WRITE
) && (buffer
->locations
& WINED3D_LOCATION_SYSMEM
))
586 || buffer
->resource
.pin_sysmem
587 || !(buffer
->flags
& WINED3D_BUFFER_USE_BO
))
589 if (!(buffer
->locations
& WINED3D_LOCATION_SYSMEM
))
591 context
= context_acquire(device
, NULL
, 0);
592 wined3d_buffer_load_location(buffer
, context
, WINED3D_LOCATION_SYSMEM
);
593 context_release(context
);
596 if (flags
& WINED3D_MAP_WRITE
)
597 wined3d_buffer_invalidate_range(buffer
, ~WINED3D_LOCATION_SYSMEM
, dirty_offset
, dirty_size
);
601 context
= context_acquire(device
, NULL
, 0);
603 if (flags
& WINED3D_MAP_DISCARD
)
605 if (!wined3d_buffer_prepare_location(buffer
, context
, WINED3D_LOCATION_BUFFER
))
607 context_release(context
);
608 return E_OUTOFMEMORY
;
610 wined3d_buffer_validate_location(buffer
, WINED3D_LOCATION_BUFFER
);
614 wined3d_buffer_load_location(buffer
, context
, WINED3D_LOCATION_BUFFER
);
617 if (flags
& WINED3D_MAP_WRITE
)
619 wined3d_buffer_acquire_bo_for_write(buffer
, context
);
620 wined3d_buffer_invalidate_location(buffer
, ~WINED3D_LOCATION_BUFFER
);
621 buffer_invalidate_bo_range(buffer
, dirty_offset
, dirty_size
);
624 if ((flags
& WINED3D_MAP_DISCARD
) && resource
->heap_memory
)
625 wined3d_buffer_evict_sysmem(buffer
);
629 addr
.buffer_object
= buffer
->buffer_object
;
631 buffer
->map_ptr
= wined3d_context_map_bo_address(context
, &addr
, resource
->size
, flags
);
633 /* We are accessing buffer->resource.client from the CS thread,
634 * but it's safe because the client thread will wait for the
635 * map to return, thus completely serializing this call with
636 * other client code. */
637 if (context
->d3d_info
->persistent_map
)
638 buffer
->resource
.client
.addr
= addr
;
640 if (((DWORD_PTR
)buffer
->map_ptr
) & (RESOURCE_ALIGNMENT
- 1))
642 WARN("Pointer %p is not %u byte aligned.\n", buffer
->map_ptr
, RESOURCE_ALIGNMENT
);
644 wined3d_context_unmap_bo_address(context
, &addr
, 0, NULL
);
645 buffer
->map_ptr
= NULL
;
647 if (resource
->usage
& WINED3DUSAGE_DYNAMIC
)
649 /* The extra copy is more expensive than not using VBOs
650 * at all on the NVIDIA Linux driver, which is the
651 * only driver that returns unaligned pointers. */
652 TRACE("Dynamic buffer, dropping VBO.\n");
653 wined3d_buffer_drop_bo(buffer
);
657 TRACE("Falling back to doublebuffered operation.\n");
658 wined3d_buffer_load_location(buffer
, context
, WINED3D_LOCATION_SYSMEM
);
659 buffer
->resource
.pin_sysmem
= 1;
661 TRACE("New pointer is %p.\n", resource
->heap_memory
);
665 context_release(context
);
668 base
= buffer
->map_ptr
? buffer
->map_ptr
: resource
->heap_memory
;
669 *map_ptr
= base
+ offset
;
671 TRACE("Returning memory at %p (base %p, offset %u).\n", *map_ptr
, base
, offset
);
676 static HRESULT
buffer_resource_sub_resource_unmap(struct wined3d_resource
*resource
, unsigned int sub_resource_idx
)
678 struct wined3d_buffer
*buffer
= buffer_from_resource(resource
);
679 struct wined3d_device
*device
= resource
->device
;
680 struct wined3d_context
*context
;
681 struct wined3d_bo_address addr
;
683 TRACE("resource %p, sub_resource_idx %u.\n", resource
, sub_resource_idx
);
685 if (sub_resource_idx
)
687 WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx
);
691 if (!resource
->map_count
)
693 WARN("Unmap called without a previous map call.\n");
697 if (--resource
->map_count
)
699 /* Delay loading the buffer until everything is unmapped. */
700 TRACE("Ignoring unmap.\n");
704 if (!buffer
->map_ptr
)
707 context
= context_acquire(device
, NULL
, 0);
709 addr
.buffer_object
= buffer
->buffer_object
;
711 wined3d_context_unmap_bo_address(context
, &addr
, buffer
->dirty_range_count
, buffer
->dirty_ranges
);
713 context_release(context
);
715 buffer_clear_dirty_areas(buffer
);
716 buffer
->map_ptr
= NULL
;
721 static void wined3d_buffer_set_bo(struct wined3d_buffer
*buffer
, struct wined3d_context
*context
, struct wined3d_bo
*bo
)
723 struct wined3d_bo
*prev_bo
= buffer
->buffer_object
;
725 TRACE("buffer %p, context %p, bo %p.\n", buffer
, context
, bo
);
729 struct wined3d_bo_user
*bo_user
;
731 /* The previous BO might have users in other buffers which were valid,
732 * and should in theory remain valid. The problem is that it's not easy
733 * to tell which users belong to this buffer and which don't. We could
734 * add a field, but for now it's easier and probably fine to just
735 * invalidate every user. */
736 LIST_FOR_EACH_ENTRY(bo_user
, &prev_bo
->users
, struct wined3d_bo_user
, entry
)
737 bo_user
->valid
= false;
738 list_init(&prev_bo
->users
);
740 if (!--prev_bo
->refcount
)
742 wined3d_context_destroy_bo(context
, prev_bo
);
747 buffer
->buffer_object
= bo
;
750 void wined3d_buffer_acquire_bo_for_write(struct wined3d_buffer
*buffer
, struct wined3d_context
*context
)
752 const struct wined3d_range range
= {.size
= buffer
->resource
.size
};
753 struct wined3d_bo_address dst
, src
;
754 struct wined3d_bo
*bo
;
756 if (!(bo
= buffer
->buffer_object
))
759 /* If we are the only owner of this BO, there is nothing to do. */
760 if (bo
->refcount
== 1)
763 TRACE("Performing copy-on-write for BO %p.\n", bo
);
765 /* Grab a reference to the current BO. It's okay if this overflows, because
766 * the following unload will release it. */
769 /* Unload and re-prepare to get a new buffer. This is a bit cheap and not
770 * perfectly idiomatic—we should really just factor out an adapter-agnostic
771 * function to create a BO and then use wined3d_buffer_set_bo()—but it'll
773 wined3d_buffer_unload_location(buffer
, context
, WINED3D_LOCATION_BUFFER
);
774 wined3d_buffer_prepare_location(buffer
, context
, WINED3D_LOCATION_BUFFER
);
776 /* And finally, perform the actual copy. */
777 assert(buffer
->buffer_object
!= bo
);
778 dst
.buffer_object
= buffer
->buffer_object
;
780 src
.buffer_object
= bo
;
782 wined3d_context_copy_bo_address(context
, &dst
, &src
, 1, &range
, WINED3D_MAP_WRITE
| WINED3D_MAP_DISCARD
);
785 void wined3d_buffer_copy_bo_address(struct wined3d_buffer
*dst_buffer
, struct wined3d_context
*context
,
786 unsigned int dst_offset
, const struct wined3d_const_bo_address
*src_addr
, unsigned int size
)
788 uint32_t map_flags
= WINED3D_MAP_WRITE
;
789 struct wined3d_bo_address dst_addr
;
790 struct wined3d_range range
;
793 if (!dst_offset
&& size
== dst_buffer
->resource
.size
)
794 map_flags
|= WINED3D_MAP_DISCARD
;
796 if (map_flags
& WINED3D_MAP_DISCARD
)
797 wined3d_buffer_acquire_bo_for_write(dst_buffer
, context
);
799 dst_location
= wined3d_buffer_get_memory(dst_buffer
, context
, &dst_addr
);
800 dst_addr
.addr
+= dst_offset
;
804 wined3d_context_copy_bo_address(context
, &dst_addr
, (const struct wined3d_bo_address
*)src_addr
, 1, &range
, map_flags
);
805 wined3d_buffer_invalidate_range(dst_buffer
, ~dst_location
, dst_offset
, size
);
808 void wined3d_buffer_copy(struct wined3d_buffer
*dst_buffer
, unsigned int dst_offset
,
809 struct wined3d_buffer
*src_buffer
, unsigned int src_offset
, unsigned int size
)
811 struct wined3d_context
*context
;
812 struct wined3d_bo_address src
;
814 TRACE("dst_buffer %p, dst_offset %u, src_buffer %p, src_offset %u, size %u.\n",
815 dst_buffer
, dst_offset
, src_buffer
, src_offset
, size
);
817 context
= context_acquire(dst_buffer
->resource
.device
, NULL
, 0);
819 wined3d_buffer_get_memory(src_buffer
, context
, &src
);
820 src
.addr
+= src_offset
;
822 wined3d_buffer_copy_bo_address(dst_buffer
, context
, dst_offset
, wined3d_const_bo_address(&src
), size
);
824 context_release(context
);
827 void wined3d_buffer_update_sub_resource(struct wined3d_buffer
*buffer
, struct wined3d_context
*context
,
828 const struct upload_bo
*upload_bo
, unsigned int offset
, unsigned int size
)
830 struct wined3d_bo
*bo
= upload_bo
->addr
.buffer_object
;
831 uint32_t flags
= upload_bo
->flags
;
833 /* Try to take this buffer for COW. Don't take it if we've saturated the
835 if (!offset
&& size
== buffer
->resource
.size
836 && bo
&& bo
->refcount
< UINT8_MAX
&& !(upload_bo
->flags
& UPLOAD_BO_RENAME_ON_UNMAP
))
838 flags
|= UPLOAD_BO_RENAME_ON_UNMAP
;
842 if (flags
& UPLOAD_BO_RENAME_ON_UNMAP
)
844 /* Don't increment the refcount. UPLOAD_BO_RENAME_ON_UNMAP transfers an
845 * existing reference.
847 * FIXME: We could degenerate RENAME to a copy + free and rely on the
848 * COW logic to detect this case.
850 wined3d_buffer_set_bo(buffer
, context
, upload_bo
->addr
.buffer_object
);
851 wined3d_buffer_validate_location(buffer
, WINED3D_LOCATION_BUFFER
);
852 wined3d_buffer_invalidate_location(buffer
, ~WINED3D_LOCATION_BUFFER
);
855 if (upload_bo
->addr
.buffer_object
&& upload_bo
->addr
.buffer_object
== buffer
->buffer_object
)
857 struct wined3d_range range
;
859 /* We need to flush changes, which is implicitly done by
860 * wined3d_context_unmap_bo_address() even if we aren't actually going
863 * We would also like to free up virtual address space used by this BO
864 * if it's at a premium—note that this BO was allocated for an
865 * accelerated map. Hence we unmap the BO instead of merely flushing it;
866 * if we don't care about unmapping BOs then
867 * wined3d_context_unmap_bo_address() will flush and return.
869 range
.offset
= offset
;
871 if (upload_bo
->addr
.buffer_object
->map_ptr
)
872 wined3d_context_unmap_bo_address(context
, (const struct wined3d_bo_address
*)&upload_bo
->addr
, 1, &range
);
876 wined3d_buffer_copy_bo_address(buffer
, context
, offset
, &upload_bo
->addr
, size
);
880 static void wined3d_buffer_init_data(struct wined3d_buffer
*buffer
,
881 struct wined3d_device
*device
, const struct wined3d_sub_resource_data
*data
)
883 struct wined3d_resource
*resource
= &buffer
->resource
;
884 struct wined3d_box box
;
886 if (buffer
->flags
& WINED3D_BUFFER_USE_BO
)
888 wined3d_box_set(&box
, 0, 0, resource
->size
, 1, 0, 1);
889 wined3d_device_context_emit_update_sub_resource(&device
->cs
->c
, resource
,
890 0, &box
, data
->data
, data
->row_pitch
, data
->slice_pitch
);
894 memcpy(buffer
->resource
.heap_memory
, data
->data
, resource
->size
);
895 wined3d_buffer_validate_location(buffer
, WINED3D_LOCATION_SYSMEM
);
896 wined3d_buffer_invalidate_location(buffer
, ~WINED3D_LOCATION_SYSMEM
);
900 static ULONG
buffer_resource_incref(struct wined3d_resource
*resource
)
902 return wined3d_buffer_incref(buffer_from_resource(resource
));
905 static ULONG
buffer_resource_decref(struct wined3d_resource
*resource
)
907 return wined3d_buffer_decref(buffer_from_resource(resource
));
910 static void buffer_resource_preload(struct wined3d_resource
*resource
)
912 struct wined3d_context
*context
;
914 context
= context_acquire(resource
->device
, NULL
, 0);
915 wined3d_buffer_load(buffer_from_resource(resource
), context
, NULL
);
916 context_release(context
);
919 static const struct wined3d_resource_ops buffer_resource_ops
=
921 buffer_resource_incref
,
922 buffer_resource_decref
,
923 buffer_resource_preload
,
924 buffer_resource_unload
,
925 buffer_resource_sub_resource_get_desc
,
926 buffer_resource_sub_resource_get_map_pitch
,
927 buffer_resource_sub_resource_map
,
928 buffer_resource_sub_resource_unmap
,
931 static HRESULT
wined3d_buffer_init(struct wined3d_buffer
*buffer
, struct wined3d_device
*device
,
932 const struct wined3d_buffer_desc
*desc
, const struct wined3d_sub_resource_data
*data
,
933 void *parent
, const struct wined3d_parent_ops
*parent_ops
, const struct wined3d_buffer_ops
*buffer_ops
)
935 const struct wined3d_format
*format
= wined3d_get_format(device
->adapter
, WINED3DFMT_R8_UNORM
, desc
->bind_flags
);
936 struct wined3d_resource
*resource
= &buffer
->resource
;
940 TRACE("buffer %p, device %p, desc byte_width %u, usage %s, bind_flags %s, "
941 "access %s, data %p, parent %p, parent_ops %p.\n",
942 buffer
, device
, desc
->byte_width
, debug_d3dusage(desc
->usage
), wined3d_debug_bind_flags(desc
->bind_flags
),
943 wined3d_debug_resource_access(desc
->access
), data
, parent
, parent_ops
);
945 if (!desc
->byte_width
)
947 WARN("Size 0 requested, returning E_INVALIDARG.\n");
951 if (desc
->bind_flags
& WINED3D_BIND_CONSTANT_BUFFER
&& desc
->byte_width
& (WINED3D_CONSTANT_BUFFER_ALIGNMENT
- 1))
953 WARN("Size %#x is not suitably aligned for constant buffers.\n", desc
->byte_width
);
957 if (data
&& !data
->data
)
959 WARN("Invalid sub-resource data specified.\n");
963 access
= desc
->access
;
964 if (desc
->bind_flags
& WINED3D_BIND_CONSTANT_BUFFER
&& wined3d_settings
.cb_access_map_w
)
965 access
|= WINED3D_RESOURCE_ACCESS_MAP_W
;
967 if (FAILED(hr
= resource_init(resource
, device
, WINED3D_RTYPE_BUFFER
, format
,
968 WINED3D_MULTISAMPLE_NONE
, 0, desc
->usage
, desc
->bind_flags
, access
,
969 desc
->byte_width
, 1, 1, desc
->byte_width
, parent
, parent_ops
, &buffer_resource_ops
)))
971 WARN("Failed to initialize resource, hr %#lx.\n", hr
);
974 buffer
->buffer_ops
= buffer_ops
;
975 buffer
->structure_byte_stride
= desc
->structure_byte_stride
;
976 buffer
->locations
= WINED3D_LOCATION_CLEARED
;
978 TRACE("buffer %p, size %#x, usage %#x, memory @ %p.\n",
979 buffer
, buffer
->resource
.size
, buffer
->resource
.usage
, buffer
->resource
.heap_memory
);
981 if (device
->create_parms
.flags
& WINED3DCREATE_SOFTWARE_VERTEXPROCESSING
982 || (desc
->usage
& WINED3DUSAGE_MANAGED
))
984 /* SWvp and managed buffers always return the same pointer in buffer
985 * maps and retain data in DISCARD maps. Keep a system memory copy of
986 * the buffer to provide the same behavior to the application. */
987 TRACE("Pinning system memory.\n");
988 buffer
->resource
.pin_sysmem
= 1;
989 buffer
->locations
= WINED3D_LOCATION_SYSMEM
;
992 if (buffer
->locations
& WINED3D_LOCATION_SYSMEM
|| !(buffer
->flags
& WINED3D_BUFFER_USE_BO
))
994 if (!wined3d_resource_prepare_sysmem(&buffer
->resource
))
995 return E_OUTOFMEMORY
;
998 if ((buffer
->flags
& WINED3D_BUFFER_USE_BO
) && !wined3d_array_reserve((void **)&buffer
->dirty_ranges
,
999 &buffer
->dirty_ranges_capacity
, 1, sizeof(*buffer
->dirty_ranges
)))
1001 ERR("Out of memory.\n");
1002 buffer_resource_unload(resource
);
1003 resource_cleanup(resource
);
1004 wined3d_resource_wait_idle(resource
);
1005 return E_OUTOFMEMORY
;
1008 if (buffer
->locations
& WINED3D_LOCATION_DISCARDED
)
1009 buffer
->resource
.client
.addr
.buffer_object
= CLIENT_BO_DISCARDED
;
1012 wined3d_buffer_init_data(buffer
, device
, data
);
1017 static BOOL
wined3d_buffer_no3d_prepare_location(struct wined3d_buffer
*buffer
,
1018 struct wined3d_context
*context
, unsigned int location
)
1020 if (location
== WINED3D_LOCATION_SYSMEM
)
1021 return wined3d_resource_prepare_sysmem(&buffer
->resource
);
1023 FIXME("Unhandled location %s.\n", wined3d_debug_location(location
));
1028 static void wined3d_buffer_no3d_unload_location(struct wined3d_buffer
*buffer
,
1029 struct wined3d_context
*context
, unsigned int location
)
1031 TRACE("buffer %p, context %p, location %s.\n", buffer
, context
, wined3d_debug_location(location
));
1034 static const struct wined3d_buffer_ops wined3d_buffer_no3d_ops
=
1036 wined3d_buffer_no3d_prepare_location
,
1037 wined3d_buffer_no3d_unload_location
,
1040 HRESULT
wined3d_buffer_no3d_init(struct wined3d_buffer
*buffer_no3d
, struct wined3d_device
*device
,
1041 const struct wined3d_buffer_desc
*desc
, const struct wined3d_sub_resource_data
*data
,
1042 void *parent
, const struct wined3d_parent_ops
*parent_ops
)
1044 TRACE("buffer_no3d %p, device %p, desc %p, data %p, parent %p, parent_ops %p.\n",
1045 buffer_no3d
, device
, desc
, data
, parent
, parent_ops
);
1047 return wined3d_buffer_init(buffer_no3d
, device
, desc
, data
, parent
, parent_ops
, &wined3d_buffer_no3d_ops
);
1050 static BOOL
wined3d_buffer_gl_prepare_location(struct wined3d_buffer
*buffer
,
1051 struct wined3d_context
*context
, unsigned int location
)
1053 struct wined3d_context_gl
*context_gl
= wined3d_context_gl(context
);
1054 struct wined3d_buffer_gl
*buffer_gl
= wined3d_buffer_gl(buffer
);
1058 case WINED3D_LOCATION_SYSMEM
:
1059 return wined3d_resource_prepare_sysmem(&buffer
->resource
);
1061 case WINED3D_LOCATION_BUFFER
:
1062 if (buffer
->buffer_object
)
1065 if (!(buffer
->flags
& WINED3D_BUFFER_USE_BO
))
1067 WARN("Trying to create BO for buffer %p with no WINED3D_BUFFER_USE_BO.\n", buffer
);
1070 return wined3d_buffer_gl_create_buffer_object(buffer_gl
, context_gl
);
1073 ERR("Invalid location %s.\n", wined3d_debug_location(location
));
1078 static void wined3d_buffer_gl_unload_location(struct wined3d_buffer
*buffer
,
1079 struct wined3d_context
*context
, unsigned int location
)
1081 TRACE("buffer %p, context %p, location %s.\n", buffer
, context
, wined3d_debug_location(location
));
1085 case WINED3D_LOCATION_BUFFER
:
1086 wined3d_buffer_gl_destroy_buffer_object(wined3d_buffer_gl(buffer
), wined3d_context_gl(context
));
1090 ERR("Unhandled location %s.\n", wined3d_debug_location(location
));
1095 static const struct wined3d_buffer_ops wined3d_buffer_gl_ops
=
1097 wined3d_buffer_gl_prepare_location
,
1098 wined3d_buffer_gl_unload_location
,
1101 HRESULT
wined3d_buffer_gl_init(struct wined3d_buffer_gl
*buffer_gl
, struct wined3d_device
*device
,
1102 const struct wined3d_buffer_desc
*desc
, const struct wined3d_sub_resource_data
*data
,
1103 void *parent
, const struct wined3d_parent_ops
*parent_ops
)
1105 const struct wined3d_gl_info
*gl_info
= &wined3d_adapter_gl(device
->adapter
)->gl_info
;
1107 TRACE("buffer_gl %p, device %p, desc %p, data %p, parent %p, parent_ops %p.\n",
1108 buffer_gl
, device
, desc
, data
, parent
, parent_ops
);
1110 /* Observations show that draw_primitive_immediate_mode() is faster on
1111 * dynamic vertex buffers than converting + draw_primitive_arrays().
1112 * (Half-Life 2 and others.) */
1113 if (!(desc
->access
& WINED3D_RESOURCE_ACCESS_GPU
))
1114 TRACE("Not creating a BO because the buffer is not GPU accessible.\n");
1115 else if (!gl_info
->supported
[ARB_VERTEX_BUFFER_OBJECT
])
1116 TRACE("Not creating a BO because GL_ARB_vertex_buffer is not supported.\n");
1117 else if (!(gl_info
->supported
[APPLE_FLUSH_BUFFER_RANGE
] || gl_info
->supported
[ARB_MAP_BUFFER_RANGE
])
1118 && (desc
->usage
& WINED3DUSAGE_DYNAMIC
))
1119 TRACE("Not creating a BO because the buffer has dynamic usage and no GL support.\n");
1121 buffer_gl
->b
.flags
|= WINED3D_BUFFER_USE_BO
;
1123 return wined3d_buffer_init(&buffer_gl
->b
, device
, desc
, data
, parent
, parent_ops
, &wined3d_buffer_gl_ops
);
1126 VkBufferUsageFlags
vk_buffer_usage_from_bind_flags(uint32_t bind_flags
)
1128 VkBufferUsageFlags usage
;
1130 usage
= VK_BUFFER_USAGE_TRANSFER_SRC_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT
;
1131 if (bind_flags
& WINED3D_BIND_VERTEX_BUFFER
)
1132 usage
|= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
;
1133 if (bind_flags
& WINED3D_BIND_INDEX_BUFFER
)
1134 usage
|= VK_BUFFER_USAGE_INDEX_BUFFER_BIT
;
1135 if (bind_flags
& WINED3D_BIND_CONSTANT_BUFFER
)
1136 usage
|= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
;
1137 if (bind_flags
& WINED3D_BIND_SHADER_RESOURCE
)
1138 usage
|= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
;
1139 if (bind_flags
& WINED3D_BIND_STREAM_OUTPUT
)
1140 usage
|= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT
;
1141 if (bind_flags
& WINED3D_BIND_UNORDERED_ACCESS
)
1142 usage
|= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT
;
1143 if (bind_flags
& WINED3D_BIND_INDIRECT_BUFFER
)
1144 usage
|= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
;
1145 if (bind_flags
& (WINED3D_BIND_RENDER_TARGET
| WINED3D_BIND_DEPTH_STENCIL
))
1146 FIXME("Ignoring some bind flags %#x.\n", bind_flags
);
1150 VkMemoryPropertyFlags
vk_memory_type_from_access_flags(uint32_t access
, uint32_t usage
)
1152 VkMemoryPropertyFlags memory_type
= 0;
1154 if (access
& WINED3D_RESOURCE_ACCESS_MAP_R
)
1155 memory_type
|= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT
;
1156 else if (access
& WINED3D_RESOURCE_ACCESS_MAP_W
)
1157 memory_type
|= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
;
1158 else if (!(usage
& WINED3DUSAGE_DYNAMIC
))
1159 memory_type
|= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
;
1163 static BOOL
wined3d_buffer_vk_create_buffer_object(struct wined3d_buffer_vk
*buffer_vk
,
1164 struct wined3d_context_vk
*context_vk
)
1166 struct wined3d_resource
*resource
= &buffer_vk
->b
.resource
;
1167 struct wined3d_bo_vk
*bo_vk
;
1169 if (!(bo_vk
= malloc(sizeof(*bo_vk
))))
1172 if (!(wined3d_context_vk_create_bo(context_vk
, resource
->size
,
1173 vk_buffer_usage_from_bind_flags(resource
->bind_flags
),
1174 vk_memory_type_from_access_flags(resource
->access
, resource
->usage
), bo_vk
)))
1176 WARN("Failed to create Vulkan buffer.\n");
1181 buffer_vk
->b
.buffer_object
= &bo_vk
->b
;
1182 buffer_invalidate_bo_range(&buffer_vk
->b
, 0, 0);
1187 const VkDescriptorBufferInfo
*wined3d_buffer_vk_get_buffer_info(struct wined3d_buffer_vk
*buffer_vk
)
1189 struct wined3d_bo_vk
*bo
= wined3d_bo_vk(buffer_vk
->b
.buffer_object
);
1191 if (buffer_vk
->b
.bo_user
.valid
)
1192 return &buffer_vk
->buffer_info
;
1194 buffer_vk
->buffer_info
.buffer
= bo
->vk_buffer
;
1195 buffer_vk
->buffer_info
.offset
= bo
->b
.buffer_offset
;
1196 buffer_vk
->buffer_info
.range
= buffer_vk
->b
.resource
.size
;
1197 wined3d_buffer_validate_user(&buffer_vk
->b
);
1199 return &buffer_vk
->buffer_info
;
1202 static BOOL
wined3d_buffer_vk_prepare_location(struct wined3d_buffer
*buffer
,
1203 struct wined3d_context
*context
, unsigned int location
)
1207 case WINED3D_LOCATION_SYSMEM
:
1208 return wined3d_resource_prepare_sysmem(&buffer
->resource
);
1210 case WINED3D_LOCATION_BUFFER
:
1211 if (buffer
->buffer_object
)
1214 return wined3d_buffer_vk_create_buffer_object(wined3d_buffer_vk(buffer
), wined3d_context_vk(context
));
1217 FIXME("Unhandled location %s.\n", wined3d_debug_location(location
));
1222 static void wined3d_buffer_vk_unload_location(struct wined3d_buffer
*buffer
,
1223 struct wined3d_context
*context
, unsigned int location
)
1225 struct wined3d_context_vk
*context_vk
= wined3d_context_vk(context
);
1226 struct wined3d_bo_vk
*bo_vk
= wined3d_bo_vk(buffer
->buffer_object
);
1228 TRACE("buffer %p, context %p, location %s.\n", buffer
, context
, wined3d_debug_location(location
));
1232 case WINED3D_LOCATION_BUFFER
:
1233 if (buffer
->bo_user
.valid
)
1235 buffer
->bo_user
.valid
= false;
1236 list_remove(&buffer
->bo_user
.entry
);
1238 if (!--bo_vk
->b
.refcount
)
1240 wined3d_context_vk_destroy_bo(context_vk
, bo_vk
);
1243 buffer
->buffer_object
= NULL
;
1247 ERR("Unhandled location %s.\n", wined3d_debug_location(location
));
1252 static const struct wined3d_buffer_ops wined3d_buffer_vk_ops
=
1254 wined3d_buffer_vk_prepare_location
,
1255 wined3d_buffer_vk_unload_location
,
1258 HRESULT
wined3d_buffer_vk_init(struct wined3d_buffer_vk
*buffer_vk
, struct wined3d_device
*device
,
1259 const struct wined3d_buffer_desc
*desc
, const struct wined3d_sub_resource_data
*data
,
1260 void *parent
, const struct wined3d_parent_ops
*parent_ops
)
1262 const struct wined3d_vk_info
*vk_info
= &wined3d_adapter_vk(device
->adapter
)->vk_info
;
1264 TRACE("buffer_vk %p, device %p, desc %p, data %p, parent %p, parent_ops %p.\n",
1265 buffer_vk
, device
, desc
, data
, parent
, parent_ops
);
1267 if ((desc
->bind_flags
& WINED3D_BIND_STREAM_OUTPUT
)
1268 && !vk_info
->supported
[WINED3D_VK_EXT_TRANSFORM_FEEDBACK
])
1270 WARN("The Vulkan implementation does not support transform feedback.\n");
1271 return WINED3DERR_INVALIDCALL
;
1274 if (desc
->access
& WINED3D_RESOURCE_ACCESS_GPU
)
1275 buffer_vk
->b
.flags
|= WINED3D_BUFFER_USE_BO
;
1277 return wined3d_buffer_init(&buffer_vk
->b
, device
, desc
, data
, parent
, parent_ops
, &wined3d_buffer_vk_ops
);
1280 void wined3d_buffer_vk_barrier(struct wined3d_buffer_vk
*buffer_vk
,
1281 struct wined3d_context_vk
*context_vk
, uint32_t bind_mask
)
1283 uint32_t src_bind_mask
= 0;
1285 TRACE("buffer_vk %p, context_vk %p, bind_mask %s.\n",
1286 buffer_vk
, context_vk
, wined3d_debug_bind_flags(bind_mask
));
1288 if (bind_mask
& ~WINED3D_READ_ONLY_BIND_MASK
)
1290 src_bind_mask
= buffer_vk
->bind_mask
& WINED3D_READ_ONLY_BIND_MASK
;
1292 src_bind_mask
= buffer_vk
->bind_mask
;
1294 buffer_vk
->bind_mask
= bind_mask
;
1296 else if ((buffer_vk
->bind_mask
& bind_mask
) != bind_mask
)
1298 src_bind_mask
= buffer_vk
->bind_mask
& ~WINED3D_READ_ONLY_BIND_MASK
;
1299 buffer_vk
->bind_mask
|= bind_mask
;
1304 const struct wined3d_bo_vk
*bo
= wined3d_bo_vk(buffer_vk
->b
.buffer_object
);
1305 const struct wined3d_vk_info
*vk_info
= context_vk
->vk_info
;
1306 VkBufferMemoryBarrier vk_barrier
;
1308 TRACE(" %s -> %s.\n",
1309 wined3d_debug_bind_flags(src_bind_mask
), wined3d_debug_bind_flags(bind_mask
));
1311 wined3d_context_vk_end_current_render_pass(context_vk
);
1313 vk_barrier
.sType
= VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER
;
1314 vk_barrier
.pNext
= NULL
;
1315 vk_barrier
.srcAccessMask
= vk_access_mask_from_bind_flags(src_bind_mask
);
1316 vk_barrier
.dstAccessMask
= vk_access_mask_from_bind_flags(bind_mask
);
1317 vk_barrier
.srcQueueFamilyIndex
= VK_QUEUE_FAMILY_IGNORED
;
1318 vk_barrier
.dstQueueFamilyIndex
= VK_QUEUE_FAMILY_IGNORED
;
1319 vk_barrier
.buffer
= bo
->vk_buffer
;
1320 vk_barrier
.offset
= bo
->b
.buffer_offset
;
1321 vk_barrier
.size
= buffer_vk
->b
.resource
.size
;
1322 VK_CALL(vkCmdPipelineBarrier(wined3d_context_vk_get_command_buffer(context_vk
),
1323 vk_pipeline_stage_mask_from_bind_flags(src_bind_mask
),
1324 vk_pipeline_stage_mask_from_bind_flags(bind_mask
),
1325 0, 0, NULL
, 1, &vk_barrier
, 0, NULL
));
1329 HRESULT CDECL
wined3d_buffer_create(struct wined3d_device
*device
, const struct wined3d_buffer_desc
*desc
,
1330 const struct wined3d_sub_resource_data
*data
, void *parent
, const struct wined3d_parent_ops
*parent_ops
,
1331 struct wined3d_buffer
**buffer
)
1333 TRACE("device %p, desc %p, data %p, parent %p, parent_ops %p, buffer %p.\n",
1334 device
, desc
, data
, parent
, parent_ops
, buffer
);
1336 return device
->adapter
->adapter_ops
->adapter_create_buffer(device
, desc
, data
, parent
, parent_ops
, buffer
);
1339 static HRESULT
wined3d_streaming_buffer_prepare(struct wined3d_device
*device
,
1340 struct wined3d_streaming_buffer
*buffer
, unsigned int min_size
)
1342 struct wined3d_buffer
*wined3d_buffer
;
1343 struct wined3d_buffer_desc desc
;
1344 unsigned int old_size
= 0;
1350 old_size
= buffer
->buffer
->resource
.size
;
1351 if (old_size
>= min_size
)
1355 size
= max(SB_MIN_SIZE
, max(old_size
* 2, min_size
));
1356 TRACE("Growing buffer to %u bytes.\n", size
);
1358 desc
.byte_width
= size
;
1359 desc
.usage
= WINED3DUSAGE_DYNAMIC
;
1360 desc
.bind_flags
= buffer
->bind_flags
;
1361 desc
.access
= WINED3D_RESOURCE_ACCESS_GPU
| WINED3D_RESOURCE_ACCESS_MAP_W
;
1362 desc
.misc_flags
= 0;
1363 desc
.structure_byte_stride
= 0;
1365 if (SUCCEEDED(hr
= wined3d_buffer_create(device
, &desc
, NULL
, NULL
, &wined3d_null_parent_ops
, &wined3d_buffer
)))
1368 wined3d_buffer_decref(buffer
->buffer
);
1369 buffer
->buffer
= wined3d_buffer
;
1375 HRESULT CDECL
wined3d_streaming_buffer_map(struct wined3d_device
*device
,
1376 struct wined3d_streaming_buffer
*buffer
, unsigned int size
, unsigned int stride
,
1377 unsigned int *ret_pos
, void **ret_data
)
1379 unsigned int map_flags
= WINED3D_MAP_WRITE
;
1380 struct wined3d_resource
*resource
;
1381 struct wined3d_map_desc map_desc
;
1382 unsigned int pos
, align
;
1383 struct wined3d_box box
;
1386 TRACE("device %p, buffer %p, size %u, stride %u, ret_pos %p, ret_data %p.\n",
1387 device
, buffer
, size
, stride
, ret_pos
, ret_data
);
1389 if (FAILED(hr
= wined3d_streaming_buffer_prepare(device
, buffer
, size
)))
1391 resource
= &buffer
->buffer
->resource
;
1394 if ((align
= pos
% stride
))
1395 align
= stride
- align
;
1396 if (pos
+ size
+ align
> resource
->size
)
1399 map_flags
|= WINED3D_MAP_DISCARD
;
1404 map_flags
|= WINED3D_MAP_NOOVERWRITE
;
1407 wined3d_box_set(&box
, pos
, 0, pos
+ size
, 1, 0, 1);
1408 if (SUCCEEDED(hr
= wined3d_resource_map(resource
, 0, &map_desc
, &box
, map_flags
)))
1411 *ret_data
= map_desc
.data
;
1412 buffer
->pos
= pos
+ size
;
1417 void CDECL
wined3d_streaming_buffer_unmap(struct wined3d_streaming_buffer
*buffer
)
1419 wined3d_resource_unmap(&buffer
->buffer
->resource
, 0);
1422 HRESULT CDECL
wined3d_streaming_buffer_upload(struct wined3d_device
*device
, struct wined3d_streaming_buffer
*buffer
,
1423 const void *data
, unsigned int size
, unsigned int stride
, unsigned int *ret_pos
)
1428 if (SUCCEEDED(hr
= wined3d_streaming_buffer_map(device
, buffer
, size
, stride
, ret_pos
, &dst_data
)))
1430 memcpy(dst_data
, data
, size
);
1431 wined3d_streaming_buffer_unmap(buffer
);