Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenbuffer.c
Go to the documentation of this file.
00001 /* 00002 * Copyright 2002-2005 Jason Edmeades 00003 * Copyright 2002-2005 Raphael Junqueira 00004 * Copyright 2004 Christian Costa 00005 * Copyright 2005 Oliver Stieber 00006 * Copyright 2007-2010 Stefan Dösinger for CodeWeavers 00007 * Copyright 2009-2010 Henri Verbeet for CodeWeavers 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00022 * 00023 */ 00024 00025 #include "config.h" 00026 #include "wine/port.h" 00027 00028 #include "wined3d_private.h" 00029 00030 WINE_DEFAULT_DEBUG_CHANNEL(d3d); 00031 00032 #define VB_MAXDECLCHANGES 100 /* After that number of decl changes we stop converting */ 00033 #define VB_RESETDECLCHANGE 1000 /* Reset the decl changecount after that number of draws */ 00034 #define VB_MAXFULLCONVERSIONS 5 /* Number of full conversions before we stop converting */ 00035 #define VB_RESETFULLCONVS 20 /* Reset full conversion counts after that number of draws */ 00036 00037 static inline BOOL buffer_add_dirty_area(struct wined3d_buffer *This, UINT offset, UINT size) 00038 { 00039 if (!This->buffer_object) return TRUE; 00040 00041 if (This->maps_size <= This->modified_areas) 00042 { 00043 void *new = HeapReAlloc(GetProcessHeap(), 0, This->maps, 00044 This->maps_size * 2 * sizeof(*This->maps)); 00045 if (!new) 00046 { 00047 ERR("Out of memory\n"); 00048 return FALSE; 00049 } 00050 else 00051 { 00052 This->maps = new; 00053 This->maps_size *= 2; 00054 } 00055 } 00056 00057 if(offset > This->resource.size || offset + size > This->resource.size) 00058 { 00059 WARN("Invalid range dirtified, marking entire buffer dirty\n"); 00060 offset = 0; 00061 size = This->resource.size; 00062 } 00063 else if(!offset && !size) 00064 { 00065 size = This->resource.size; 00066 } 00067 00068 This->maps[This->modified_areas].offset = offset; 00069 This->maps[This->modified_areas].size = size; 00070 This->modified_areas++; 00071 return TRUE; 00072 } 00073 00074 static inline void buffer_clear_dirty_areas(struct wined3d_buffer *This) 00075 { 00076 This->modified_areas = 0; 00077 } 00078 00079 static BOOL buffer_is_dirty(const struct wined3d_buffer *buffer) 00080 { 00081 return !!buffer->modified_areas; 00082 } 00083 00084 static BOOL buffer_is_fully_dirty(const struct wined3d_buffer *buffer) 00085 { 00086 unsigned int i; 00087 00088 for (i = 0; i < buffer->modified_areas; ++i) 00089 { 00090 if (!buffer->maps[i].offset && buffer->maps[i].size == buffer->resource.size) 00091 return TRUE; 00092 } 00093 return FALSE; 00094 } 00095 00096 /* Context activation is done by the caller */ 00097 static void delete_gl_buffer(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info) 00098 { 00099 if(!This->buffer_object) return; 00100 00101 ENTER_GL(); 00102 GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object)); 00103 checkGLcall("glDeleteBuffersARB"); 00104 LEAVE_GL(); 00105 This->buffer_object = 0; 00106 00107 if(This->query) 00108 { 00109 wined3d_event_query_destroy(This->query); 00110 This->query = NULL; 00111 } 00112 This->flags &= ~WINED3D_BUFFER_APPLESYNC; 00113 } 00114 00115 /* Context activation is done by the caller. */ 00116 static void buffer_create_buffer_object(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info) 00117 { 00118 GLenum error, gl_usage; 00119 00120 TRACE("Creating an OpenGL vertex buffer object for wined3d_buffer %p with usage %s.\n", 00121 This, debug_d3dusage(This->resource.usage)); 00122 00123 ENTER_GL(); 00124 00125 /* Make sure that the gl error is cleared. Do not use checkGLcall 00126 * here because checkGLcall just prints a fixme and continues. However, 00127 * if an error during VBO creation occurs we can fall back to non-vbo operation 00128 * with full functionality(but performance loss) 00129 */ 00130 while (glGetError() != GL_NO_ERROR); 00131 00132 /* Basically the FVF parameter passed to CreateVertexBuffer is no good. 00133 * The vertex declaration from the device determines how the data in the 00134 * buffer is interpreted. This means that on each draw call the buffer has 00135 * to be verified to check if the rhw and color values are in the correct 00136 * format. */ 00137 00138 GL_EXTCALL(glGenBuffersARB(1, &This->buffer_object)); 00139 error = glGetError(); 00140 if (!This->buffer_object || error != GL_NO_ERROR) 00141 { 00142 ERR("Failed to create a VBO with error %s (%#x)\n", debug_glerror(error), error); 00143 LEAVE_GL(); 00144 goto fail; 00145 } 00146 00147 if (This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB) 00148 device_invalidate_state(This->resource.device, STATE_INDEXBUFFER); 00149 GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); 00150 error = glGetError(); 00151 if (error != GL_NO_ERROR) 00152 { 00153 ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error), error); 00154 LEAVE_GL(); 00155 goto fail; 00156 } 00157 00158 /* Don't use static, because dx apps tend to update the buffer 00159 * quite often even if they specify 0 usage. 00160 */ 00161 if(This->resource.usage & WINED3DUSAGE_DYNAMIC) 00162 { 00163 TRACE("Gl usage = GL_STREAM_DRAW_ARB\n"); 00164 gl_usage = GL_STREAM_DRAW_ARB; 00165 00166 if(gl_info->supported[APPLE_FLUSH_BUFFER_RANGE]) 00167 { 00168 GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE)); 00169 checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE)"); 00170 This->flags |= WINED3D_BUFFER_FLUSH; 00171 00172 GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE)); 00173 checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE)"); 00174 This->flags |= WINED3D_BUFFER_APPLESYNC; 00175 } 00176 /* No setup is needed here for GL_ARB_map_buffer_range */ 00177 } 00178 else 00179 { 00180 TRACE("Gl usage = GL_DYNAMIC_DRAW_ARB\n"); 00181 gl_usage = GL_DYNAMIC_DRAW_ARB; 00182 } 00183 00184 /* Reserve memory for the buffer. The amount of data won't change 00185 * so we are safe with calling glBufferData once and 00186 * calling glBufferSubData on updates. Upload the actual data in case 00187 * we're not double buffering, so we can release the heap mem afterwards 00188 */ 00189 GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, This->resource.allocatedMemory, gl_usage)); 00190 error = glGetError(); 00191 LEAVE_GL(); 00192 if (error != GL_NO_ERROR) 00193 { 00194 ERR("glBufferDataARB failed with error %s (%#x)\n", debug_glerror(error), error); 00195 goto fail; 00196 } 00197 00198 This->buffer_object_size = This->resource.size; 00199 This->buffer_object_usage = gl_usage; 00200 00201 if(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) 00202 { 00203 if(!buffer_add_dirty_area(This, 0, 0)) 00204 { 00205 ERR("buffer_add_dirty_area failed, this is not expected\n"); 00206 goto fail; 00207 } 00208 } 00209 else 00210 { 00211 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory); 00212 This->resource.allocatedMemory = NULL; 00213 This->resource.heapMemory = NULL; 00214 } 00215 00216 return; 00217 00218 fail: 00219 /* Clean up all vbo init, but continue because we can work without a vbo :-) */ 00220 ERR("Failed to create a vertex buffer object. Continuing, but performance issues may occur\n"); 00221 delete_gl_buffer(This, gl_info); 00222 buffer_clear_dirty_areas(This); 00223 } 00224 00225 static BOOL buffer_process_converted_attribute(struct wined3d_buffer *This, 00226 const enum wined3d_buffer_conversion_type conversion_type, 00227 const struct wined3d_stream_info_element *attrib, DWORD *stride_this_run) 00228 { 00229 DWORD attrib_size; 00230 BOOL ret = FALSE; 00231 unsigned int i; 00232 DWORD_PTR data; 00233 00234 /* Check for some valid situations which cause us pain. One is if the buffer is used for 00235 * constant attributes(stride = 0), the other one is if the buffer is used on two streams 00236 * with different strides. In the 2nd case we might have to drop conversion entirely, 00237 * it is possible that the same bytes are once read as FLOAT2 and once as UBYTE4N. 00238 */ 00239 if (!attrib->stride) 00240 { 00241 FIXME("%s used with stride 0, let's hope we get the vertex stride from somewhere else\n", 00242 debug_d3dformat(attrib->format->id)); 00243 } 00244 else if(attrib->stride != *stride_this_run && *stride_this_run) 00245 { 00246 FIXME("Got two concurrent strides, %d and %d\n", attrib->stride, *stride_this_run); 00247 } 00248 else 00249 { 00250 *stride_this_run = attrib->stride; 00251 if (This->stride != *stride_this_run) 00252 { 00253 /* We rely that this happens only on the first converted attribute that is found, 00254 * if at all. See above check 00255 */ 00256 TRACE("Reconverting because converted attributes occur, and the stride changed\n"); 00257 This->stride = *stride_this_run; 00258 HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, This->conversion_map); 00259 This->conversion_map = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 00260 sizeof(*This->conversion_map) * This->stride); 00261 ret = TRUE; 00262 } 00263 } 00264 00265 data = ((DWORD_PTR)attrib->data.addr) % This->stride; 00266 attrib_size = attrib->format->component_count * attrib->format->component_size; 00267 for (i = 0; i < attrib_size; ++i) 00268 { 00269 DWORD_PTR idx = (data + i) % This->stride; 00270 if (This->conversion_map[idx] != conversion_type) 00271 { 00272 TRACE("Byte %ld in vertex changed\n", idx); 00273 TRACE("It was type %d, is %d now\n", This->conversion_map[idx], conversion_type); 00274 ret = TRUE; 00275 This->conversion_map[idx] = conversion_type; 00276 } 00277 } 00278 00279 return ret; 00280 } 00281 00282 static BOOL buffer_check_attribute(struct wined3d_buffer *This, const struct wined3d_stream_info *si, 00283 UINT attrib_idx, const BOOL check_d3dcolor, const BOOL is_ffp_position, const BOOL is_ffp_color, 00284 DWORD *stride_this_run) 00285 { 00286 const struct wined3d_stream_info_element *attrib = &si->elements[attrib_idx]; 00287 enum wined3d_format_id format; 00288 BOOL ret = FALSE; 00289 00290 /* Ignore attributes that do not have our vbo. After that check we can be sure that the attribute is 00291 * there, on nonexistent attribs the vbo is 0. 00292 */ 00293 if (!(si->use_map & (1 << attrib_idx)) 00294 || attrib->data.buffer_object != This->buffer_object) 00295 return FALSE; 00296 00297 format = attrib->format->id; 00298 /* Look for newly appeared conversion */ 00299 if (check_d3dcolor && format == WINED3DFMT_B8G8R8A8_UNORM) 00300 { 00301 ret = buffer_process_converted_attribute(This, CONV_D3DCOLOR, attrib, stride_this_run); 00302 00303 if (!is_ffp_color) FIXME("Test for non-color fixed function WINED3DFMT_B8G8R8A8_UNORM format\n"); 00304 } 00305 else if (is_ffp_position && format == WINED3DFMT_R32G32B32A32_FLOAT) 00306 { 00307 ret = buffer_process_converted_attribute(This, CONV_POSITIONT, attrib, stride_this_run); 00308 } 00309 else if (This->conversion_map) 00310 { 00311 ret = buffer_process_converted_attribute(This, CONV_NONE, attrib, stride_this_run); 00312 } 00313 00314 return ret; 00315 } 00316 00317 static BOOL buffer_find_decl(struct wined3d_buffer *This) 00318 { 00319 struct wined3d_device *device = This->resource.device; 00320 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 00321 const struct wined3d_stream_info *si = &device->strided_streams; 00322 const struct wined3d_state *state = &device->stateBlock->state; 00323 UINT stride_this_run = 0; 00324 BOOL ret = FALSE; 00325 BOOL support_d3dcolor = gl_info->supported[ARB_VERTEX_ARRAY_BGRA]; 00326 00327 /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer. 00328 * Once we have our declaration there is no need to look it up again. Index buffers also never need 00329 * conversion, so once the (empty) conversion structure is created don't bother checking again 00330 */ 00331 if (This->flags & WINED3D_BUFFER_HASDESC) 00332 { 00333 if(This->resource.usage & WINED3DUSAGE_STATICDECL) return FALSE; 00334 } 00335 00336 if (use_vs(state)) 00337 { 00338 TRACE("Vertex shaders used, no VBO conversion is needed\n"); 00339 if(This->conversion_map) 00340 { 00341 HeapFree(GetProcessHeap(), 0, This->conversion_map); 00342 This->conversion_map = NULL; 00343 This->stride = 0; 00344 return TRUE; 00345 } 00346 00347 return FALSE; 00348 } 00349 00350 TRACE("Finding vertex buffer conversion information\n"); 00351 /* Certain declaration types need some fixups before we can pass them to 00352 * opengl. This means D3DCOLOR attributes with fixed function vertex 00353 * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if 00354 * GL_ARB_half_float_vertex is not supported. 00355 * 00356 * Note for d3d8 and d3d9: 00357 * The vertex buffer FVF doesn't help with finding them, we have to use 00358 * the decoded vertex declaration and pick the things that concern the 00359 * current buffer. A problem with this is that this can change between 00360 * draws, so we have to validate the information and reprocess the buffer 00361 * if it changes, and avoid false positives for performance reasons. 00362 * WineD3D doesn't even know the vertex buffer any more, it is managed 00363 * by the client libraries and passed to SetStreamSource and ProcessVertices 00364 * as needed. 00365 * 00366 * We have to distinguish between vertex shaders and fixed function to 00367 * pick the way we access the strided vertex information. 00368 * 00369 * This code sets up a per-byte array with the size of the detected 00370 * stride of the arrays in the buffer. For each byte we have a field 00371 * that marks the conversion needed on this byte. For example, the 00372 * following declaration with fixed function vertex processing: 00373 * 00374 * POSITIONT, FLOAT4 00375 * NORMAL, FLOAT3 00376 * DIFFUSE, FLOAT16_4 00377 * SPECULAR, D3DCOLOR 00378 * 00379 * Will result in 00380 * { POSITIONT }{ NORMAL }{ DIFFUSE }{SPECULAR } 00381 * [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] 00382 * 00383 * Where in this example map P means 4 component position conversion, 0 00384 * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR 00385 * conversion (red / blue swizzle). 00386 * 00387 * If we're doing conversion and the stride changes we have to reconvert 00388 * the whole buffer. Note that we do not mind if the semantic changes, 00389 * we only care for the conversion type. So if the NORMAL is replaced 00390 * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced 00391 * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some 00392 * conversion types depend on the semantic as well, for example a FLOAT4 00393 * texcoord needs no conversion while a FLOAT4 positiont needs one 00394 */ 00395 00396 ret = buffer_check_attribute(This, si, WINED3D_FFP_POSITION, 00397 TRUE, TRUE, FALSE, &stride_this_run) || ret; 00398 ret = buffer_check_attribute(This, si, WINED3D_FFP_NORMAL, 00399 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00400 ret = buffer_check_attribute(This, si, WINED3D_FFP_DIFFUSE, 00401 !support_d3dcolor, FALSE, TRUE, &stride_this_run) || ret; 00402 ret = buffer_check_attribute(This, si, WINED3D_FFP_SPECULAR, 00403 !support_d3dcolor, FALSE, TRUE, &stride_this_run) || ret; 00404 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD0, 00405 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00406 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD1, 00407 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00408 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD2, 00409 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00410 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD3, 00411 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00412 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD4, 00413 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00414 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD5, 00415 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00416 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD6, 00417 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00418 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD7, 00419 TRUE, FALSE, FALSE, &stride_this_run) || ret; 00420 00421 if (!stride_this_run && This->conversion_map) 00422 { 00423 /* Sanity test */ 00424 if (!ret) ERR("no converted attributes found, old conversion map exists, and no declaration change?\n"); 00425 HeapFree(GetProcessHeap(), 0, This->conversion_map); 00426 This->conversion_map = NULL; 00427 This->stride = 0; 00428 } 00429 00430 if (ret) TRACE("Conversion information changed\n"); 00431 00432 return ret; 00433 } 00434 00435 static inline void fixup_d3dcolor(DWORD *dst_color) 00436 { 00437 DWORD src_color = *dst_color; 00438 00439 /* Color conversion like in drawStridedSlow. watch out for little endianity 00440 * If we want that stuff to work on big endian machines too we have to consider more things 00441 * 00442 * 0xff000000: Alpha mask 00443 * 0x00ff0000: Blue mask 00444 * 0x0000ff00: Green mask 00445 * 0x000000ff: Red mask 00446 */ 00447 *dst_color = 0; 00448 *dst_color |= (src_color & 0xff00ff00); /* Alpha Green */ 00449 *dst_color |= (src_color & 0x00ff0000) >> 16; /* Red */ 00450 *dst_color |= (src_color & 0x000000ff) << 16; /* Blue */ 00451 } 00452 00453 static inline void fixup_transformed_pos(float *p) 00454 { 00455 /* rhw conversion like in position_float4(). */ 00456 if (p[3] != 1.0f && p[3] != 0.0f) 00457 { 00458 float w = 1.0f / p[3]; 00459 p[0] *= w; 00460 p[1] *= w; 00461 p[2] *= w; 00462 p[3] = w; 00463 } 00464 } 00465 00466 /* Context activation is done by the caller. */ 00467 void buffer_get_memory(struct wined3d_buffer *buffer, const struct wined3d_gl_info *gl_info, 00468 struct wined3d_bo_address *data) 00469 { 00470 data->buffer_object = buffer->buffer_object; 00471 if (!buffer->buffer_object) 00472 { 00473 if (buffer->flags & WINED3D_BUFFER_CREATEBO) 00474 { 00475 buffer_create_buffer_object(buffer, gl_info); 00476 buffer->flags &= ~WINED3D_BUFFER_CREATEBO; 00477 if (buffer->buffer_object) 00478 { 00479 data->buffer_object = buffer->buffer_object; 00480 data->addr = NULL; 00481 return; 00482 } 00483 } 00484 data->addr = buffer->resource.allocatedMemory; 00485 } 00486 else 00487 { 00488 data->addr = NULL; 00489 } 00490 } 00491 00492 ULONG CDECL wined3d_buffer_incref(struct wined3d_buffer *buffer) 00493 { 00494 ULONG refcount = InterlockedIncrement(&buffer->resource.ref); 00495 00496 TRACE("%p increasing refcount to %u.\n", buffer, refcount); 00497 00498 return refcount; 00499 } 00500 00501 /* Context activation is done by the caller. */ 00502 BYTE *buffer_get_sysmem(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info) 00503 { 00504 /* AllocatedMemory exists if the buffer is double buffered or has no buffer object at all */ 00505 if(This->resource.allocatedMemory) return This->resource.allocatedMemory; 00506 00507 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT); 00508 This->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1)); 00509 00510 if (This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB) 00511 device_invalidate_state(This->resource.device, STATE_INDEXBUFFER); 00512 00513 ENTER_GL(); 00514 GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); 00515 GL_EXTCALL(glGetBufferSubDataARB(This->buffer_type_hint, 0, This->resource.size, This->resource.allocatedMemory)); 00516 LEAVE_GL(); 00517 This->flags |= WINED3D_BUFFER_DOUBLEBUFFER; 00518 00519 return This->resource.allocatedMemory; 00520 } 00521 00522 /* Do not call while under the GL lock. */ 00523 static void buffer_unload(struct wined3d_resource *resource) 00524 { 00525 struct wined3d_buffer *buffer = buffer_from_resource(resource); 00526 00527 TRACE("buffer %p.\n", buffer); 00528 00529 if (buffer->buffer_object) 00530 { 00531 struct wined3d_device *device = resource->device; 00532 struct wined3d_context *context; 00533 00534 context = context_acquire(device, NULL); 00535 00536 /* Download the buffer, but don't permanently enable double buffering */ 00537 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER)) 00538 { 00539 buffer_get_sysmem(buffer, context->gl_info); 00540 buffer->flags &= ~WINED3D_BUFFER_DOUBLEBUFFER; 00541 } 00542 00543 delete_gl_buffer(buffer, context->gl_info); 00544 buffer->flags |= WINED3D_BUFFER_CREATEBO; /* Recreate the buffer object next load */ 00545 buffer_clear_dirty_areas(buffer); 00546 00547 context_release(context); 00548 00549 HeapFree(GetProcessHeap(), 0, buffer->conversion_map); 00550 buffer->conversion_map = NULL; 00551 buffer->stride = 0; 00552 buffer->conversion_stride = 0; 00553 buffer->flags &= ~WINED3D_BUFFER_HASDESC; 00554 } 00555 00556 resource_unload(resource); 00557 } 00558 00559 /* Do not call while under the GL lock. */ 00560 ULONG CDECL wined3d_buffer_decref(struct wined3d_buffer *buffer) 00561 { 00562 ULONG refcount = InterlockedDecrement(&buffer->resource.ref); 00563 00564 TRACE("%p decreasing refcount to %u.\n", buffer, refcount); 00565 00566 if (!refcount) 00567 { 00568 buffer_unload(&buffer->resource); 00569 resource_cleanup(&buffer->resource); 00570 buffer->resource.parent_ops->wined3d_object_destroyed(buffer->resource.parent); 00571 HeapFree(GetProcessHeap(), 0, buffer->maps); 00572 HeapFree(GetProcessHeap(), 0, buffer); 00573 } 00574 00575 return refcount; 00576 } 00577 00578 void * CDECL wined3d_buffer_get_parent(const struct wined3d_buffer *buffer) 00579 { 00580 TRACE("buffer %p.\n", buffer); 00581 00582 return buffer->resource.parent; 00583 } 00584 00585 DWORD CDECL wined3d_buffer_set_priority(struct wined3d_buffer *buffer, DWORD priority) 00586 { 00587 return resource_set_priority(&buffer->resource, priority); 00588 } 00589 00590 DWORD CDECL wined3d_buffer_get_priority(const struct wined3d_buffer *buffer) 00591 { 00592 return resource_get_priority(&buffer->resource); 00593 } 00594 00595 /* The caller provides a context and binds the buffer */ 00596 static void buffer_sync_apple(struct wined3d_buffer *This, DWORD flags, const struct wined3d_gl_info *gl_info) 00597 { 00598 enum wined3d_event_query_result ret; 00599 00600 /* No fencing needs to be done if the app promises not to overwrite 00601 * existing data */ 00602 if(flags & WINED3DLOCK_NOOVERWRITE) return; 00603 if(flags & WINED3DLOCK_DISCARD) 00604 { 00605 ENTER_GL(); 00606 GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, NULL, This->buffer_object_usage)); 00607 checkGLcall("glBufferDataARB\n"); 00608 LEAVE_GL(); 00609 return; 00610 } 00611 00612 if(!This->query) 00613 { 00614 TRACE("Creating event query for buffer %p\n", This); 00615 00616 if (!wined3d_event_query_supported(gl_info)) 00617 { 00618 FIXME("Event queries not supported, dropping async buffer locks.\n"); 00619 goto drop_query; 00620 } 00621 00622 This->query = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This->query)); 00623 if (!This->query) 00624 { 00625 ERR("Failed to allocate event query memory, dropping async buffer locks.\n"); 00626 goto drop_query; 00627 } 00628 00629 /* Since we don't know about old draws a glFinish is needed once */ 00630 wglFinish(); 00631 return; 00632 } 00633 TRACE("Synchronizing buffer %p\n", This); 00634 ret = wined3d_event_query_finish(This->query, This->resource.device); 00635 switch(ret) 00636 { 00637 case WINED3D_EVENT_QUERY_NOT_STARTED: 00638 case WINED3D_EVENT_QUERY_OK: 00639 /* All done */ 00640 return; 00641 00642 case WINED3D_EVENT_QUERY_WRONG_THREAD: 00643 WARN("Cannot synchronize buffer lock due to a thread conflict\n"); 00644 goto drop_query; 00645 00646 default: 00647 ERR("wined3d_event_query_finish returned %u, dropping async buffer locks\n", ret); 00648 goto drop_query; 00649 } 00650 00651 drop_query: 00652 if(This->query) 00653 { 00654 wined3d_event_query_destroy(This->query); 00655 This->query = NULL; 00656 } 00657 00658 wglFinish(); 00659 ENTER_GL(); 00660 GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)); 00661 checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)"); 00662 LEAVE_GL(); 00663 This->flags &= ~WINED3D_BUFFER_APPLESYNC; 00664 } 00665 00666 /* The caller provides a GL context */ 00667 static void buffer_direct_upload(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info, DWORD flags) 00668 { 00669 BYTE *map; 00670 UINT start = 0, len = 0; 00671 00672 ENTER_GL(); 00673 00674 /* This potentially invalidates the element array buffer binding, but the 00675 * caller always takes care of this. */ 00676 GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); 00677 checkGLcall("glBindBufferARB"); 00678 if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) 00679 { 00680 GLbitfield mapflags; 00681 mapflags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; 00682 if (flags & WINED3D_BUFFER_DISCARD) 00683 mapflags |= GL_MAP_INVALIDATE_BUFFER_BIT; 00684 if (flags & WINED3D_BUFFER_NOSYNC) 00685 mapflags |= GL_MAP_UNSYNCHRONIZED_BIT; 00686 map = GL_EXTCALL(glMapBufferRange(This->buffer_type_hint, 0, 00687 This->resource.size, mapflags)); 00688 checkGLcall("glMapBufferRange"); 00689 } 00690 else 00691 { 00692 if (This->flags & WINED3D_BUFFER_APPLESYNC) 00693 { 00694 DWORD syncflags = 0; 00695 if (flags & WINED3D_BUFFER_DISCARD) syncflags |= WINED3DLOCK_DISCARD; 00696 if (flags & WINED3D_BUFFER_NOSYNC) syncflags |= WINED3DLOCK_NOOVERWRITE; 00697 LEAVE_GL(); 00698 buffer_sync_apple(This, syncflags, gl_info); 00699 ENTER_GL(); 00700 } 00701 map = GL_EXTCALL(glMapBufferARB(This->buffer_type_hint, GL_WRITE_ONLY_ARB)); 00702 checkGLcall("glMapBufferARB"); 00703 } 00704 if (!map) 00705 { 00706 LEAVE_GL(); 00707 ERR("Failed to map opengl buffer\n"); 00708 return; 00709 } 00710 00711 while (This->modified_areas) 00712 { 00713 This->modified_areas--; 00714 start = This->maps[This->modified_areas].offset; 00715 len = This->maps[This->modified_areas].size; 00716 00717 memcpy(map + start, This->resource.allocatedMemory + start, len); 00718 00719 if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) 00720 { 00721 GL_EXTCALL(glFlushMappedBufferRange(This->buffer_type_hint, start, len)); 00722 checkGLcall("glFlushMappedBufferRange"); 00723 } 00724 else if (This->flags & WINED3D_BUFFER_FLUSH) 00725 { 00726 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(This->buffer_type_hint, start, len)); 00727 checkGLcall("glFlushMappedBufferRangeAPPLE"); 00728 } 00729 } 00730 GL_EXTCALL(glUnmapBufferARB(This->buffer_type_hint)); 00731 checkGLcall("glUnmapBufferARB"); 00732 00733 LEAVE_GL(); 00734 } 00735 00736 /* Do not call while under the GL lock. */ 00737 void CDECL wined3d_buffer_preload(struct wined3d_buffer *buffer) 00738 { 00739 DWORD flags = buffer->flags & (WINED3D_BUFFER_NOSYNC | WINED3D_BUFFER_DISCARD); 00740 struct wined3d_device *device = buffer->resource.device; 00741 UINT start = 0, end = 0, len = 0, vertices; 00742 const struct wined3d_gl_info *gl_info; 00743 struct wined3d_context *context; 00744 BOOL decl_changed = FALSE; 00745 unsigned int i, j; 00746 BYTE *data; 00747 00748 TRACE("buffer %p.\n", buffer); 00749 00750 buffer->flags &= ~(WINED3D_BUFFER_NOSYNC | WINED3D_BUFFER_DISCARD); 00751 00752 if (!buffer->buffer_object) 00753 { 00754 /* TODO: Make converting independent from VBOs */ 00755 if (buffer->flags & WINED3D_BUFFER_CREATEBO) 00756 { 00757 context = context_acquire(device, NULL); 00758 buffer_create_buffer_object(buffer, context->gl_info); 00759 context_release(context); 00760 buffer->flags &= ~WINED3D_BUFFER_CREATEBO; 00761 } 00762 else 00763 { 00764 /* Not doing any conversion */ 00765 return; 00766 } 00767 } 00768 00769 /* Reading the declaration makes only sense if the stateblock is finalized and the buffer bound to a stream */ 00770 if (device->isInDraw && buffer->bind_count > 0) 00771 { 00772 decl_changed = buffer_find_decl(buffer); 00773 buffer->flags |= WINED3D_BUFFER_HASDESC; 00774 } 00775 00776 if (!decl_changed && !(buffer->flags & WINED3D_BUFFER_HASDESC && buffer_is_dirty(buffer))) 00777 { 00778 ++buffer->draw_count; 00779 if (buffer->draw_count > VB_RESETDECLCHANGE) 00780 buffer->decl_change_count = 0; 00781 if (buffer->draw_count > VB_RESETFULLCONVS) 00782 buffer->full_conversion_count = 0; 00783 return; 00784 } 00785 00786 /* If applications change the declaration over and over, reconverting all the time is a huge 00787 * performance hit. So count the declaration changes and release the VBO if there are too many 00788 * of them (and thus stop converting) 00789 */ 00790 if (decl_changed) 00791 { 00792 ++buffer->decl_change_count; 00793 buffer->draw_count = 0; 00794 00795 if (buffer->decl_change_count > VB_MAXDECLCHANGES 00796 || (buffer->conversion_map && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC))) 00797 { 00798 FIXME("Too many declaration changes or converting dynamic buffer, stopping converting\n"); 00799 00800 buffer_unload(&buffer->resource); 00801 buffer->flags &= ~WINED3D_BUFFER_CREATEBO; 00802 00803 /* The stream source state handler might have read the memory of 00804 * the vertex buffer already and got the memory in the vbo which 00805 * is not valid any longer. Dirtify the stream source to force a 00806 * reload. This happens only once per changed vertexbuffer and 00807 * should occur rather rarely. */ 00808 device_invalidate_state(device, STATE_STREAMSRC); 00809 return; 00810 } 00811 00812 /* The declaration changed, reload the whole buffer */ 00813 WARN("Reloading buffer because of decl change\n"); 00814 buffer_clear_dirty_areas(buffer); 00815 if (!buffer_add_dirty_area(buffer, 0, 0)) 00816 { 00817 ERR("buffer_add_dirty_area failed, this is not expected\n"); 00818 return; 00819 } 00820 /* Avoid unfenced updates, we might overwrite more areas of the buffer than the application 00821 * cleared for unsynchronized updates 00822 */ 00823 flags = 0; 00824 } 00825 else 00826 { 00827 /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that 00828 * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without 00829 * decl changes and reset the decl change count after a specific number of them 00830 */ 00831 if (buffer->conversion_map && buffer_is_fully_dirty(buffer)) 00832 { 00833 ++buffer->full_conversion_count; 00834 if (buffer->full_conversion_count > VB_MAXFULLCONVERSIONS) 00835 { 00836 FIXME("Too many full buffer conversions, stopping converting.\n"); 00837 buffer_unload(&buffer->resource); 00838 buffer->flags &= ~WINED3D_BUFFER_CREATEBO; 00839 if (buffer->bind_count) 00840 device_invalidate_state(device, STATE_STREAMSRC); 00841 return; 00842 } 00843 } 00844 else 00845 { 00846 ++buffer->draw_count; 00847 if (buffer->draw_count > VB_RESETDECLCHANGE) 00848 buffer->decl_change_count = 0; 00849 if (buffer->draw_count > VB_RESETFULLCONVS) 00850 buffer->full_conversion_count = 0; 00851 } 00852 } 00853 00854 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB) 00855 device_invalidate_state(device, STATE_INDEXBUFFER); 00856 00857 if (!buffer->conversion_map) 00858 { 00859 /* That means that there is nothing to fixup. Just upload from 00860 * buffer->resource.allocatedMemory directly into the vbo. Do not 00861 * free the system memory copy because drawPrimitive may need it if 00862 * the stride is 0, for instancing emulation, vertex blending 00863 * emulation or shader emulation. */ 00864 TRACE("No conversion needed.\n"); 00865 00866 /* Nothing to do because we locked directly into the vbo */ 00867 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER)) 00868 { 00869 return; 00870 } 00871 00872 context = context_acquire(device, NULL); 00873 buffer_direct_upload(buffer, context->gl_info, flags); 00874 00875 context_release(context); 00876 return; 00877 } 00878 00879 context = context_acquire(device, NULL); 00880 gl_info = context->gl_info; 00881 00882 if(!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER)) 00883 { 00884 buffer_get_sysmem(buffer, gl_info); 00885 } 00886 00887 /* Now for each vertex in the buffer that needs conversion */ 00888 vertices = buffer->resource.size / buffer->stride; 00889 00890 data = HeapAlloc(GetProcessHeap(), 0, buffer->resource.size); 00891 00892 while(buffer->modified_areas) 00893 { 00894 buffer->modified_areas--; 00895 start = buffer->maps[buffer->modified_areas].offset; 00896 len = buffer->maps[buffer->modified_areas].size; 00897 end = start + len; 00898 00899 memcpy(data + start, buffer->resource.allocatedMemory + start, end - start); 00900 for (i = start / buffer->stride; i < min((end / buffer->stride) + 1, vertices); ++i) 00901 { 00902 for (j = 0; j < buffer->stride; ++j) 00903 { 00904 switch (buffer->conversion_map[j]) 00905 { 00906 case CONV_NONE: 00907 /* Done already */ 00908 j += 3; 00909 break; 00910 case CONV_D3DCOLOR: 00911 fixup_d3dcolor((DWORD *) (data + i * buffer->stride + j)); 00912 j += 3; 00913 break; 00914 00915 case CONV_POSITIONT: 00916 fixup_transformed_pos((float *) (data + i * buffer->stride + j)); 00917 j += 15; 00918 break; 00919 default: 00920 FIXME("Unimplemented conversion %d in shifted conversion\n", buffer->conversion_map[j]); 00921 } 00922 } 00923 } 00924 00925 ENTER_GL(); 00926 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object)); 00927 checkGLcall("glBindBufferARB"); 00928 GL_EXTCALL(glBufferSubDataARB(buffer->buffer_type_hint, start, len, data + start)); 00929 checkGLcall("glBufferSubDataARB"); 00930 LEAVE_GL(); 00931 } 00932 00933 HeapFree(GetProcessHeap(), 0, data); 00934 context_release(context); 00935 } 00936 00937 static DWORD buffer_sanitize_flags(const struct wined3d_buffer *buffer, DWORD flags) 00938 { 00939 /* Not all flags make sense together, but Windows never returns an error. Catch the 00940 * cases that could cause issues */ 00941 if(flags & WINED3DLOCK_READONLY) 00942 { 00943 if(flags & WINED3DLOCK_DISCARD) 00944 { 00945 WARN("WINED3DLOCK_READONLY combined with WINED3DLOCK_DISCARD, ignoring flags\n"); 00946 return 0; 00947 } 00948 if(flags & WINED3DLOCK_NOOVERWRITE) 00949 { 00950 WARN("WINED3DLOCK_READONLY combined with WINED3DLOCK_NOOVERWRITE, ignoring flags\n"); 00951 return 0; 00952 } 00953 } 00954 else if((flags & (WINED3DLOCK_DISCARD | WINED3DLOCK_NOOVERWRITE)) == (WINED3DLOCK_DISCARD | WINED3DLOCK_NOOVERWRITE)) 00955 { 00956 WARN("WINED3DLOCK_DISCARD and WINED3DLOCK_NOOVERWRITE used together, ignoring\n"); 00957 return 0; 00958 } 00959 else if (flags & (WINED3DLOCK_DISCARD | WINED3DLOCK_NOOVERWRITE) && !(buffer->resource.usage & WINED3DUSAGE_DYNAMIC)) 00960 { 00961 WARN("DISCARD or NOOVERWRITE lock on non-dynamic buffer, ignoring\n"); 00962 return 0; 00963 } 00964 00965 return flags; 00966 } 00967 00968 static GLbitfield buffer_gl_map_flags(DWORD d3d_flags) 00969 { 00970 GLbitfield ret = 0; 00971 00972 if (!(d3d_flags & WINED3DLOCK_READONLY)) 00973 ret |= GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; 00974 if (!(d3d_flags & (WINED3DLOCK_DISCARD | WINED3DLOCK_NOOVERWRITE))) 00975 ret |= GL_MAP_READ_BIT; 00976 00977 if (d3d_flags & WINED3DLOCK_DISCARD) 00978 ret |= GL_MAP_INVALIDATE_BUFFER_BIT; 00979 if (d3d_flags & WINED3DLOCK_NOOVERWRITE) 00980 ret |= GL_MAP_UNSYNCHRONIZED_BIT; 00981 00982 return ret; 00983 } 00984 00985 struct wined3d_resource * CDECL wined3d_buffer_get_resource(struct wined3d_buffer *buffer) 00986 { 00987 TRACE("buffer %p.\n", buffer); 00988 00989 return &buffer->resource; 00990 } 00991 00992 HRESULT CDECL wined3d_buffer_map(struct wined3d_buffer *buffer, UINT offset, UINT size, BYTE **data, DWORD flags) 00993 { 00994 BOOL dirty = buffer_is_dirty(buffer); 00995 LONG count; 00996 00997 TRACE("buffer %p, offset %u, size %u, data %p, flags %#x\n", buffer, offset, size, data, flags); 00998 00999 flags = buffer_sanitize_flags(buffer, flags); 01000 if (!(flags & WINED3DLOCK_READONLY)) 01001 { 01002 if (!buffer_add_dirty_area(buffer, offset, size)) return E_OUTOFMEMORY; 01003 } 01004 01005 count = InterlockedIncrement(&buffer->lock_count); 01006 01007 if (buffer->buffer_object) 01008 { 01009 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER)) 01010 { 01011 if (count == 1) 01012 { 01013 struct wined3d_device *device = buffer->resource.device; 01014 struct wined3d_context *context; 01015 const struct wined3d_gl_info *gl_info; 01016 01017 context = context_acquire(device, NULL); 01018 gl_info = context->gl_info; 01019 01020 ENTER_GL(); 01021 01022 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB) 01023 context_invalidate_state(context, STATE_INDEXBUFFER); 01024 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object)); 01025 01026 if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) 01027 { 01028 GLbitfield mapflags = buffer_gl_map_flags(flags); 01029 buffer->resource.allocatedMemory = GL_EXTCALL(glMapBufferRange(buffer->buffer_type_hint, 01030 0, buffer->resource.size, mapflags)); 01031 checkGLcall("glMapBufferRange"); 01032 } 01033 else 01034 { 01035 if (buffer->flags & WINED3D_BUFFER_APPLESYNC) 01036 { 01037 LEAVE_GL(); 01038 buffer_sync_apple(buffer, flags, gl_info); 01039 ENTER_GL(); 01040 } 01041 buffer->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(buffer->buffer_type_hint, 01042 GL_READ_WRITE_ARB)); 01043 checkGLcall("glMapBufferARB"); 01044 } 01045 LEAVE_GL(); 01046 01047 if (((DWORD_PTR)buffer->resource.allocatedMemory) & (RESOURCE_ALIGNMENT - 1)) 01048 { 01049 WARN("Pointer %p is not %u byte aligned.\n", buffer->resource.allocatedMemory, RESOURCE_ALIGNMENT); 01050 01051 ENTER_GL(); 01052 GL_EXTCALL(glUnmapBufferARB(buffer->buffer_type_hint)); 01053 checkGLcall("glUnmapBufferARB"); 01054 LEAVE_GL(); 01055 buffer->resource.allocatedMemory = NULL; 01056 01057 if (buffer->resource.usage & WINED3DUSAGE_DYNAMIC) 01058 { 01059 /* The extra copy is more expensive than not using VBOs at 01060 * all on the Nvidia Linux driver, which is the only driver 01061 * that returns unaligned pointers 01062 */ 01063 TRACE("Dynamic buffer, dropping VBO\n"); 01064 buffer_unload(&buffer->resource); 01065 buffer->flags &= ~WINED3D_BUFFER_CREATEBO; 01066 if (buffer->bind_count) 01067 device_invalidate_state(device, STATE_STREAMSRC); 01068 } 01069 else 01070 { 01071 TRACE("Falling back to doublebuffered operation\n"); 01072 buffer_get_sysmem(buffer, gl_info); 01073 } 01074 TRACE("New pointer is %p.\n", buffer->resource.allocatedMemory); 01075 } 01076 context_release(context); 01077 } 01078 } 01079 else 01080 { 01081 if (dirty) 01082 { 01083 if (buffer->flags & WINED3D_BUFFER_NOSYNC && !(flags & WINED3DLOCK_NOOVERWRITE)) 01084 { 01085 buffer->flags &= ~WINED3D_BUFFER_NOSYNC; 01086 } 01087 } 01088 else if(flags & WINED3DLOCK_NOOVERWRITE) 01089 { 01090 buffer->flags |= WINED3D_BUFFER_NOSYNC; 01091 } 01092 01093 if (flags & WINED3DLOCK_DISCARD) 01094 { 01095 buffer->flags |= WINED3D_BUFFER_DISCARD; 01096 } 01097 } 01098 } 01099 01100 *data = buffer->resource.allocatedMemory + offset; 01101 01102 TRACE("Returning memory at %p (base %p, offset %u).\n", *data, buffer->resource.allocatedMemory, offset); 01103 /* TODO: check Flags compatibility with buffer->currentDesc.Usage (see MSDN) */ 01104 01105 return WINED3D_OK; 01106 } 01107 01108 void CDECL wined3d_buffer_unmap(struct wined3d_buffer *buffer) 01109 { 01110 ULONG i; 01111 01112 TRACE("buffer %p.\n", buffer); 01113 01114 /* In the case that the number of Unmap calls > the 01115 * number of Map calls, d3d returns always D3D_OK. 01116 * This is also needed to prevent Map from returning garbage on 01117 * the next call (this will happen if the lock_count is < 0). */ 01118 if (!buffer->lock_count) 01119 { 01120 WARN("Unmap called without a previous map call.\n"); 01121 return; 01122 } 01123 01124 if (InterlockedDecrement(&buffer->lock_count)) 01125 { 01126 /* Delay loading the buffer until everything is unlocked */ 01127 TRACE("Ignoring unmap.\n"); 01128 return; 01129 } 01130 01131 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER) && buffer->buffer_object) 01132 { 01133 struct wined3d_device *device = buffer->resource.device; 01134 const struct wined3d_gl_info *gl_info; 01135 struct wined3d_context *context; 01136 01137 context = context_acquire(device, NULL); 01138 gl_info = context->gl_info; 01139 01140 ENTER_GL(); 01141 01142 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB) 01143 context_invalidate_state(context, STATE_INDEXBUFFER); 01144 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object)); 01145 01146 if (gl_info->supported[ARB_MAP_BUFFER_RANGE]) 01147 { 01148 for (i = 0; i < buffer->modified_areas; ++i) 01149 { 01150 GL_EXTCALL(glFlushMappedBufferRange(buffer->buffer_type_hint, 01151 buffer->maps[i].offset, buffer->maps[i].size)); 01152 checkGLcall("glFlushMappedBufferRange"); 01153 } 01154 } 01155 else if (buffer->flags & WINED3D_BUFFER_FLUSH) 01156 { 01157 for (i = 0; i < buffer->modified_areas; ++i) 01158 { 01159 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(buffer->buffer_type_hint, 01160 buffer->maps[i].offset, buffer->maps[i].size)); 01161 checkGLcall("glFlushMappedBufferRangeAPPLE"); 01162 } 01163 } 01164 01165 GL_EXTCALL(glUnmapBufferARB(buffer->buffer_type_hint)); 01166 LEAVE_GL(); 01167 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */ 01168 context_release(context); 01169 01170 buffer->resource.allocatedMemory = NULL; 01171 buffer_clear_dirty_areas(buffer); 01172 } 01173 else if (buffer->flags & WINED3D_BUFFER_HASDESC) 01174 { 01175 wined3d_buffer_preload(buffer); 01176 } 01177 } 01178 01179 static const struct wined3d_resource_ops buffer_resource_ops = 01180 { 01181 buffer_unload, 01182 }; 01183 01184 static HRESULT buffer_init(struct wined3d_buffer *buffer, struct wined3d_device *device, 01185 UINT size, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, GLenum bind_hint, 01186 const char *data, void *parent, const struct wined3d_parent_ops *parent_ops) 01187 { 01188 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; 01189 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id); 01190 HRESULT hr; 01191 BOOL dynamic_buffer_ok; 01192 01193 if (!size) 01194 { 01195 WARN("Size 0 requested, returning WINED3DERR_INVALIDCALL\n"); 01196 return WINED3DERR_INVALIDCALL; 01197 } 01198 01199 hr = resource_init(&buffer->resource, device, WINED3D_RTYPE_BUFFER, format, 01200 WINED3D_MULTISAMPLE_NONE, 0, usage, pool, size, 1, 1, size, 01201 parent, parent_ops, &buffer_resource_ops); 01202 if (FAILED(hr)) 01203 { 01204 WARN("Failed to initialize resource, hr %#x\n", hr); 01205 return hr; 01206 } 01207 buffer->buffer_type_hint = bind_hint; 01208 01209 TRACE("size %#x, usage %#x, format %s, memory @ %p, iface @ %p.\n", buffer->resource.size, buffer->resource.usage, 01210 debug_d3dformat(buffer->resource.format->id), buffer->resource.allocatedMemory, buffer); 01211 01212 dynamic_buffer_ok = gl_info->supported[APPLE_FLUSH_BUFFER_RANGE] || gl_info->supported[ARB_MAP_BUFFER_RANGE]; 01213 01214 /* Observations show that drawStridedSlow is faster on dynamic VBs than converting + 01215 * drawStridedFast (half-life 2 and others). 01216 * 01217 * Basically converting the vertices in the buffer is quite expensive, and observations 01218 * show that drawStridedSlow is faster than converting + uploading + drawStridedFast. 01219 * Therefore do not create a VBO for WINED3DUSAGE_DYNAMIC buffers. 01220 */ 01221 if (!gl_info->supported[ARB_VERTEX_BUFFER_OBJECT]) 01222 { 01223 TRACE("Not creating a vbo because GL_ARB_vertex_buffer is not supported\n"); 01224 } 01225 else if(buffer->resource.pool == WINED3D_POOL_SYSTEM_MEM) 01226 { 01227 TRACE("Not creating a vbo because the vertex buffer is in system memory\n"); 01228 } 01229 else if(!dynamic_buffer_ok && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)) 01230 { 01231 TRACE("Not creating a vbo because the buffer has dynamic usage and no GL support\n"); 01232 } 01233 else 01234 { 01235 buffer->flags |= WINED3D_BUFFER_CREATEBO; 01236 } 01237 01238 if (data) 01239 { 01240 BYTE *ptr; 01241 01242 hr = wined3d_buffer_map(buffer, 0, size, &ptr, 0); 01243 if (FAILED(hr)) 01244 { 01245 ERR("Failed to map buffer, hr %#x\n", hr); 01246 buffer_unload(&buffer->resource); 01247 resource_cleanup(&buffer->resource); 01248 return hr; 01249 } 01250 01251 memcpy(ptr, data, size); 01252 01253 wined3d_buffer_unmap(buffer); 01254 } 01255 01256 buffer->maps = HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer->maps)); 01257 if (!buffer->maps) 01258 { 01259 ERR("Out of memory\n"); 01260 buffer_unload(&buffer->resource); 01261 resource_cleanup(&buffer->resource); 01262 return E_OUTOFMEMORY; 01263 } 01264 buffer->maps_size = 1; 01265 01266 return WINED3D_OK; 01267 } 01268 01269 HRESULT CDECL wined3d_buffer_create(struct wined3d_device *device, struct wined3d_buffer_desc *desc, const void *data, 01270 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer) 01271 { 01272 struct wined3d_buffer *object; 01273 HRESULT hr; 01274 01275 TRACE("device %p, desc %p, data %p, parent %p, buffer %p\n", device, desc, data, parent, buffer); 01276 01277 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); 01278 if (!object) 01279 { 01280 ERR("Failed to allocate memory\n"); 01281 return E_OUTOFMEMORY; 01282 } 01283 01284 FIXME("Ignoring access flags (pool)\n"); 01285 01286 hr = buffer_init(object, device, desc->byte_width, desc->usage, WINED3DFMT_UNKNOWN, 01287 WINED3D_POOL_MANAGED, GL_ARRAY_BUFFER_ARB, data, parent, parent_ops); 01288 if (FAILED(hr)) 01289 { 01290 WARN("Failed to initialize buffer, hr %#x.\n", hr); 01291 HeapFree(GetProcessHeap(), 0, object); 01292 return hr; 01293 } 01294 object->desc = *desc; 01295 01296 TRACE("Created buffer %p.\n", object); 01297 01298 *buffer = object; 01299 01300 return WINED3D_OK; 01301 } 01302 01303 HRESULT CDECL wined3d_buffer_create_vb(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool, 01304 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer) 01305 { 01306 struct wined3d_buffer *object; 01307 HRESULT hr; 01308 01309 TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n", 01310 device, size, usage, pool, parent, parent_ops, buffer); 01311 01312 if (pool == WINED3D_POOL_SCRATCH) 01313 { 01314 /* The d3d9 tests shows that this is not allowed. It doesn't make much 01315 * sense anyway, SCRATCH buffers wouldn't be usable anywhere. */ 01316 WARN("Vertex buffer in WINED3D_POOL_SCRATCH requested, returning WINED3DERR_INVALIDCALL.\n"); 01317 *buffer = NULL; 01318 return WINED3DERR_INVALIDCALL; 01319 } 01320 01321 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); 01322 if (!object) 01323 { 01324 ERR("Out of memory\n"); 01325 *buffer = NULL; 01326 return WINED3DERR_OUTOFVIDEOMEMORY; 01327 } 01328 01329 hr = buffer_init(object, device, size, usage, WINED3DFMT_VERTEXDATA, 01330 pool, GL_ARRAY_BUFFER_ARB, NULL, parent, parent_ops); 01331 if (FAILED(hr)) 01332 { 01333 WARN("Failed to initialize buffer, hr %#x.\n", hr); 01334 HeapFree(GetProcessHeap(), 0, object); 01335 return hr; 01336 } 01337 01338 TRACE("Created buffer %p.\n", object); 01339 *buffer = object; 01340 01341 return WINED3D_OK; 01342 } 01343 01344 HRESULT CDECL wined3d_buffer_create_ib(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool, 01345 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer) 01346 { 01347 struct wined3d_buffer *object; 01348 HRESULT hr; 01349 01350 TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n", 01351 device, size, usage, pool, parent, parent_ops, buffer); 01352 01353 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); 01354 if (!object) 01355 { 01356 ERR("Out of memory\n"); 01357 *buffer = NULL; 01358 return WINED3DERR_OUTOFVIDEOMEMORY; 01359 } 01360 01361 hr = buffer_init(object, device, size, usage | WINED3DUSAGE_STATICDECL, 01362 WINED3DFMT_UNKNOWN, pool, GL_ELEMENT_ARRAY_BUFFER_ARB, NULL, 01363 parent, parent_ops); 01364 if (FAILED(hr)) 01365 { 01366 WARN("Failed to initialize buffer, hr %#x\n", hr); 01367 HeapFree(GetProcessHeap(), 0, object); 01368 return hr; 01369 } 01370 01371 TRACE("Created buffer %p.\n", object); 01372 *buffer = object; 01373 01374 return WINED3D_OK; 01375 } Generated on Fri May 25 2012 04:19:30 for ReactOS by
1.7.6.1
|