From: Stefan Dösinger stefan@codeweavers.com
This implementation works similarly to the current permanently mapped buffer one and shares most of the code.
By nature it will be slow for d3d10+ applications because we don't get any map range info from the API. --- dlls/wined3d/buffer.c | 30 ++++++++++++++------- dlls/wined3d/context_gl.c | 6 ++++- dlls/wined3d/cs.c | 48 ++++++++++++++++++++++++++++++---- dlls/wined3d/wined3d_private.h | 6 +++++ 4 files changed, 75 insertions(+), 15 deletions(-)
diff --git a/dlls/wined3d/buffer.c b/dlls/wined3d/buffer.c index 082d502ff98..a107c7ecb1d 100644 --- a/dlls/wined3d/buffer.c +++ b/dlls/wined3d/buffer.c @@ -1245,15 +1245,27 @@ void wined3d_buffer_update_sub_resource(struct wined3d_buffer *buffer, struct wi
if (flags & UPLOAD_BO_RENAME_ON_UNMAP) { - /* Don't increment the refcount. UPLOAD_BO_RENAME_ON_UNMAP transfers an - * existing reference. - * - * FIXME: We could degenerate RENAME to a copy + free and rely on the - * COW logic to detect this case. - */ - wined3d_buffer_set_bo(buffer, context, upload_bo->addr.buffer_object); - wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_BUFFER); - wined3d_buffer_invalidate_location(buffer, ~WINED3D_LOCATION_BUFFER); + if (upload_bo->addr.buffer_object) + { + /* Don't increment the refcount. UPLOAD_BO_RENAME_ON_UNMAP transfers an + * existing reference. + * + * FIXME: We could degenerate RENAME to a copy + free and rely on the + * COW logic to detect this case. + */ + wined3d_buffer_set_bo(buffer, context, upload_bo->addr.buffer_object); + wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_BUFFER); + wined3d_buffer_invalidate_location(buffer, ~WINED3D_LOCATION_BUFFER); + } + else + { + _aligned_free(buffer->resource.heap_memory); + buffer->resource.heap_memory = (void *)upload_bo->addr.addr; + wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_SYSMEM); + wined3d_buffer_invalidate_location(buffer, ~WINED3D_LOCATION_SYSMEM); + /* We are done here. Due to the invalidated LOCATION_BUFFER the data will be uplooaded on the next draw. */ + return; + } }
if (upload_bo->addr.buffer_object && upload_bo->addr.buffer_object == buffer->buffer_object) diff --git a/dlls/wined3d/context_gl.c b/dlls/wined3d/context_gl.c index eb3c8b2438e..ece2573545d 100644 --- a/dlls/wined3d/context_gl.c +++ b/dlls/wined3d/context_gl.c @@ -3138,8 +3138,12 @@ void wined3d_context_gl_copy_bo_address(struct wined3d_context_gl *context_gl, wined3d_context_gl_reference_bo(context_gl, dst_bo); } } - else + else if (dst->addr != src->addr) { + /* Copies between the same address can happen if we NOOVERWRITE mapped into heap_memory + * and buffer_get_location picked the sysmem location to copy the new data to (e.g. because + * the buffer has been invalidated after DISCARD. This function will get called again on + * the next draw to copy the data into the BO. */ for (i = 0; i < range_count; ++i) memcpy(dst->addr + ranges[i].offset, src->addr + ranges[i].offset, ranges[i].size); } diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c index 80edad7affe..75bcc427523 100644 --- a/dlls/wined3d/cs.c +++ b/dlls/wined3d/cs.c @@ -3078,6 +3078,11 @@ static bool wined3d_cs_map_upload_bo(struct wined3d_device_context *context, str { const struct wined3d_format *format = resource->format;
+ /* FIXME: This codepath exists to retain update_sub_resource's ability to copy incoming data + * above 4k (or above what we want to place on the CS queue itself) at the cost of doing a + * heap_alloc + heap_free. It is incorrect for maps - even if we have a write-only map, there + * is no guarantee that the application will overwrite every single byte in the mapped range, + * so we would have to copy the current buffer data into the new buffer before returning it. */ wined3d_format_calculate_pitch(format, 1, box->right - box->left, box->bottom - box->top, &map_desc->row_pitch, &map_desc->slice_pitch);
@@ -3120,7 +3125,24 @@ static bool wined3d_cs_map_upload_bo(struct wined3d_device_context *context, str if (flags & WINED3D_MAP_DISCARD) { if (!device->adapter->adapter_ops->adapter_alloc_bo(device, resource, sub_resource_idx, &addr)) - return false; + { + if (resource->type == WINED3D_RTYPE_BUFFER) + { + size = resource->size; + } + else + { + struct wined3d_texture *texture = texture_from_resource(resource); + size = texture->sub_resources[sub_resource_idx].size; + } + + addr.buffer_object = 0; + if (!(addr.addr = _aligned_malloc(size, RESOURCE_ALIGNMENT))) + { + WARN_(d3d_perf)("Failed to allocate a heap memory buffer.\n"); + return false; + } + }
/* Limit NOOVERWRITE maps to buffers for now; there are too many * ways that a texture can be invalidated to even count. */ @@ -3161,7 +3183,6 @@ static bool wined3d_cs_map_upload_bo(struct wined3d_device_context *context, str wined3d_resource_get_sub_resource_map_pitch(resource, sub_resource_idx, &map_desc->row_pitch, &map_desc->slice_pitch);
- client->mapped_upload.addr = *wined3d_const_bo_address(&addr); client->mapped_upload.flags = 0; if (bo) { @@ -3171,11 +3192,28 @@ static bool wined3d_cs_map_upload_bo(struct wined3d_device_context *context, str * to unmap the resource, so that we can free VA space. */ if (!bo->coherent || !wined3d_map_persistent()) client->mapped_upload.flags |= UPLOAD_BO_UPLOAD_ON_UNMAP; + + if (flags & WINED3D_MAP_DISCARD) + client->mapped_upload.flags |= UPLOAD_BO_UPLOAD_ON_UNMAP | UPLOAD_BO_RENAME_ON_UNMAP; + + client->mapped_upload.addr = *wined3d_const_bo_address(&addr); } - map_desc->data = resource_offset_map_pointer(resource, sub_resource_idx, map_ptr, box); + else + { + client->mapped_upload.flags |= UPLOAD_BO_UPLOAD_ON_UNMAP;
- if (flags & WINED3D_MAP_DISCARD) - client->mapped_upload.flags |= UPLOAD_BO_UPLOAD_ON_UNMAP | UPLOAD_BO_RENAME_ON_UNMAP; + /* FIXME: This different handling vs the buffer case is ugly. Its because the above + * codepath abuses UPLOAD_ON_UNMAP to flush the BO, but it never reads mapped_upload.addr. + * For the sysmem codepath it is where glBufferSubData will eventually get the actual data + * from. */ + client->mapped_upload.addr.buffer_object = 0; + client->mapped_upload.addr.addr = resource_offset_map_pointer(resource, sub_resource_idx, map_ptr, box); + + if (flags & WINED3D_MAP_DISCARD) + client->mapped_upload.flags |= UPLOAD_BO_RENAME_ON_UNMAP; + } + + map_desc->data = resource_offset_map_pointer(resource, sub_resource_idx, map_ptr, box);
client->mapped_box = *box;
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 0b92ae52e64..441e04fbbe3 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -2518,6 +2518,12 @@ bool wined3d_driver_info_init(struct wined3d_driver_info *driver_info, const struct wined3d_gpu_description *gpu_description, enum wined3d_feature_level feature_level, UINT64 vram_bytes, UINT64 sysmem_bytes) DECLSPEC_HIDDEN;
+/* If RENAME_ON_UNMAP or FREE_ON_UNMAP are used, then the "const" qualifier in addr is wrong. In the + * first case the destination d3d buffer takes ownership of the BO (either GL/vulkan BO or sysmem + * allocation), in the second case the sysmem allocation is freed. + * + * But the code has to be able to deal with genuine const pointers in case of update_sub_resource. + * It must not modify the contents in this case. */ #define UPLOAD_BO_UPLOAD_ON_UNMAP 0x1 #define UPLOAD_BO_RENAME_ON_UNMAP 0x2 #define UPLOAD_BO_FREE_ON_UNMAP 0x4