1 /* 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 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 25 #include "wined3d_private.h" 26 27 WINE_DEFAULT_DEBUG_CHANNEL(d3d); 28 29 #define WINED3D_BUFFER_HASDESC 0x01 /* A vertex description has been found. */ 30 #define WINED3D_BUFFER_USE_BO 0x02 /* Use a buffer object for this buffer. */ 31 #define WINED3D_BUFFER_PIN_SYSMEM 0x04 /* Keep a system memory copy for this buffer. */ 32 #define WINED3D_BUFFER_DISCARD 0x08 /* A DISCARD lock has occurred since the last preload. */ 33 #define WINED3D_BUFFER_APPLESYNC 0x10 /* Using sync as in GL_APPLE_flush_buffer_range. */ 34 35 #define VB_MAXDECLCHANGES 100 /* After that number of decl changes we stop converting */ 36 #define VB_RESETDECLCHANGE 1000 /* Reset the decl changecount after that number of draws */ 37 #define VB_MAXFULLCONVERSIONS 5 /* Number of full conversions before we stop converting */ 38 #define VB_RESETFULLCONVS 20 /* Reset full conversion counts after that number of draws */ 39 40 static void wined3d_buffer_evict_sysmem(struct wined3d_buffer *buffer) 41 { 42 if (buffer->flags & WINED3D_BUFFER_PIN_SYSMEM) 43 { 44 TRACE("Not evicting system memory for buffer %p.\n", buffer); 45 return; 46 } 47 48 TRACE("Evicting system memory for buffer %p.\n", buffer); 49 wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_SYSMEM); 50 wined3d_resource_free_sysmem(&buffer->resource); 51 } 52 53 static void buffer_invalidate_bo_range(struct wined3d_buffer *buffer, unsigned int offset, unsigned int size) 54 { 55 if (!offset && (!size || size == buffer->resource.size)) 56 goto invalidate_all; 57 58 if (offset > buffer->resource.size || size > buffer->resource.size - offset) 59 { 60 WARN("Invalid range specified, invalidating entire buffer.\n"); 61 goto invalidate_all; 62 } 63 64 if (!wined3d_array_reserve((void **)&buffer->maps, &buffer->maps_size, 65 buffer->modified_areas + 1, sizeof(*buffer->maps))) 66 { 67 ERR("Failed to allocate maps array, invalidating entire buffer.\n"); 68 goto invalidate_all; 69 } 70 71 buffer->maps[buffer->modified_areas].offset = offset; 72 buffer->maps[buffer->modified_areas].size = size; 73 ++buffer->modified_areas; 74 return; 75 76 invalidate_all: 77 buffer->modified_areas = 1; 78 buffer->maps[0].offset = 0; 79 buffer->maps[0].size = buffer->resource.size; 80 } 81 82 static inline void buffer_clear_dirty_areas(struct wined3d_buffer *This) 83 { 84 This->modified_areas = 0; 85 } 86 87 static BOOL buffer_is_dirty(const struct wined3d_buffer *buffer) 88 { 89 return !!buffer->modified_areas; 90 } 91 92 static BOOL buffer_is_fully_dirty(const struct wined3d_buffer *buffer) 93 { 94 return buffer->modified_areas == 1 95 && !buffer->maps->offset && buffer->maps->size == buffer->resource.size; 96 } 97 98 static void wined3d_buffer_validate_location(struct wined3d_buffer *buffer, DWORD location) 99 { 100 TRACE("buffer %p, location %s.\n", buffer, wined3d_debug_location(location)); 101 102 if (location & WINED3D_LOCATION_BUFFER) 103 buffer_clear_dirty_areas(buffer); 104 105 buffer->locations |= location; 106 107 TRACE("New locations flags are %s.\n", wined3d_debug_location(buffer->locations)); 108 } 109 110 static void wined3d_buffer_invalidate_range(struct wined3d_buffer *buffer, DWORD location, 111 unsigned int offset, unsigned int size) 112 { 113 TRACE("buffer %p, location %s, offset %u, size %u.\n", 114 buffer, wined3d_debug_location(location), offset, size); 115 116 if (location & WINED3D_LOCATION_BUFFER) 117 buffer_invalidate_bo_range(buffer, offset, size); 118 119 buffer->locations &= ~location; 120 121 TRACE("New locations flags are %s.\n", wined3d_debug_location(buffer->locations)); 122 123 if (!buffer->locations) 124 ERR("Buffer %p does not have any up to date location.\n", buffer); 125 } 126 127 void wined3d_buffer_invalidate_location(struct wined3d_buffer *buffer, DWORD location) 128 { 129 wined3d_buffer_invalidate_range(buffer, location, 0, 0); 130 } 131 132 /* Context activation is done by the caller. */ 133 static void buffer_bind(struct wined3d_buffer *buffer, struct wined3d_context *context) 134 { 135 context_bind_bo(context, buffer->buffer_type_hint, buffer->buffer_object); 136 } 137 138 /* Context activation is done by the caller. */ 139 static void buffer_destroy_buffer_object(struct wined3d_buffer *buffer, struct wined3d_context *context) 140 { 141 const struct wined3d_gl_info *gl_info = context->gl_info; 142 struct wined3d_resource *resource = &buffer->resource; 143 144 if (!buffer->buffer_object) 145 return; 146 147 /* The stream source state handler might have read the memory of the 148 * vertex buffer already and got the memory in the vbo which is not 149 * valid any longer. Dirtify the stream source to force a reload. This 150 * happens only once per changed vertexbuffer and should occur rather 151 * rarely. */ 152 if (resource->bind_count) 153 { 154 if (buffer->bind_flags & WINED3D_BIND_VERTEX_BUFFER) 155 device_invalidate_state(resource->device, STATE_STREAMSRC); 156 if (buffer->bind_flags & WINED3D_BIND_INDEX_BUFFER) 157 device_invalidate_state(resource->device, STATE_INDEXBUFFER); 158 if (buffer->bind_flags & WINED3D_BIND_CONSTANT_BUFFER) 159 { 160 device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX)); 161 device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL)); 162 device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN)); 163 device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY)); 164 device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL)); 165 device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE)); 166 } 167 if (buffer->bind_flags & WINED3D_BIND_STREAM_OUTPUT) 168 { 169 device_invalidate_state(resource->device, STATE_STREAM_OUTPUT); 170 if (context->transform_feedback_active) 171 { 172 /* We have to make sure that transform feedback is not active 173 * when deleting a potentially bound transform feedback buffer. 174 * This may happen when the device is being destroyed. */ 175 WARN("Deleting buffer object for buffer %p, disabling transform feedback.\n", buffer); 176 context_end_transform_feedback(context); 177 } 178 } 179 } 180 181 GL_EXTCALL(glDeleteBuffers(1, &buffer->buffer_object)); 182 checkGLcall("glDeleteBuffers"); 183 buffer->buffer_object = 0; 184 185 if (buffer->fence) 186 { 187 wined3d_fence_destroy(buffer->fence); 188 buffer->fence = NULL; 189 } 190 buffer->flags &= ~WINED3D_BUFFER_APPLESYNC; 191 } 192 193 /* Context activation is done by the caller. */ 194 static BOOL buffer_create_buffer_object(struct wined3d_buffer *buffer, struct wined3d_context *context) 195 { 196 const struct wined3d_gl_info *gl_info = context->gl_info; 197 GLenum gl_usage = GL_STATIC_DRAW; 198 GLenum error; 199 200 TRACE("Creating an OpenGL buffer object for wined3d_buffer %p with usage %s.\n", 201 buffer, debug_d3dusage(buffer->resource.usage)); 202 203 /* Make sure that the gl error is cleared. Do not use checkGLcall 204 * here because checkGLcall just prints a fixme and continues. However, 205 * if an error during VBO creation occurs we can fall back to non-VBO operation 206 * with full functionality(but performance loss). 207 */ 208 while (gl_info->gl_ops.gl.p_glGetError() != GL_NO_ERROR); 209 210 /* Basically the FVF parameter passed to CreateVertexBuffer is no good. 211 * The vertex declaration from the device determines how the data in the 212 * buffer is interpreted. This means that on each draw call the buffer has 213 * to be verified to check if the rhw and color values are in the correct 214 * format. */ 215 216 GL_EXTCALL(glGenBuffers(1, &buffer->buffer_object)); 217 error = gl_info->gl_ops.gl.p_glGetError(); 218 if (!buffer->buffer_object || error != GL_NO_ERROR) 219 { 220 ERR("Failed to create a BO with error %s (%#x).\n", debug_glerror(error), error); 221 goto fail; 222 } 223 224 buffer_bind(buffer, context); 225 error = gl_info->gl_ops.gl.p_glGetError(); 226 if (error != GL_NO_ERROR) 227 { 228 ERR("Failed to bind the BO with error %s (%#x).\n", debug_glerror(error), error); 229 goto fail; 230 } 231 232 if (buffer->resource.usage & WINED3DUSAGE_DYNAMIC) 233 { 234 TRACE("Buffer has WINED3DUSAGE_DYNAMIC set.\n"); 235 gl_usage = GL_STREAM_DRAW_ARB; 236 237 if (gl_info->supported[APPLE_FLUSH_BUFFER_RANGE]) 238 { 239 GL_EXTCALL(glBufferParameteriAPPLE(buffer->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE)); 240 GL_EXTCALL(glBufferParameteriAPPLE(buffer->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE)); 241 checkGLcall("glBufferParameteriAPPLE"); 242 buffer->flags |= WINED3D_BUFFER_APPLESYNC; 243 } 244 /* No setup is needed here for GL_ARB_map_buffer_range. */ 245 } 246 247 GL_EXTCALL(glBufferData(buffer->buffer_type_hint, buffer->resource.size, NULL, gl_usage)); 248 error = gl_info->gl_ops.gl.p_glGetError(); 249 if (error != GL_NO_ERROR) 250 { 251 ERR("glBufferData failed with error %s (%#x).\n", debug_glerror(error), error); 252 goto fail; 253 } 254 255 buffer->buffer_object_usage = gl_usage; 256 buffer_invalidate_bo_range(buffer, 0, 0); 257 258 return TRUE; 259 260 fail: 261 /* Clean up all BO init, but continue because we can work without a BO :-) */ 262 ERR("Failed to create a buffer object. Continuing, but performance issues may occur.\n"); 263 buffer->flags &= ~WINED3D_BUFFER_USE_BO; 264 buffer_destroy_buffer_object(buffer, context); 265 buffer_clear_dirty_areas(buffer); 266 return FALSE; 267 } 268 269 static BOOL buffer_process_converted_attribute(struct wined3d_buffer *buffer, 270 const enum wined3d_buffer_conversion_type conversion_type, 271 const struct wined3d_stream_info_element *attrib, DWORD *stride_this_run) 272 { 273 const struct wined3d_format *format = attrib->format; 274 BOOL ret = FALSE; 275 unsigned int i; 276 DWORD_PTR data; 277 278 /* Check for some valid situations which cause us pain. One is if the buffer is used for 279 * constant attributes(stride = 0), the other one is if the buffer is used on two streams 280 * with different strides. In the 2nd case we might have to drop conversion entirely, 281 * it is possible that the same bytes are once read as FLOAT2 and once as UBYTE4N. 282 */ 283 if (!attrib->stride) 284 { 285 FIXME("%s used with stride 0, let's hope we get the vertex stride from somewhere else.\n", 286 debug_d3dformat(format->id)); 287 } 288 else if (attrib->stride != *stride_this_run && *stride_this_run) 289 { 290 FIXME("Got two concurrent strides, %d and %d.\n", attrib->stride, *stride_this_run); 291 } 292 else 293 { 294 *stride_this_run = attrib->stride; 295 if (buffer->stride != *stride_this_run) 296 { 297 /* We rely that this happens only on the first converted attribute that is found, 298 * if at all. See above check 299 */ 300 TRACE("Reconverting because converted attributes occur, and the stride changed.\n"); 301 buffer->stride = *stride_this_run; 302 HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer->conversion_map); 303 buffer->conversion_map = wined3d_calloc(buffer->stride, sizeof(*buffer->conversion_map)); 304 ret = TRUE; 305 } 306 } 307 308 data = ((DWORD_PTR)attrib->data.addr) % buffer->stride; 309 for (i = 0; i < format->attribute_size; ++i) 310 { 311 DWORD_PTR idx = (data + i) % buffer->stride; 312 if (buffer->conversion_map[idx] != conversion_type) 313 { 314 TRACE("Byte %lu in vertex changed:\n", idx); 315 TRACE(" It was type %#x, is %#x now.\n", buffer->conversion_map[idx], conversion_type); 316 ret = TRUE; 317 buffer->conversion_map[idx] = conversion_type; 318 } 319 } 320 321 return ret; 322 } 323 324 #define WINED3D_BUFFER_FIXUP_D3DCOLOR 0x01 325 #define WINED3D_BUFFER_FIXUP_XYZRHW 0x02 326 327 static BOOL buffer_check_attribute(struct wined3d_buffer *This, const struct wined3d_stream_info *si, 328 const struct wined3d_state *state, UINT attrib_idx, DWORD fixup_flags, DWORD *stride_this_run) 329 { 330 const struct wined3d_stream_info_element *attrib = &si->elements[attrib_idx]; 331 enum wined3d_format_id format; 332 BOOL ret = FALSE; 333 334 /* Ignore attributes that do not have our vbo. After that check we can be sure that the attribute is 335 * there, on nonexistent attribs the vbo is 0. 336 */ 337 if (!(si->use_map & (1u << attrib_idx)) 338 || state->streams[attrib->stream_idx].buffer != This) 339 return FALSE; 340 341 format = attrib->format->id; 342 /* Look for newly appeared conversion */ 343 if (fixup_flags & WINED3D_BUFFER_FIXUP_D3DCOLOR && format == WINED3DFMT_B8G8R8A8_UNORM) 344 { 345 ret = buffer_process_converted_attribute(This, CONV_D3DCOLOR, attrib, stride_this_run); 346 } 347 else if (fixup_flags & WINED3D_BUFFER_FIXUP_XYZRHW && si->position_transformed) 348 { 349 if (format != WINED3DFMT_R32G32B32A32_FLOAT) 350 { 351 FIXME("Unexpected format %s for transformed position.\n", debug_d3dformat(format)); 352 return FALSE; 353 } 354 355 ret = buffer_process_converted_attribute(This, CONV_POSITIONT, attrib, stride_this_run); 356 } 357 else if (This->conversion_map) 358 { 359 ret = buffer_process_converted_attribute(This, CONV_NONE, attrib, stride_this_run); 360 } 361 362 return ret; 363 } 364 365 static BOOL buffer_find_decl(struct wined3d_buffer *This, const struct wined3d_stream_info *si, 366 const struct wined3d_state *state, DWORD fixup_flags) 367 { 368 UINT stride_this_run = 0; 369 BOOL ret = FALSE; 370 371 /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer. 372 * Once we have our declaration there is no need to look it up again. Index buffers also never need 373 * conversion, so once the (empty) conversion structure is created don't bother checking again 374 */ 375 if (This->flags & WINED3D_BUFFER_HASDESC) 376 { 377 if(This->resource.usage & WINED3DUSAGE_STATICDECL) return FALSE; 378 } 379 380 if (!fixup_flags) 381 { 382 TRACE("No fixup required.\n"); 383 if(This->conversion_map) 384 { 385 HeapFree(GetProcessHeap(), 0, This->conversion_map); 386 This->conversion_map = NULL; 387 This->stride = 0; 388 return TRUE; 389 } 390 391 return FALSE; 392 } 393 394 TRACE("Finding vertex buffer conversion information\n"); 395 /* Certain declaration types need some fixups before we can pass them to 396 * opengl. This means D3DCOLOR attributes with fixed function vertex 397 * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if 398 * GL_ARB_half_float_vertex is not supported. 399 * 400 * Note for d3d8 and d3d9: 401 * The vertex buffer FVF doesn't help with finding them, we have to use 402 * the decoded vertex declaration and pick the things that concern the 403 * current buffer. A problem with this is that this can change between 404 * draws, so we have to validate the information and reprocess the buffer 405 * if it changes, and avoid false positives for performance reasons. 406 * WineD3D doesn't even know the vertex buffer any more, it is managed 407 * by the client libraries and passed to SetStreamSource and ProcessVertices 408 * as needed. 409 * 410 * We have to distinguish between vertex shaders and fixed function to 411 * pick the way we access the strided vertex information. 412 * 413 * This code sets up a per-byte array with the size of the detected 414 * stride of the arrays in the buffer. For each byte we have a field 415 * that marks the conversion needed on this byte. For example, the 416 * following declaration with fixed function vertex processing: 417 * 418 * POSITIONT, FLOAT4 419 * NORMAL, FLOAT3 420 * DIFFUSE, FLOAT16_4 421 * SPECULAR, D3DCOLOR 422 * 423 * Will result in 424 * { POSITIONT }{ NORMAL }{ DIFFUSE }{SPECULAR } 425 * [P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][0][0][0][0][0][0][0][0][0][0][0][0][F][F][F][F][F][F][F][F][C][C][C][C] 426 * 427 * Where in this example map P means 4 component position conversion, 0 428 * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR 429 * conversion (red / blue swizzle). 430 * 431 * If we're doing conversion and the stride changes we have to reconvert 432 * the whole buffer. Note that we do not mind if the semantic changes, 433 * we only care for the conversion type. So if the NORMAL is replaced 434 * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced 435 * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some 436 * conversion types depend on the semantic as well, for example a FLOAT4 437 * texcoord needs no conversion while a FLOAT4 positiont needs one 438 */ 439 440 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_POSITION, 441 fixup_flags, &stride_this_run) || ret; 442 fixup_flags &= ~WINED3D_BUFFER_FIXUP_XYZRHW; 443 444 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_BLENDWEIGHT, 445 fixup_flags, &stride_this_run) || ret; 446 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_BLENDINDICES, 447 fixup_flags, &stride_this_run) || ret; 448 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_NORMAL, 449 fixup_flags, &stride_this_run) || ret; 450 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_DIFFUSE, 451 fixup_flags, &stride_this_run) || ret; 452 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_SPECULAR, 453 fixup_flags, &stride_this_run) || ret; 454 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD0, 455 fixup_flags, &stride_this_run) || ret; 456 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD1, 457 fixup_flags, &stride_this_run) || ret; 458 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD2, 459 fixup_flags, &stride_this_run) || ret; 460 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD3, 461 fixup_flags, &stride_this_run) || ret; 462 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD4, 463 fixup_flags, &stride_this_run) || ret; 464 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD5, 465 fixup_flags, &stride_this_run) || ret; 466 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD6, 467 fixup_flags, &stride_this_run) || ret; 468 ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD7, 469 fixup_flags, &stride_this_run) || ret; 470 471 if (!stride_this_run && This->conversion_map) 472 { 473 /* Sanity test */ 474 if (!ret) ERR("no converted attributes found, old conversion map exists, and no declaration change?\n"); 475 HeapFree(GetProcessHeap(), 0, This->conversion_map); 476 This->conversion_map = NULL; 477 This->stride = 0; 478 } 479 480 if (ret) TRACE("Conversion information changed\n"); 481 482 return ret; 483 } 484 485 static inline unsigned int fixup_d3dcolor(DWORD *dst_color) 486 { 487 DWORD src_color = *dst_color; 488 489 /* Color conversion like in draw_primitive_immediate_mode(). Watch out for 490 * endianness. If we want this to work on big-endian machines as well we 491 * have to consider more things. 492 * 493 * 0xff000000: Alpha mask 494 * 0x00ff0000: Blue mask 495 * 0x0000ff00: Green mask 496 * 0x000000ff: Red mask 497 */ 498 *dst_color = 0; 499 *dst_color |= (src_color & 0xff00ff00u); /* Alpha Green */ 500 *dst_color |= (src_color & 0x00ff0000u) >> 16; /* Red */ 501 *dst_color |= (src_color & 0x000000ffu) << 16; /* Blue */ 502 503 return sizeof(*dst_color); 504 } 505 506 static inline unsigned int fixup_transformed_pos(struct wined3d_vec4 *p) 507 { 508 /* rhw conversion like in position_float4(). */ 509 if (p->w != 1.0f && p->w != 0.0f) 510 { 511 float w = 1.0f / p->w; 512 p->x *= w; 513 p->y *= w; 514 p->z *= w; 515 p->w = w; 516 } 517 518 return sizeof(*p); 519 } 520 521 ULONG CDECL wined3d_buffer_incref(struct wined3d_buffer *buffer) 522 { 523 ULONG refcount = InterlockedIncrement(&buffer->resource.ref); 524 525 TRACE("%p increasing refcount to %u.\n", buffer, refcount); 526 527 return refcount; 528 } 529 530 /* Context activation is done by the caller. */ 531 static void wined3d_buffer_upload_ranges(struct wined3d_buffer *buffer, struct wined3d_context *context, 532 const void *data, unsigned int range_count, const struct wined3d_map_range *ranges) 533 { 534 const struct wined3d_gl_info *gl_info = context->gl_info; 535 const struct wined3d_map_range *range; 536 537 buffer_bind(buffer, context); 538 539 while (range_count--) 540 { 541 range = &ranges[range_count]; 542 GL_EXTCALL(glBufferSubData(buffer->buffer_type_hint, 543 range->offset, range->size, (BYTE *)data + range->offset)); 544 } 545 checkGLcall("glBufferSubData"); 546 } 547 548 static void buffer_conversion_upload(struct wined3d_buffer *buffer, struct wined3d_context *context) 549 { 550 unsigned int i, j, range_idx, start, end, vertex_count; 551 BYTE *data; 552 553 if (!wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM)) 554 { 555 ERR("Failed to load system memory.\n"); 556 return; 557 } 558 buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM; 559 560 /* Now for each vertex in the buffer that needs conversion. */ 561 vertex_count = buffer->resource.size / buffer->stride; 562 563 if (!(data = HeapAlloc(GetProcessHeap(), 0, buffer->resource.size))) 564 { 565 ERR("Out of memory.\n"); 566 return; 567 } 568 569 for (range_idx = 0; range_idx < buffer->modified_areas; ++range_idx) 570 { 571 start = buffer->maps[range_idx].offset; 572 end = start + buffer->maps[range_idx].size; 573 574 memcpy(data + start, (BYTE *)buffer->resource.heap_memory + start, end - start); 575 for (i = start / buffer->stride; i < min((end / buffer->stride) + 1, vertex_count); ++i) 576 { 577 for (j = 0; j < buffer->stride;) 578 { 579 switch (buffer->conversion_map[j]) 580 { 581 case CONV_NONE: 582 /* Done already */ 583 j += sizeof(DWORD); 584 break; 585 case CONV_D3DCOLOR: 586 j += fixup_d3dcolor((DWORD *) (data + i * buffer->stride + j)); 587 break; 588 case CONV_POSITIONT: 589 j += fixup_transformed_pos((struct wined3d_vec4 *) (data + i * buffer->stride + j)); 590 break; 591 default: 592 FIXME("Unimplemented conversion %d in shifted conversion.\n", buffer->conversion_map[j]); 593 ++j; 594 } 595 } 596 } 597 } 598 599 wined3d_buffer_upload_ranges(buffer, context, data, buffer->modified_areas, buffer->maps); 600 601 HeapFree(GetProcessHeap(), 0, data); 602 } 603 604 static BOOL wined3d_buffer_prepare_location(struct wined3d_buffer *buffer, 605 struct wined3d_context *context, DWORD location) 606 { 607 switch (location) 608 { 609 case WINED3D_LOCATION_SYSMEM: 610 if (buffer->resource.heap_memory) 611 return TRUE; 612 613 if (!wined3d_resource_allocate_sysmem(&buffer->resource)) 614 { 615 ERR("Failed to allocate system memory.\n"); 616 return FALSE; 617 } 618 return TRUE; 619 620 case WINED3D_LOCATION_BUFFER: 621 if (buffer->buffer_object) 622 return TRUE; 623 624 if (!(buffer->flags & WINED3D_BUFFER_USE_BO)) 625 { 626 WARN("Trying to create BO for buffer %p with no WINED3D_BUFFER_USE_BO.\n", buffer); 627 return FALSE; 628 } 629 return buffer_create_buffer_object(buffer, context); 630 631 default: 632 ERR("Invalid location %s.\n", wined3d_debug_location(location)); 633 return FALSE; 634 } 635 } 636 637 BOOL wined3d_buffer_load_location(struct wined3d_buffer *buffer, 638 struct wined3d_context *context, DWORD location) 639 { 640 const struct wined3d_gl_info *gl_info = context->gl_info; 641 642 TRACE("buffer %p, context %p, location %s.\n", 643 buffer, context, wined3d_debug_location(location)); 644 645 if (buffer->locations & location) 646 { 647 TRACE("Location (%#x) is already up to date.\n", location); 648 return TRUE; 649 } 650 651 if (!buffer->locations) 652 { 653 ERR("Buffer %p does not have any up to date location.\n", buffer); 654 wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_DISCARDED); 655 return wined3d_buffer_load_location(buffer, context, location); 656 } 657 658 TRACE("Current buffer location %s.\n", wined3d_debug_location(buffer->locations)); 659 660 if (!wined3d_buffer_prepare_location(buffer, context, location)) 661 return FALSE; 662 663 if (buffer->locations & WINED3D_LOCATION_DISCARDED) 664 { 665 TRACE("Buffer previously discarded, nothing to do.\n"); 666 wined3d_buffer_validate_location(buffer, location); 667 wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_DISCARDED); 668 return TRUE; 669 } 670 671 switch (location) 672 { 673 case WINED3D_LOCATION_SYSMEM: 674 buffer_bind(buffer, context); 675 GL_EXTCALL(glGetBufferSubData(buffer->buffer_type_hint, 0, buffer->resource.size, 676 buffer->resource.heap_memory)); 677 checkGLcall("buffer download"); 678 break; 679 680 case WINED3D_LOCATION_BUFFER: 681 if (!buffer->conversion_map) 682 wined3d_buffer_upload_ranges(buffer, context, buffer->resource.heap_memory, 683 buffer->modified_areas, buffer->maps); 684 else 685 buffer_conversion_upload(buffer, context); 686 break; 687 688 default: 689 ERR("Invalid location %s.\n", wined3d_debug_location(location)); 690 return FALSE; 691 } 692 693 wined3d_buffer_validate_location(buffer, location); 694 if (buffer->resource.heap_memory && location == WINED3D_LOCATION_BUFFER 695 && !(buffer->resource.usage & WINED3DUSAGE_DYNAMIC)) 696 wined3d_buffer_evict_sysmem(buffer); 697 698 return TRUE; 699 } 700 701 /* Context activation is done by the caller. */ 702 BYTE *wined3d_buffer_load_sysmem(struct wined3d_buffer *buffer, struct wined3d_context *context) 703 { 704 if (wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM)) 705 buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM; 706 return buffer->resource.heap_memory; 707 } 708 709 DWORD wined3d_buffer_get_memory(struct wined3d_buffer *buffer, 710 struct wined3d_bo_address *data, DWORD locations) 711 { 712 TRACE("buffer %p, data %p, locations %s.\n", 713 buffer, data, wined3d_debug_location(locations)); 714 715 if (locations & WINED3D_LOCATION_BUFFER) 716 { 717 data->buffer_object = buffer->buffer_object; 718 data->addr = NULL; 719 return WINED3D_LOCATION_BUFFER; 720 } 721 if (locations & WINED3D_LOCATION_SYSMEM) 722 { 723 data->buffer_object = 0; 724 data->addr = buffer->resource.heap_memory; 725 return WINED3D_LOCATION_SYSMEM; 726 } 727 728 ERR("Unexpected locations %s.\n", wined3d_debug_location(locations)); 729 data->buffer_object = 0; 730 data->addr = NULL; 731 return 0; 732 } 733 734 static void buffer_unload(struct wined3d_resource *resource) 735 { 736 struct wined3d_buffer *buffer = buffer_from_resource(resource); 737 738 TRACE("buffer %p.\n", buffer); 739 740 if (buffer->buffer_object) 741 { 742 struct wined3d_context *context; 743 744 context = context_acquire(resource->device, NULL, 0); 745 746 wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM); 747 wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_BUFFER); 748 buffer_destroy_buffer_object(buffer, context); 749 buffer_clear_dirty_areas(buffer); 750 751 context_release(context); 752 753 HeapFree(GetProcessHeap(), 0, buffer->conversion_map); 754 buffer->conversion_map = NULL; 755 buffer->stride = 0; 756 buffer->conversion_stride = 0; 757 buffer->flags &= ~WINED3D_BUFFER_HASDESC; 758 } 759 760 resource_unload(resource); 761 } 762 763 static void wined3d_buffer_drop_bo(struct wined3d_buffer *buffer) 764 { 765 buffer->flags &= ~WINED3D_BUFFER_USE_BO; 766 buffer_unload(&buffer->resource); 767 } 768 769 static void wined3d_buffer_destroy_object(void *object) 770 { 771 struct wined3d_buffer *buffer = object; 772 struct wined3d_context *context; 773 774 if (buffer->buffer_object) 775 { 776 context = context_acquire(buffer->resource.device, NULL, 0); 777 buffer_destroy_buffer_object(buffer, context); 778 context_release(context); 779 780 HeapFree(GetProcessHeap(), 0, buffer->conversion_map); 781 } 782 783 HeapFree(GetProcessHeap(), 0, buffer->maps); 784 HeapFree(GetProcessHeap(), 0, buffer); 785 } 786 787 ULONG CDECL wined3d_buffer_decref(struct wined3d_buffer *buffer) 788 { 789 ULONG refcount = InterlockedDecrement(&buffer->resource.ref); 790 791 TRACE("%p decreasing refcount to %u.\n", buffer, refcount); 792 793 if (!refcount) 794 { 795 buffer->resource.parent_ops->wined3d_object_destroyed(buffer->resource.parent); 796 resource_cleanup(&buffer->resource); 797 wined3d_cs_destroy_object(buffer->resource.device->cs, wined3d_buffer_destroy_object, buffer); 798 } 799 800 return refcount; 801 } 802 803 void * CDECL wined3d_buffer_get_parent(const struct wined3d_buffer *buffer) 804 { 805 TRACE("buffer %p.\n", buffer); 806 807 return buffer->resource.parent; 808 } 809 810 /* The caller provides a context and binds the buffer */ 811 static void buffer_sync_apple(struct wined3d_buffer *buffer, DWORD flags, const struct wined3d_gl_info *gl_info) 812 { 813 enum wined3d_fence_result ret; 814 HRESULT hr; 815 816 /* No fencing needs to be done if the app promises not to overwrite 817 * existing data. */ 818 if (flags & WINED3D_MAP_NOOVERWRITE) 819 return; 820 821 if (flags & WINED3D_MAP_DISCARD) 822 { 823 GL_EXTCALL(glBufferData(buffer->buffer_type_hint, buffer->resource.size, NULL, buffer->buffer_object_usage)); 824 checkGLcall("glBufferData"); 825 return; 826 } 827 828 if (!buffer->fence) 829 { 830 TRACE("Creating fence for buffer %p.\n", buffer); 831 832 if (FAILED(hr = wined3d_fence_create(buffer->resource.device, &buffer->fence))) 833 { 834 if (hr == WINED3DERR_NOTAVAILABLE) 835 FIXME("Fences not supported, dropping async buffer locks.\n"); 836 else 837 ERR("Failed to create fence, hr %#x.\n", hr); 838 goto drop_fence; 839 } 840 841 /* Since we don't know about old draws a glFinish is needed once */ 842 gl_info->gl_ops.gl.p_glFinish(); 843 return; 844 } 845 846 TRACE("Synchronizing buffer %p.\n", buffer); 847 ret = wined3d_fence_wait(buffer->fence, buffer->resource.device); 848 switch (ret) 849 { 850 case WINED3D_FENCE_NOT_STARTED: 851 case WINED3D_FENCE_OK: 852 /* All done */ 853 return; 854 855 case WINED3D_FENCE_WRONG_THREAD: 856 WARN("Cannot synchronize buffer lock due to a thread conflict.\n"); 857 goto drop_fence; 858 859 default: 860 ERR("wined3d_fence_wait() returned %u, dropping async buffer locks.\n", ret); 861 goto drop_fence; 862 } 863 864 drop_fence: 865 if (buffer->fence) 866 { 867 wined3d_fence_destroy(buffer->fence); 868 buffer->fence = NULL; 869 } 870 871 gl_info->gl_ops.gl.p_glFinish(); 872 GL_EXTCALL(glBufferParameteriAPPLE(buffer->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)); 873 checkGLcall("glBufferParameteriAPPLE(buffer->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)"); 874 buffer->flags &= ~WINED3D_BUFFER_APPLESYNC; 875 } 876 877 static void buffer_mark_used(struct wined3d_buffer *buffer) 878 { 879 buffer->flags &= ~WINED3D_BUFFER_DISCARD; 880 } 881 882 /* Context activation is done by the caller. */ 883 void wined3d_buffer_load(struct wined3d_buffer *buffer, struct wined3d_context *context, 884 const struct wined3d_state *state) 885 { 886 const struct wined3d_gl_info *gl_info = context->gl_info; 887 BOOL decl_changed = FALSE; 888 889 TRACE("buffer %p.\n", buffer); 890 891 if (buffer->resource.map_count) 892 { 893 WARN("Buffer is mapped, skipping preload.\n"); 894 return; 895 } 896 897 buffer_mark_used(buffer); 898 899 /* TODO: Make converting independent from VBOs */ 900 if (!(buffer->flags & WINED3D_BUFFER_USE_BO)) 901 { 902 /* Not doing any conversion */ 903 return; 904 } 905 906 if (!wined3d_buffer_prepare_location(buffer, context, WINED3D_LOCATION_BUFFER)) 907 { 908 ERR("Failed to prepare buffer location.\n"); 909 return; 910 } 911 912 /* Reading the declaration makes only sense if we have valid state information 913 * (i.e., if this function is called during draws). */ 914 if (state) 915 { 916 DWORD fixup_flags = 0; 917 918 if (!use_vs(state)) 919 { 920 if (!gl_info->supported[ARB_VERTEX_ARRAY_BGRA] && !context->d3d_info->ffp_generic_attributes) 921 fixup_flags |= WINED3D_BUFFER_FIXUP_D3DCOLOR; 922 if (!context->d3d_info->xyzrhw) 923 fixup_flags |= WINED3D_BUFFER_FIXUP_XYZRHW; 924 } 925 926 decl_changed = buffer_find_decl(buffer, &context->stream_info, state, fixup_flags); 927 buffer->flags |= WINED3D_BUFFER_HASDESC; 928 } 929 930 if (!decl_changed && !(buffer->flags & WINED3D_BUFFER_HASDESC && buffer_is_dirty(buffer))) 931 { 932 ++buffer->draw_count; 933 if (buffer->draw_count > VB_RESETDECLCHANGE) 934 buffer->decl_change_count = 0; 935 if (buffer->draw_count > VB_RESETFULLCONVS) 936 buffer->full_conversion_count = 0; 937 return; 938 } 939 940 /* If applications change the declaration over and over, reconverting all the time is a huge 941 * performance hit. So count the declaration changes and release the VBO if there are too many 942 * of them (and thus stop converting) 943 */ 944 if (decl_changed) 945 { 946 ++buffer->decl_change_count; 947 buffer->draw_count = 0; 948 949 if (buffer->decl_change_count > VB_MAXDECLCHANGES 950 || (buffer->conversion_map && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC))) 951 { 952 FIXME("Too many declaration changes or converting dynamic buffer, stopping converting.\n"); 953 wined3d_buffer_drop_bo(buffer); 954 return; 955 } 956 957 /* The declaration changed, reload the whole buffer. */ 958 WARN("Reloading buffer because of a vertex declaration change.\n"); 959 buffer_invalidate_bo_range(buffer, 0, 0); 960 } 961 else 962 { 963 /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that 964 * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without 965 * decl changes and reset the decl change count after a specific number of them 966 */ 967 if (buffer->conversion_map && buffer_is_fully_dirty(buffer)) 968 { 969 ++buffer->full_conversion_count; 970 if (buffer->full_conversion_count > VB_MAXFULLCONVERSIONS) 971 { 972 FIXME("Too many full buffer conversions, stopping converting.\n"); 973 wined3d_buffer_drop_bo(buffer); 974 return; 975 } 976 } 977 else 978 { 979 ++buffer->draw_count; 980 if (buffer->draw_count > VB_RESETDECLCHANGE) 981 buffer->decl_change_count = 0; 982 if (buffer->draw_count > VB_RESETFULLCONVS) 983 buffer->full_conversion_count = 0; 984 } 985 } 986 987 if (!wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_BUFFER)) 988 ERR("Failed to load buffer location.\n"); 989 } 990 991 struct wined3d_resource * CDECL wined3d_buffer_get_resource(struct wined3d_buffer *buffer) 992 { 993 TRACE("buffer %p.\n", buffer); 994 995 return &buffer->resource; 996 } 997 998 static HRESULT wined3d_buffer_map(struct wined3d_buffer *buffer, UINT offset, UINT size, BYTE **data, DWORD flags) 999 { 1000 struct wined3d_device *device = buffer->resource.device; 1001 struct wined3d_context *context; 1002 LONG count; 1003 BYTE *base; 1004 1005 TRACE("buffer %p, offset %u, size %u, data %p, flags %#x.\n", buffer, offset, size, data, flags); 1006 1007 count = ++buffer->resource.map_count; 1008 1009 if (buffer->buffer_object) 1010 { 1011 unsigned int dirty_offset = offset, dirty_size = size; 1012 1013 /* DISCARD invalidates the entire buffer, regardless of the specified 1014 * offset and size. Some applications also depend on the entire buffer 1015 * being uploaded in that case. Two such applications are Port Royale 1016 * and Darkstar One. */ 1017 if (flags & WINED3D_MAP_DISCARD) 1018 { 1019 dirty_offset = 0; 1020 dirty_size = 0; 1021 } 1022 1023 if (!(flags & (WINED3D_MAP_NOOVERWRITE | WINED3D_MAP_DISCARD | WINED3D_MAP_READONLY)) 1024 || ((flags & WINED3D_MAP_READONLY) && (buffer->locations & WINED3D_LOCATION_SYSMEM)) 1025 || buffer->flags & WINED3D_BUFFER_PIN_SYSMEM) 1026 { 1027 if (!(buffer->locations & WINED3D_LOCATION_SYSMEM)) 1028 { 1029 context = context_acquire(device, NULL, 0); 1030 wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM); 1031 context_release(context); 1032 } 1033 1034 if (!(flags & WINED3D_MAP_READONLY)) 1035 wined3d_buffer_invalidate_range(buffer, WINED3D_LOCATION_BUFFER, dirty_offset, dirty_size); 1036 } 1037 else 1038 { 1039 const struct wined3d_gl_info *gl_info; 1040 1041 context = context_acquire(device, NULL, 0); 1042 gl_info = context->gl_info; 1043 1044 if (flags & WINED3D_MAP_DISCARD) 1045 wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_BUFFER); 1046 else 1047 wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_BUFFER); 1048 1049 if (!(flags & WINED3D_MAP_READONLY)) 1050 buffer_invalidate_bo_range(buffer, dirty_offset, dirty_size); 1051 1052 if ((flags & WINED3D_MAP_DISCARD) && buffer->resource.heap_memory) 1053 wined3d_buffer_evict_sysmem(buffer); 1054 1055 if (count == 1) 1056 { 1057 buffer_bind(buffer, context); 1058 1059 /* Filter redundant WINED3D_MAP_DISCARD maps. The 3DMark2001 1060 * multitexture fill rate test seems to depend on this. When 1061 * we map a buffer with GL_MAP_INVALIDATE_BUFFER_BIT, the 1062 * driver is free to discard the previous contents of the 1063 * buffer. The r600g driver only does this when the buffer is 1064 * currently in use, while the proprietary NVIDIA driver 1065 * appears to do this unconditionally. */ 1066 if (buffer->flags & WINED3D_BUFFER_DISCARD) 1067 flags &= ~WINED3D_MAP_DISCARD; 1068 1069 if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) 1070 { 1071 GLbitfield mapflags = wined3d_resource_gl_map_flags(flags); 1072 buffer->map_ptr = GL_EXTCALL(glMapBufferRange(buffer->buffer_type_hint, 1073 0, buffer->resource.size, mapflags)); 1074 checkGLcall("glMapBufferRange"); 1075 } 1076 else 1077 { 1078 if (buffer->flags & WINED3D_BUFFER_APPLESYNC) 1079 buffer_sync_apple(buffer, flags, gl_info); 1080 buffer->map_ptr = GL_EXTCALL(glMapBuffer(buffer->buffer_type_hint, 1081 GL_READ_WRITE)); 1082 checkGLcall("glMapBuffer"); 1083 } 1084 1085 if (((DWORD_PTR)buffer->map_ptr) & (RESOURCE_ALIGNMENT - 1)) 1086 { 1087 WARN("Pointer %p is not %u byte aligned.\n", buffer->map_ptr, RESOURCE_ALIGNMENT); 1088 1089 GL_EXTCALL(glUnmapBuffer(buffer->buffer_type_hint)); 1090 checkGLcall("glUnmapBuffer"); 1091 buffer->map_ptr = NULL; 1092 1093 if (buffer->resource.usage & WINED3DUSAGE_DYNAMIC) 1094 { 1095 /* The extra copy is more expensive than not using VBOs at 1096 * all on the Nvidia Linux driver, which is the only driver 1097 * that returns unaligned pointers. 1098 */ 1099 TRACE("Dynamic buffer, dropping VBO.\n"); 1100 wined3d_buffer_drop_bo(buffer); 1101 } 1102 else 1103 { 1104 TRACE("Falling back to doublebuffered operation.\n"); 1105 wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM); 1106 buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM; 1107 } 1108 TRACE("New pointer is %p.\n", buffer->resource.heap_memory); 1109 } 1110 } 1111 1112 context_release(context); 1113 } 1114 1115 if (flags & WINED3D_MAP_DISCARD) 1116 buffer->flags |= WINED3D_BUFFER_DISCARD; 1117 } 1118 1119 base = buffer->map_ptr ? buffer->map_ptr : buffer->resource.heap_memory; 1120 *data = base + offset; 1121 1122 TRACE("Returning memory at %p (base %p, offset %u).\n", *data, base, offset); 1123 /* TODO: check Flags compatibility with buffer->currentDesc.Usage (see MSDN) */ 1124 1125 return WINED3D_OK; 1126 } 1127 1128 static void wined3d_buffer_unmap(struct wined3d_buffer *buffer) 1129 { 1130 ULONG i; 1131 1132 TRACE("buffer %p.\n", buffer); 1133 1134 /* In the case that the number of Unmap calls > the 1135 * number of Map calls, d3d returns always D3D_OK. 1136 * This is also needed to prevent Map from returning garbage on 1137 * the next call (this will happen if the lock_count is < 0). */ 1138 if (!buffer->resource.map_count) 1139 { 1140 WARN("Unmap called without a previous map call.\n"); 1141 return; 1142 } 1143 1144 if (--buffer->resource.map_count) 1145 { 1146 /* Delay loading the buffer until everything is unlocked */ 1147 TRACE("Ignoring unmap.\n"); 1148 return; 1149 } 1150 1151 if (buffer->map_ptr) 1152 { 1153 struct wined3d_device *device = buffer->resource.device; 1154 const struct wined3d_gl_info *gl_info; 1155 struct wined3d_context *context; 1156 1157 context = context_acquire(device, NULL, 0); 1158 gl_info = context->gl_info; 1159 1160 buffer_bind(buffer, context); 1161 1162 if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) 1163 { 1164 for (i = 0; i < buffer->modified_areas; ++i) 1165 { 1166 GL_EXTCALL(glFlushMappedBufferRange(buffer->buffer_type_hint, 1167 buffer->maps[i].offset, buffer->maps[i].size)); 1168 checkGLcall("glFlushMappedBufferRange"); 1169 } 1170 } 1171 else if (buffer->flags & WINED3D_BUFFER_APPLESYNC) 1172 { 1173 for (i = 0; i < buffer->modified_areas; ++i) 1174 { 1175 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(buffer->buffer_type_hint, 1176 buffer->maps[i].offset, buffer->maps[i].size)); 1177 checkGLcall("glFlushMappedBufferRangeAPPLE"); 1178 } 1179 } 1180 1181 GL_EXTCALL(glUnmapBuffer(buffer->buffer_type_hint)); 1182 if (wined3d_settings.strict_draw_ordering) 1183 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */ 1184 context_release(context); 1185 1186 buffer_clear_dirty_areas(buffer); 1187 buffer->map_ptr = NULL; 1188 } 1189 } 1190 1191 void wined3d_buffer_copy(struct wined3d_buffer *dst_buffer, unsigned int dst_offset, 1192 struct wined3d_buffer *src_buffer, unsigned int src_offset, unsigned int size) 1193 { 1194 struct wined3d_bo_address dst, src; 1195 struct wined3d_context *context; 1196 DWORD dst_location; 1197 1198 buffer_mark_used(dst_buffer); 1199 buffer_mark_used(src_buffer); 1200 1201 dst_location = wined3d_buffer_get_memory(dst_buffer, &dst, dst_buffer->locations); 1202 dst.addr += dst_offset; 1203 1204 wined3d_buffer_get_memory(src_buffer, &src, src_buffer->locations); 1205 src.addr += src_offset; 1206 1207 context = context_acquire(dst_buffer->resource.device, NULL, 0); 1208 context_copy_bo_address(context, &dst, dst_buffer->buffer_type_hint, 1209 &src, src_buffer->buffer_type_hint, size); 1210 context_release(context); 1211 1212 wined3d_buffer_invalidate_range(dst_buffer, ~dst_location, dst_offset, size); 1213 } 1214 1215 HRESULT wined3d_buffer_upload_data(struct wined3d_buffer *buffer, 1216 const struct wined3d_box *box, const void *data) 1217 { 1218 UINT offset, size; 1219 #if defined(STAGING_CSMT) 1220 DWORD flags = 0; 1221 #endif /* STAGING_CSMT */ 1222 HRESULT hr; 1223 BYTE *ptr; 1224 1225 if (box) 1226 { 1227 offset = box->left; 1228 size = box->right - box->left; 1229 } 1230 else 1231 { 1232 offset = 0; 1233 size = buffer->resource.size; 1234 } 1235 1236 #if !defined(STAGING_CSMT) 1237 if (FAILED(hr = wined3d_buffer_map(buffer, offset, size, &ptr, 0))) 1238 #else /* STAGING_CSMT */ 1239 if (offset == 0 && size == buffer->resource.size) 1240 flags = WINED3D_MAP_DISCARD; 1241 1242 if (FAILED(hr = wined3d_buffer_map(buffer, offset, size, &ptr, flags))) 1243 #endif /* STAGING_CSMT */ 1244 return hr; 1245 1246 memcpy(ptr, data, size); 1247 1248 wined3d_buffer_unmap(buffer); 1249 return WINED3D_OK; 1250 } 1251 1252 static ULONG buffer_resource_incref(struct wined3d_resource *resource) 1253 { 1254 return wined3d_buffer_incref(buffer_from_resource(resource)); 1255 } 1256 1257 static ULONG buffer_resource_decref(struct wined3d_resource *resource) 1258 { 1259 return wined3d_buffer_decref(buffer_from_resource(resource)); 1260 } 1261 1262 static void buffer_resource_preload(struct wined3d_resource *resource) 1263 { 1264 struct wined3d_context *context; 1265 1266 context = context_acquire(resource->device, NULL, 0); 1267 wined3d_buffer_load(buffer_from_resource(resource), context, NULL); 1268 context_release(context); 1269 } 1270 1271 static HRESULT buffer_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx, 1272 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags) 1273 { 1274 struct wined3d_buffer *buffer = buffer_from_resource(resource); 1275 UINT offset, size; 1276 1277 if (sub_resource_idx) 1278 { 1279 WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx); 1280 return E_INVALIDARG; 1281 } 1282 1283 if (box) 1284 { 1285 offset = box->left; 1286 size = box->right - box->left; 1287 } 1288 else 1289 { 1290 offset = size = 0; 1291 } 1292 1293 map_desc->row_pitch = map_desc->slice_pitch = buffer->desc.byte_width; 1294 return wined3d_buffer_map(buffer, offset, size, (BYTE **)&map_desc->data, flags); 1295 } 1296 1297 static HRESULT buffer_resource_sub_resource_map_info(struct wined3d_resource *resource, unsigned int sub_resource_idx, 1298 struct wined3d_map_info *info, DWORD flags) 1299 { 1300 struct wined3d_buffer *buffer = buffer_from_resource(resource); 1301 1302 if (sub_resource_idx) 1303 { 1304 WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx); 1305 return E_INVALIDARG; 1306 } 1307 1308 info->row_pitch = buffer->desc.byte_width; 1309 info->slice_pitch = buffer->desc.byte_width; 1310 info->size = buffer->resource.size; 1311 1312 return WINED3D_OK; 1313 } 1314 1315 static HRESULT buffer_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx) 1316 { 1317 if (sub_resource_idx) 1318 { 1319 WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx); 1320 return E_INVALIDARG; 1321 } 1322 1323 wined3d_buffer_unmap(buffer_from_resource(resource)); 1324 return WINED3D_OK; 1325 } 1326 1327 static const struct wined3d_resource_ops buffer_resource_ops = 1328 { 1329 buffer_resource_incref, 1330 buffer_resource_decref, 1331 buffer_resource_preload, 1332 buffer_unload, 1333 buffer_resource_sub_resource_map, 1334 buffer_resource_sub_resource_map_info, 1335 buffer_resource_sub_resource_unmap, 1336 }; 1337 1338 static GLenum buffer_type_hint_from_bind_flags(const struct wined3d_gl_info *gl_info, 1339 unsigned int bind_flags) 1340 { 1341 if (bind_flags == WINED3D_BIND_INDEX_BUFFER) 1342 return GL_ELEMENT_ARRAY_BUFFER; 1343 1344 if (bind_flags & (WINED3D_BIND_SHADER_RESOURCE | WINED3D_BIND_UNORDERED_ACCESS) 1345 && gl_info->supported[ARB_TEXTURE_BUFFER_OBJECT]) 1346 return GL_TEXTURE_BUFFER; 1347 1348 if (bind_flags & WINED3D_BIND_CONSTANT_BUFFER) 1349 return GL_UNIFORM_BUFFER; 1350 1351 if (bind_flags & WINED3D_BIND_STREAM_OUTPUT) 1352 return GL_TRANSFORM_FEEDBACK_BUFFER; 1353 1354 if (bind_flags & ~(WINED3D_BIND_VERTEX_BUFFER | WINED3D_BIND_INDEX_BUFFER)) 1355 FIXME("Unhandled bind flags %#x.\n", bind_flags); 1356 1357 return GL_ARRAY_BUFFER; 1358 } 1359 1360 static HRESULT buffer_init(struct wined3d_buffer *buffer, struct wined3d_device *device, 1361 UINT size, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, unsigned int bind_flags, 1362 const struct wined3d_sub_resource_data *data, void *parent, const struct wined3d_parent_ops *parent_ops) 1363 { 1364 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 1365 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id, usage); 1366 BOOL dynamic_buffer_ok; 1367 HRESULT hr; 1368 1369 if (!size) 1370 { 1371 WARN("Size 0 requested, returning E_INVALIDARG.\n"); 1372 return E_INVALIDARG; 1373 } 1374 1375 if (bind_flags & WINED3D_BIND_CONSTANT_BUFFER && size & (WINED3D_CONSTANT_BUFFER_ALIGNMENT - 1)) 1376 { 1377 WARN("Size %#x is not suitably aligned for constant buffers.\n", size); 1378 return E_INVALIDARG; 1379 } 1380 1381 if (data && !data->data) 1382 { 1383 WARN("Invalid sub-resource data specified.\n"); 1384 return E_INVALIDARG; 1385 } 1386 1387 hr = resource_init(&buffer->resource, device, WINED3D_RTYPE_BUFFER, format, 1388 WINED3D_MULTISAMPLE_NONE, 0, usage, pool, size, 1, 1, size, parent, parent_ops, &buffer_resource_ops); 1389 if (FAILED(hr)) 1390 { 1391 WARN("Failed to initialize resource, hr %#x.\n", hr); 1392 return hr; 1393 } 1394 buffer->buffer_type_hint = buffer_type_hint_from_bind_flags(gl_info, bind_flags); 1395 buffer->bind_flags = bind_flags; 1396 buffer->locations = WINED3D_LOCATION_SYSMEM; 1397 1398 TRACE("buffer %p, size %#x, usage %#x, format %s, memory @ %p.\n", 1399 buffer, buffer->resource.size, buffer->resource.usage, 1400 debug_d3dformat(buffer->resource.format->id), buffer->resource.heap_memory); 1401 1402 if (device->create_parms.flags & WINED3DCREATE_SOFTWARE_VERTEXPROCESSING || pool == WINED3D_POOL_MANAGED) 1403 { 1404 /* SWvp and managed buffers always return the same pointer in buffer 1405 * maps and retain data in DISCARD maps. Keep a system memory copy of 1406 * the buffer to provide the same behavior to the application. */ 1407 TRACE("Using doublebuffer mode.\n"); 1408 buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM; 1409 } 1410 1411 /* Observations show that draw_primitive_immediate_mode() is faster on 1412 * dynamic vertex buffers than converting + draw_primitive_arrays(). 1413 * (Half-Life 2 and others.) */ 1414 dynamic_buffer_ok = gl_info->supported[APPLE_FLUSH_BUFFER_RANGE] || gl_info->supported[ARB_MAP_BUFFER_RANGE]; 1415 1416 if (!gl_info->supported[ARB_VERTEX_BUFFER_OBJECT]) 1417 { 1418 TRACE("Not creating a BO because GL_ARB_vertex_buffer is not supported.\n"); 1419 } 1420 else if (buffer->resource.pool == WINED3D_POOL_SYSTEM_MEM) 1421 { 1422 TRACE("Not creating a BO because the buffer is in system memory.\n"); 1423 } 1424 else if (!dynamic_buffer_ok && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)) 1425 { 1426 TRACE("Not creating a BO because the buffer has dynamic usage and no GL support.\n"); 1427 } 1428 else 1429 { 1430 buffer->flags |= WINED3D_BUFFER_USE_BO; 1431 } 1432 1433 if (!(buffer->maps = HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer->maps)))) 1434 { 1435 ERR("Out of memory.\n"); 1436 buffer_unload(&buffer->resource); 1437 resource_cleanup(&buffer->resource); 1438 wined3d_resource_wait_idle(&buffer->resource); 1439 return E_OUTOFMEMORY; 1440 } 1441 buffer->maps_size = 1; 1442 1443 if (data) 1444 wined3d_device_update_sub_resource(device, &buffer->resource, 1445 0, NULL, data->data, data->row_pitch, data->slice_pitch); 1446 1447 return WINED3D_OK; 1448 } 1449 1450 HRESULT CDECL wined3d_buffer_create(struct wined3d_device *device, const struct wined3d_buffer_desc *desc, 1451 const struct wined3d_sub_resource_data *data, void *parent, const struct wined3d_parent_ops *parent_ops, 1452 struct wined3d_buffer **buffer) 1453 { 1454 struct wined3d_buffer *object; 1455 enum wined3d_pool pool; 1456 HRESULT hr; 1457 1458 TRACE("device %p, desc %p, data %p, parent %p, parent_ops %p, buffer %p.\n", 1459 device, desc, data, parent, parent_ops, buffer); 1460 1461 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) 1462 return E_OUTOFMEMORY; 1463 1464 FIXME("Ignoring access flags (pool).\n"); 1465 1466 /* Some applications map the whole buffer even if they 1467 * only update a small portion of it. If we pin such a 1468 * buffer into system memory things get very slow as 1469 * we upload the whole buffer even though just parts of 1470 * it changed. Most drivers can handle this case more 1471 * efficient using the OpenGL map functions. Applications 1472 * affected by this problem are Banished and Witcher 3. 1473 */ 1474 if (desc->byte_width > 0x10000) 1475 pool = WINED3D_POOL_DEFAULT; 1476 else 1477 pool = WINED3D_POOL_MANAGED; 1478 1479 if (FAILED(hr = buffer_init(object, device, desc->byte_width, desc->usage, WINED3DFMT_UNKNOWN, 1480 pool, desc->bind_flags, data, parent, parent_ops))) 1481 { 1482 WARN("Failed to initialize buffer, hr %#x.\n", hr); 1483 HeapFree(GetProcessHeap(), 0, object); 1484 return hr; 1485 } 1486 object->desc = *desc; 1487 1488 TRACE("Created buffer %p.\n", object); 1489 1490 *buffer = object; 1491 1492 return WINED3D_OK; 1493 } 1494 1495 HRESULT CDECL wined3d_buffer_create_vb(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool, 1496 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer) 1497 { 1498 struct wined3d_buffer *object; 1499 HRESULT hr; 1500 1501 TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n", 1502 device, size, usage, pool, parent, parent_ops, buffer); 1503 1504 if (pool == WINED3D_POOL_SCRATCH) 1505 { 1506 /* The d3d9 tests shows that this is not allowed. It doesn't make much 1507 * sense anyway, SCRATCH buffers wouldn't be usable anywhere. */ 1508 WARN("Vertex buffer in WINED3D_POOL_SCRATCH requested, returning WINED3DERR_INVALIDCALL.\n"); 1509 *buffer = NULL; 1510 return WINED3DERR_INVALIDCALL; 1511 } 1512 1513 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); 1514 if (!object) 1515 { 1516 *buffer = NULL; 1517 return WINED3DERR_OUTOFVIDEOMEMORY; 1518 } 1519 1520 hr = buffer_init(object, device, size, usage, WINED3DFMT_UNKNOWN, 1521 pool, WINED3D_BIND_VERTEX_BUFFER, NULL, parent, parent_ops); 1522 if (FAILED(hr)) 1523 { 1524 WARN("Failed to initialize buffer, hr %#x.\n", hr); 1525 HeapFree(GetProcessHeap(), 0, object); 1526 return hr; 1527 } 1528 1529 TRACE("Created buffer %p.\n", object); 1530 *buffer = object; 1531 1532 return WINED3D_OK; 1533 } 1534 1535 HRESULT CDECL wined3d_buffer_create_ib(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool, 1536 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer) 1537 { 1538 struct wined3d_buffer *object; 1539 HRESULT hr; 1540 1541 TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n", 1542 device, size, usage, pool, parent, parent_ops, buffer); 1543 1544 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); 1545 if (!object) 1546 { 1547 *buffer = NULL; 1548 return WINED3DERR_OUTOFVIDEOMEMORY; 1549 } 1550 1551 hr = buffer_init(object, device, size, usage | WINED3DUSAGE_STATICDECL, 1552 WINED3DFMT_UNKNOWN, pool, WINED3D_BIND_INDEX_BUFFER, NULL, 1553 parent, parent_ops); 1554 if (FAILED(hr)) 1555 { 1556 WARN("Failed to initialize buffer, hr %#x\n", hr); 1557 HeapFree(GetProcessHeap(), 0, object); 1558 return hr; 1559 } 1560 1561 TRACE("Created buffer %p.\n", object); 1562 *buffer = object; 1563 1564 return WINED3D_OK; 1565 } 1566