I'm opening this MR as an RFC. It's an initial attempt to support OpenGL persistent memory mapping in the new wow64. The implementation is not complete yet (details below), but it's intentionally kept as simple as possible for a proof of concept.
Since we can't control where OpenGL maps memory, the idea is to use the available Vulkan extension with enough glue between them. Because of how GL–VK interop works, this requires creating a memory object on the Vulkan side and importing it into OpenGL. Once that's done, all operations on such buffers are performed through GL, except for mapping, which is handled by Vulkan. This is achieved by hooking `gl*BufferStorage` and reimplementing it on top of `glImportMemoryFdEXT` for mappable buffers. Recently introduced buffer wrappers make further tracking of these buffers straightforward.
If we move forward with this, the feature will need to be enabled based on available driver capabilities (for now, it's force-enabled). Some parts might also be worth moving into win32u.
This alone seems enough to claim persistent memory support and thus expose OpenGL 4.6 on the new wow64. Performance looks good in my limited testing, although that hasn't been a focus yet. Possible improvements include: - Currently only GL buffers created with `gl*BufferStorage` are affected. Other cases still fall back to the slow memcpy path. - The draft just picks the first host-visible coherent memory type exposed by Vulkan. We could try harder to pick an optimal type taking into account GL flags. - The draft allocates a new Vulkan memory and fd for each mapped buffer. This is inefficient for small buffers, for which a suballocator could help. - The draft always picks the first GPU reported by Vulkan. We may need to ensure that the selected device matches the one OpenGL actually uses.
An alternative would be a new OpenGL extension. A driver-level solution could be more efficient, and it would certainly be nice to have. However, since such an extension doesn't exist yet, and even if we have it soon, I think it's still valuable to have a fallback solution that works with existing drivers.
From: Jacek Caban jacek@codeweavers.com
--- dlls/opengl32/make_opengl | 1 + dlls/opengl32/tests/opengl.c | 50 ++++++++++++++++++++++++++++++++++++ dlls/opengl32/unix_thunks.c | 3 +-- dlls/opengl32/unix_thunks.h | 1 + dlls/opengl32/unix_wgl.c | 43 ++++++++++++++++++++++++++----- 5 files changed, 90 insertions(+), 8 deletions(-)
diff --git a/dlls/opengl32/make_opengl b/dlls/opengl32/make_opengl index cf38747eae5..25862cd7ee9 100755 --- a/dlls/opengl32/make_opengl +++ b/dlls/opengl32/make_opengl @@ -283,6 +283,7 @@ my %manual_wow64_wrappers = "glFenceSync" => 0, "glGetBufferPointerv" => 0, "glGetBufferPointervARB" => 0, + "glGetError" => 0, "glGetNamedBufferPointerv" => 0, "glGetNamedBufferPointervEXT" => 0, "glGetSynciv" => 0, diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c index 55125722297..656872ec800 100644 --- a/dlls/opengl32/tests/opengl.c +++ b/dlls/opengl32/tests/opengl.c @@ -107,9 +107,11 @@ static PFN_glCopyBufferSubData pglCopyBufferSubData; static PFN_glCopyNamedBufferSubData pglCopyNamedBufferSubData; static PFN_glCreateBuffers pglCreateBuffers; static PFN_glDeleteBuffers pglDeleteBuffers; +static PFN_glDeleteSync pglDeleteSync; static PFN_glFlushMappedBufferRange pglFlushMappedBufferRange; static PFN_glFlushMappedNamedBufferRange pglFlushMappedNamedBufferRange; static PFN_glGenBuffers pglGenBuffers; +static PFN_glIsSync pglIsSync; static PFN_glMapBuffer pglMapBuffer; static PFN_glMapBufferRange pglMapBufferRange; static PFN_glMapNamedBuffer pglMapNamedBuffer; @@ -191,9 +193,11 @@ static void init_functions(void) GET_PROC(glCopyNamedBufferSubData) GET_PROC(glCreateBuffers) GET_PROC(glDeleteBuffers) + GET_PROC(glDeleteSync) GET_PROC(glFlushMappedBufferRange) GET_PROC(glFlushMappedNamedBufferRange) GET_PROC(glGenBuffers) + GET_PROC(glIsSync) GET_PROC(glMapBuffer) GET_PROC(glMapBufferRange) GET_PROC(glMapNamedBuffer) @@ -3491,6 +3495,50 @@ static void check_gl_error_( unsigned int line, GLenum exp ) ok_(__FILE__,line)( err == exp, "glGetError returned %x, expected %x\n", err, exp ); }
+static void test_gl_error( HDC hdc ) +{ + HGLRC rc, old_rc; + int i; + BOOL ret; + + if (!pglDeleteSync) + { + skip( "glDeleteSync not available\n" ); + return; + } + + old_rc = wglGetCurrentContext(); + rc = wglCreateContext( hdc ); + ok( !!rc, "got %p\n", rc ); + ret = wglMakeCurrent( hdc, rc ); + ok( ret, "got %u\n", ret ); + + check_gl_error( GL_NO_ERROR ); + glGetIntegerv( 0xdeadbeef, &i ); + check_gl_error( GL_INVALID_ENUM ); + check_gl_error( GL_NO_ERROR ); + + pglDeleteSync( (GLsync)0xdeadbeef ); + check_gl_error( GL_INVALID_VALUE ); + check_gl_error( GL_NO_ERROR ); + + glGetIntegerv( 0xdeadbeef, &i ); + pglDeleteSync( (GLsync)0xdeadbeef ); + check_gl_error( GL_INVALID_ENUM ); + check_gl_error( GL_NO_ERROR ); + + pglDeleteSync( (GLsync)0xdeadbeef ); + glGetIntegerv( 0xdeadbeef, &i ); + check_gl_error( GL_INVALID_VALUE ); + check_gl_error( GL_NO_ERROR ); + + ret = pglIsSync( (GLsync)0xdeadbeef ); + ok( !ret, "glIsSync returned %x\n", ret ); + check_gl_error( GL_NO_ERROR ); + + wglMakeCurrent( hdc, old_rc ); +} + static void test_memory_map( HDC hdc) { unsigned int i, major = 0, minor = 0; @@ -3745,6 +3793,7 @@ START_TEST(opengl) pD3DKMTCreateDCFromMemory = (void *)GetProcAddress( gdi32, "D3DKMTCreateDCFromMemory" ); pD3DKMTDestroyDCFromMemory = (void *)GetProcAddress( gdi32, "D3DKMTDestroyDCFromMemory" );
+ check_gl_error( GL_INVALID_OPERATION ); ShowWindow(hwnd, SW_SHOW);
hdc = GetDC(hwnd); @@ -3824,6 +3873,7 @@ START_TEST(opengl) test_acceleration(hdc); test_framebuffer(); test_memory_map(hdc); + test_gl_error(hdc);
wgl_extensions = pwglGetExtensionsStringARB(hdc); if(wgl_extensions == NULL) skip("Skipping opengl32 tests because this OpenGL implementation doesn't support WGL extensions!\n"); diff --git a/dlls/opengl32/unix_thunks.c b/dlls/opengl32/unix_thunks.c index 727df735759..7ecf7514afa 100644 --- a/dlls/opengl32/unix_thunks.c +++ b/dlls/opengl32/unix_thunks.c @@ -31659,8 +31659,7 @@ static NTSTATUS wow64_gl_glGetError( void *args ) GLenum ret; } *params = args; TEB *teb = get_teb64( params->teb ); - const struct opengl_funcs *funcs = teb->glTable; - params->ret = funcs->p_glGetError(); + params->ret = wow64_glGetError( teb ); return STATUS_SUCCESS; }
diff --git a/dlls/opengl32/unix_thunks.h b/dlls/opengl32/unix_thunks.h index f3084ed237d..83a53da6e87 100644 --- a/dlls/opengl32/unix_thunks.h +++ b/dlls/opengl32/unix_thunks.h @@ -47,6 +47,7 @@ extern BOOL wrap_wglReleaseTexImageARB( TEB *teb, HPBUFFERARB hPbuffer, int iBuf extern BOOL wrap_wglSetPbufferAttribARB( TEB *teb, HPBUFFERARB hPbuffer, const int *piAttribList );
#ifdef _WIN64 +extern GLenum wow64_glGetError( TEB *teb ); extern GLenum wow64_glClientWaitSync( TEB *teb, GLsync sync, GLbitfield flags, GLuint64 timeout ); extern void wow64_glDeleteBuffers( TEB *teb, GLsizei n, const GLuint *buffers ); extern void wow64_glDeleteSync( TEB *teb, GLsync sync ); diff --git a/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c index bd37d661ce6..a68773f3604 100644 --- a/dlls/opengl32/unix_wgl.c +++ b/dlls/opengl32/unix_wgl.c @@ -139,6 +139,7 @@ struct context GLuint *disabled_exts; /* indices of disabled extensions */ GLubyte *wow64_version; /* wow64 GL version override */ struct buffers *buffers; /* wow64 buffers map */ + GLenum gl_error; /* wrapped GL error */
/* semi-stub state tracker for wglCopyContext */ GLbitfield used; /* context state used bits */ @@ -2023,12 +2024,41 @@ NTSTATUS return_wow64_string( const void *str, PTR32 *wow64_str ) return STATUS_BUFFER_TOO_SMALL; }
+GLenum wow64_glGetError( TEB *teb ) +{ + const struct opengl_funcs *funcs = teb->glTable; + GLenum gl_err, prev_err; + struct context *ctx; + + if (!(ctx = get_current_context( teb, NULL, NULL ))) return GL_INVALID_OPERATION; + gl_err = funcs->p_glGetError(); + prev_err = ctx->gl_error; + ctx->gl_error = GL_NO_ERROR; + return prev_err ? prev_err : gl_err; +} + +static void set_gl_error( TEB *teb, GLenum gl_error ) +{ + const struct opengl_funcs *funcs = teb->glTable; + struct context *ctx; + + if (!(ctx = get_current_context( teb, NULL, NULL )) || ctx->gl_error) return; + if (!(ctx->gl_error = funcs->p_glGetError())) ctx->gl_error = gl_error; +} + +static struct wgl_handle *get_sync_ptr( TEB *teb, GLsync sync ) +{ + struct wgl_handle *handle = get_handle_ptr( sync ); + if (!handle) set_gl_error( teb, GL_INVALID_VALUE ); + return handle; +} + GLenum wow64_glClientWaitSync( TEB *teb, GLsync sync, GLbitfield flags, GLuint64 timeout ) { const struct opengl_funcs *funcs = teb->glTable; struct wgl_handle *handle;
- if (!(handle = get_handle_ptr( sync ))) return GL_INVALID_VALUE; + if (!(handle = get_sync_ptr( teb, sync ))) return GL_INVALID_VALUE; return funcs->p_glClientWaitSync( handle->u.sync, flags, timeout ); }
@@ -2037,7 +2067,7 @@ void wow64_glDeleteSync( TEB *teb, GLsync sync ) const struct opengl_funcs *funcs = teb->glTable; struct wgl_handle *handle;
- if ((handle = get_handle_ptr( sync ))) + if ((handle = get_sync_ptr( teb, sync ))) { funcs->p_glDeleteSync( handle->u.sync ); free_handle_ptr( handle ); @@ -2062,7 +2092,7 @@ void wow64_glGetSynciv( TEB *teb, GLsync sync, GLenum pname, GLsizei count, GLsi const struct opengl_funcs *funcs = teb->glTable; struct wgl_handle *handle;
- if ((handle = get_handle_ptr( sync ))) funcs->p_glGetSynciv( handle->u.sync, pname, count, length, values ); + if ((handle = get_sync_ptr( teb, sync ))) funcs->p_glGetSynciv( handle->u.sync, pname, count, length, values ); }
GLboolean wow64_glIsSync( TEB *teb, GLsync sync ) @@ -2079,7 +2109,7 @@ void wow64_glWaitSync( TEB *teb, GLsync sync, GLbitfield flags, GLuint64 timeout const struct opengl_funcs *funcs = teb->glTable; struct wgl_handle *handle;
- if ((handle = get_handle_ptr( sync ))) funcs->p_glWaitSync( handle->u.sync, flags, timeout ); + if ((handle = get_sync_ptr( teb, sync ))) funcs->p_glWaitSync( handle->u.sync, flags, timeout ); }
static GLint get_buffer_param( TEB *teb, GLenum target, GLenum param ) @@ -2181,7 +2211,7 @@ static struct buffer *get_target_buffer( TEB *teb, GLenum target ) return name ? get_named_buffer( teb, name ) : NULL; }
-static BOOL buffer_vm_alloc( struct buffer *buffer, SIZE_T size ) +static BOOL buffer_vm_alloc( TEB *teb, struct buffer *buffer, SIZE_T size ) { if (buffer->vm_size >= size) return TRUE; if (buffer->vm_ptr) @@ -2194,6 +2224,7 @@ static BOOL buffer_vm_alloc( struct buffer *buffer, SIZE_T size ) MEM_COMMIT, PAGE_READWRITE )) { ERR("NtAllocateVirtualMemory failed\n"); + set_gl_error( teb, GL_OUT_OF_MEMORY ); return FALSE; } buffer->vm_size = size; @@ -2233,7 +2264,7 @@ static PTR32 wow64_map_buffer( TEB *teb, GLenum target, GLuint name, GLintptr of goto unmap; }
- if (!buffer_vm_alloc( buffer, length + (offset & 0xf) )) return 0; + if (!buffer_vm_alloc( teb, buffer, length + (offset & 0xf) )) goto unmap; buffer->map_ptr = (char *)buffer->vm_ptr + (offset & 0xf); buffer->copy_length = (access & GL_MAP_WRITE_BIT) ? length : 0; if (!(access & (GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)))
From: Jacek Caban jacek@codeweavers.com
--- dlls/opengl32/unix_wgl.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c index a68773f3604..ca5e76fddbe 100644 --- a/dlls/opengl32/unix_wgl.c +++ b/dlls/opengl32/unix_wgl.c @@ -51,6 +51,11 @@ static BOOL is_wow64(void) return !!NtCurrentTeb()->WowTebOffset; }
+static BOOL hide_buffer_storage(void) +{ + return is_win64 && is_wow64(); +} + static UINT64 call_gl_debug_message_callback; pthread_mutex_t wgl_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -505,7 +510,7 @@ static GLubyte *filter_extensions_list( const char *extensions, const char *disa
/* We do not support GL_MAP_PERSISTENT_BIT, and hence * ARB_buffer_storage, on wow64. */ - if (is_win64 && is_wow64() && (!strcmp( p, "GL_ARB_buffer_storage" ) || !strcmp( p, "GL_EXT_buffer_storage" ))) + if (hide_buffer_storage() && (!strcmp( p, "GL_ARB_buffer_storage" ) || !strcmp( p, "GL_EXT_buffer_storage" ))) { TRACE( "-- %s (disabled due to wow64)\n", p ); } @@ -587,7 +592,7 @@ static GLuint *filter_extensions_index( TEB *teb, const char *disabled, const ch
/* We do not support GL_MAP_PERSISTENT_BIT, and hence * ARB_buffer_storage, on wow64. */ - if (is_win64 && is_wow64() && (!strcmp( ext, "GL_ARB_buffer_storage" ) || !strcmp( ext, "GL_EXT_buffer_storage" ))) + if (hide_buffer_storage() && (!strcmp( ext, "GL_ARB_buffer_storage" ) || !strcmp( ext, "GL_EXT_buffer_storage" ))) { TRACE( "-- %s (disabled due to wow64)\n", ext ); disabled_index[i++] = j; @@ -888,7 +893,7 @@ static BOOL get_integer( TEB *teb, GLenum pname, GLint *data ) return TRUE; }
- if (is_win64 && is_wow64()) + if (hide_buffer_storage()) { /* 4.4 depends on ARB_buffer_storage, which we don't support on wow64. */ if (pname == GL_MAJOR_VERSION) @@ -939,7 +944,7 @@ const GLubyte *wrap_glGetString( TEB *teb, GLenum name ) GLuint **disabled = &ctx->disabled_exts; if (*extensions || filter_extensions( teb, (const char *)ret, extensions, disabled )) return *extensions; } - else if (name == GL_VERSION && is_win64 && is_wow64()) + else if (name == GL_VERSION && hide_buffer_storage()) { struct context *ctx = get_current_context( teb, NULL, NULL ); GLubyte **str = &ctx->wow64_version;
From: Jacek Caban jacek@codeweavers.com
--- dlls/opengl32/make_opengl | 6 + dlls/opengl32/unix_thunks.c | 18 +- dlls/opengl32/unix_thunks.h | 6 + dlls/opengl32/unix_wgl.c | 437 +++++++++++++++++++++++++++++++++--- 4 files changed, 429 insertions(+), 38 deletions(-)
diff --git a/dlls/opengl32/make_opengl b/dlls/opengl32/make_opengl index 25862cd7ee9..285814e1e6e 100755 --- a/dlls/opengl32/make_opengl +++ b/dlls/opengl32/make_opengl @@ -277,10 +277,14 @@ my %map_default_fbo_thunks = ); my %manual_wow64_wrappers = ( + "glBufferStorage" => 0, "glClientWaitSync" => 0, "glDeleteBuffers" => 0, "glDeleteSync" => 0, "glFenceSync" => 0, + "glFlushMappedBufferRange" => 0, + "glFlushMappedNamedBufferRange" => 0, + "glFlushMappedNamedBufferRangeEXT" => 0, "glGetBufferPointerv" => 0, "glGetBufferPointervARB" => 0, "glGetError" => 0, @@ -295,6 +299,8 @@ my %manual_wow64_wrappers = "glMapNamedBufferEXT" => 0, "glMapNamedBufferRange" => 0, "glMapNamedBufferRangeEXT" => 0, + "glNamedBufferStorage" => 0, + "glNamedBufferStorageEXT" => 0, "glUnmapBuffer" => 0, "glUnmapBufferARB" => 0, "glUnmapNamedBuffer" => 0, diff --git a/dlls/opengl32/unix_thunks.c b/dlls/opengl32/unix_thunks.c index 7ecf7514afa..c15f5b862bf 100644 --- a/dlls/opengl32/unix_thunks.c +++ b/dlls/opengl32/unix_thunks.c @@ -37432,10 +37432,9 @@ static NTSTATUS wow64_ext_glBufferStorage( void *args ) GLbitfield flags; } *params = args; TEB *teb = get_teb64( params->teb ); - const struct opengl_funcs *funcs = teb->glTable; pthread_mutex_lock( &wgl_lock ); invalidate_buffer_target( teb, params->target ); - funcs->p_glBufferStorage( params->target, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags ); + wow64_glBufferStorage( teb, params->target, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags ); pthread_mutex_unlock( &wgl_lock ); set_context_attribute( teb, -1 /* unsupported */, NULL, 0 ); return STATUS_SUCCESS; @@ -43613,8 +43612,7 @@ static NTSTATUS wow64_ext_glFlushMappedBufferRange( void *args ) PTR32 length; } *params = args; TEB *teb = get_teb64( params->teb ); - const struct opengl_funcs *funcs = teb->glTable; - funcs->p_glFlushMappedBufferRange( params->target, (GLintptr)ULongToPtr(params->offset), (GLsizeiptr)ULongToPtr(params->length) ); + wow64_glFlushMappedBufferRange( teb, params->target, (GLintptr)ULongToPtr(params->offset), (GLsizeiptr)ULongToPtr(params->length) ); set_context_attribute( teb, -1 /* unsupported */, NULL, 0 ); return STATUS_SUCCESS; } @@ -43645,8 +43643,7 @@ static NTSTATUS wow64_ext_glFlushMappedNamedBufferRange( void *args ) PTR32 length; } *params = args; TEB *teb = get_teb64( params->teb ); - const struct opengl_funcs *funcs = teb->glTable; - funcs->p_glFlushMappedNamedBufferRange( params->buffer, (GLintptr)ULongToPtr(params->offset), (GLsizeiptr)ULongToPtr(params->length) ); + wow64_glFlushMappedNamedBufferRange( teb, params->buffer, (GLintptr)ULongToPtr(params->offset), (GLsizeiptr)ULongToPtr(params->length) ); set_context_attribute( teb, -1 /* unsupported */, NULL, 0 ); return STATUS_SUCCESS; } @@ -43661,8 +43658,7 @@ static NTSTATUS wow64_ext_glFlushMappedNamedBufferRangeEXT( void *args ) PTR32 length; } *params = args; TEB *teb = get_teb64( params->teb ); - const struct opengl_funcs *funcs = teb->glTable; - funcs->p_glFlushMappedNamedBufferRangeEXT( params->buffer, (GLintptr)ULongToPtr(params->offset), (GLsizeiptr)ULongToPtr(params->length) ); + wow64_glFlushMappedNamedBufferRangeEXT( teb, params->buffer, (GLintptr)ULongToPtr(params->offset), (GLsizeiptr)ULongToPtr(params->length) ); set_context_attribute( teb, -1 /* unsupported */, NULL, 0 ); return STATUS_SUCCESS; } @@ -58208,10 +58204,9 @@ static NTSTATUS wow64_ext_glNamedBufferStorage( void *args ) GLbitfield flags; } *params = args; TEB *teb = get_teb64( params->teb ); - const struct opengl_funcs *funcs = teb->glTable; pthread_mutex_lock( &wgl_lock ); invalidate_buffer_name( teb, params->buffer ); - funcs->p_glNamedBufferStorage( params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags ); + wow64_glNamedBufferStorage( teb, params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags ); pthread_mutex_unlock( &wgl_lock ); set_context_attribute( teb, -1 /* unsupported */, NULL, 0 ); return STATUS_SUCCESS; @@ -58228,10 +58223,9 @@ static NTSTATUS wow64_ext_glNamedBufferStorageEXT( void *args ) GLbitfield flags; } *params = args; TEB *teb = get_teb64( params->teb ); - const struct opengl_funcs *funcs = teb->glTable; pthread_mutex_lock( &wgl_lock ); invalidate_buffer_name( teb, params->buffer ); - funcs->p_glNamedBufferStorageEXT( params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags ); + wow64_glNamedBufferStorageEXT( teb, params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags ); pthread_mutex_unlock( &wgl_lock ); set_context_attribute( teb, -1 /* unsupported */, NULL, 0 ); return STATUS_SUCCESS; diff --git a/dlls/opengl32/unix_thunks.h b/dlls/opengl32/unix_thunks.h index 83a53da6e87..f06960ba97b 100644 --- a/dlls/opengl32/unix_thunks.h +++ b/dlls/opengl32/unix_thunks.h @@ -48,10 +48,14 @@ extern BOOL wrap_wglSetPbufferAttribARB( TEB *teb, HPBUFFERARB hPbuffer, const i
#ifdef _WIN64 extern GLenum wow64_glGetError( TEB *teb ); +extern void wow64_glBufferStorage( TEB *teb, GLenum target, GLsizeiptr size, const void *data, GLbitfield flags ); extern GLenum wow64_glClientWaitSync( TEB *teb, GLsync sync, GLbitfield flags, GLuint64 timeout ); extern void wow64_glDeleteBuffers( TEB *teb, GLsizei n, const GLuint *buffers ); extern void wow64_glDeleteSync( TEB *teb, GLsync sync ); extern GLsync wow64_glFenceSync( TEB *teb, GLenum condition, GLbitfield flags ); +extern void wow64_glFlushMappedBufferRange( TEB *teb, GLenum target, GLintptr offset, GLsizeiptr length ); +extern void wow64_glFlushMappedNamedBufferRange( TEB *teb, GLuint buffer, GLintptr offset, GLsizeiptr length ); +extern void wow64_glFlushMappedNamedBufferRangeEXT( TEB *teb, GLuint buffer, GLintptr offset, GLsizeiptr length ); extern void wow64_glGetBufferPointerv( TEB *teb, GLenum target, GLenum pname, PTR32 *params ); extern void wow64_glGetBufferPointervARB( TEB *teb, GLenum target, GLenum pname, PTR32 *params ); extern void wow64_glGetNamedBufferPointerv( TEB *teb, GLuint buffer, GLenum pname, PTR32 *params ); @@ -65,6 +69,8 @@ extern PTR32 wow64_glMapNamedBuffer( TEB *teb, GLuint buffer, GLenum access ); extern PTR32 wow64_glMapNamedBufferEXT( TEB *teb, GLuint buffer, GLenum access ); extern PTR32 wow64_glMapNamedBufferRange( TEB *teb, GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access ); extern PTR32 wow64_glMapNamedBufferRangeEXT( TEB *teb, GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access ); +extern void wow64_glNamedBufferStorage( TEB *teb, GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags ); +extern void wow64_glNamedBufferStorageEXT( TEB *teb, GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags ); extern GLboolean wow64_glUnmapBuffer( TEB *teb, GLenum target ); extern GLboolean wow64_glUnmapBufferARB( TEB *teb, GLenum target ); extern GLboolean wow64_glUnmapNamedBuffer( TEB *teb, GLuint buffer ); diff --git a/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c index ca5e76fddbe..141d0884015 100644 --- a/dlls/opengl32/unix_wgl.c +++ b/dlls/opengl32/unix_wgl.c @@ -26,6 +26,7 @@
#include <stdarg.h> #include <stdlib.h> +#include <assert.h>
#include <pthread.h>
@@ -41,6 +42,7 @@
#include "wine/debug.h" #include "wine/rbtree.h" +#include "wine/vulkan_driver.h"
WINE_DEFAULT_DEBUG_CHANNEL(opengl);
@@ -53,6 +55,7 @@ static BOOL is_wow64(void)
static BOOL hide_buffer_storage(void) { + return 0; /* FIXME: check if required vk/gl features are available */ return is_win64 && is_wow64(); }
@@ -163,6 +166,7 @@ struct context struct buffer { struct rb_entry entry; + void (*cleanup)( const struct opengl_funcs *funcs, struct buffer *buffer ); GLuint name; size_t size; void *host_ptr; @@ -170,6 +174,10 @@ struct buffer size_t copy_length; void *vm_ptr; SIZE_T vm_size; + + VkDeviceMemory vk_memory; + GLuint gl_memory; + GLbitfield flags; };
struct wgl_handle @@ -349,20 +357,21 @@ static int compare_buffer_name( const void *key, const struct rb_entry *entry ) return memcmp( key, &buffer->name, sizeof(buffer->name) ); }
-static void free_buffer( struct buffer *buffer ) +static void free_buffer( const struct opengl_funcs *funcs, struct buffer *buffer ) { + if (buffer->cleanup) buffer->cleanup( funcs, buffer ); if (buffer->vm_ptr) NtFreeVirtualMemory( GetCurrentProcess(), &buffer->vm_ptr, &buffer->vm_size, MEM_RELEASE ); free( buffer ); }
-static void release_buffers( struct buffers *buffers ) +static void release_buffers( const struct opengl_funcs *funcs, struct buffers *buffers ) { struct buffer *buffer, *next;
if (--buffers->ref) return;
RB_FOR_EACH_ENTRY_DESTRUCTOR( buffer, next, &buffers->map, struct buffer, entry ) - free_buffer( buffer ); + free_buffer( funcs, buffer ); free( buffers ); }
@@ -385,7 +394,7 @@ static void update_handle_context( TEB *teb, HGLRC handle, struct wgl_handle *pt } if (shared && shared->buffers) { - release_buffers( ctx->buffers ); + release_buffers( funcs, ctx->buffers ); ctx->buffers = shared->buffers; ctx->buffers->ref++; } @@ -2201,7 +2210,7 @@ void invalidate_buffer_name( TEB *teb, GLuint name )
if (!buffer || !(ctx = get_current_context( teb, NULL, NULL ))) return; rb_remove( &ctx->buffers->map, &buffer->entry ); - free_buffer( buffer ); + free_buffer( teb->glTable, buffer ); }
void invalidate_buffer_target( TEB *teb, GLenum target ) @@ -2236,10 +2245,310 @@ static BOOL buffer_vm_alloc( TEB *teb, struct buffer *buffer, SIZE_T size ) return TRUE; }
-static PTR32 wow64_map_buffer( TEB *teb, GLenum target, GLuint name, GLintptr offset, +static VkInstance vk_instance; +static VkDevice vk_device; +static VkPhysicalDeviceMemoryProperties vk_memory_properties; + +static PFN_vkAllocateMemory p_vkAllocateMemory; +static PFN_vkFreeMemory p_vkFreeMemory; +static PFN_vkGetMemoryFdKHR p_vkGetMemoryFdKHR; +static PFN_vkMapMemory2KHR p_vkMapMemory2KHR; +static PFN_vkUnmapMemory2KHR p_vkUnmapMemory2KHR; +static PFN_vkFlushMappedMemoryRanges p_vkFlushMappedMemoryRanges; + +static void init_vulkan_instance(void) +{ + PFN_vkGetPhysicalDeviceQueueFamilyProperties p_vkGetPhysicalDeviceQueueFamilyProperties; + PFN_vkGetPhysicalDeviceMemoryProperties p_vkGetPhysicalDeviceMemoryProperties; + PFN_vkEnumeratePhysicalDevices p_vkEnumeratePhysicalDevices; + PFN_vkCreateInstance p_vkCreateInstance; + PFN_vkCreateDevice p_vkCreateDevice; + VkPhysicalDevice *vk_physical_devices; + const struct vulkan_funcs *vk_funcs; + VkQueueFamilyProperties *properties; + float priority = 0.0f; + uint32_t count; + VkResult vr; + + static const char *device_extensions[] = + { + "VK_KHR_external_memory", + "VK_KHR_external_memory_fd", + "VK_EXT_map_memory_placed", + "VK_KHR_map_memory2", + }; + VkDeviceQueueCreateInfo queue_info = + { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueCount = 1, + .pQueuePriorities = &priority, + }; + VkDeviceCreateInfo device_info = + { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queue_info, + .enabledExtensionCount = ARRAYSIZE(device_extensions), + .ppEnabledExtensionNames = device_extensions, + }; + VkInstanceCreateInfo create_info = + { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + }; + + if (!(vk_funcs = __wine_get_vulkan_driver( WINE_VULKAN_DRIVER_VERSION ))) return; + + p_vkCreateInstance = (void *)vk_funcs->p_vkGetInstanceProcAddr( NULL, "vkCreateInstance" ); + if ((vr = p_vkCreateInstance( &create_info, NULL, &vk_instance ))) + { + WARN( "Failed to create a Vulkan instance, vr %d.\n", vr ); + return; + } + + p_vkCreateDevice = (void *)vk_funcs->p_vkGetInstanceProcAddr( vk_instance, "vkCreateDevice" ); + p_vkEnumeratePhysicalDevices = (void *)vk_funcs->p_vkGetInstanceProcAddr( vk_instance, "vkEnumeratePhysicalDevices" ); + p_vkGetPhysicalDeviceMemoryProperties = (void *)vk_funcs->p_vkGetInstanceProcAddr( vk_instance, "vkGetPhysicalDeviceMemoryProperties" ); + p_vkGetPhysicalDeviceQueueFamilyProperties = + (void *)vk_funcs->p_vkGetInstanceProcAddr( vk_instance, "vkGetPhysicalDeviceQueueFamilyProperties" ); + + vr = p_vkEnumeratePhysicalDevices( vk_instance, &count, NULL ); + if (!vr && count) + { + vk_physical_devices = calloc( count, sizeof(*vk_physical_devices) ); + vr = p_vkEnumeratePhysicalDevices( vk_instance, &count, vk_physical_devices ); + } + if (vr || !count) + { + WARN( "Could not get vulkan physical devices: %d\n", vr ); + return; + } + + p_vkGetPhysicalDeviceQueueFamilyProperties( vk_physical_devices[0], &count, NULL ); + if (!vr && count) + { + properties = calloc( count, sizeof(*properties) ); + p_vkGetPhysicalDeviceQueueFamilyProperties( vk_physical_devices[0], &count, properties ); + } + if (!count) + { + WARN( "Could not get vulkan device properties: %d\n", vr ); + return; + } + + p_vkGetPhysicalDeviceMemoryProperties( vk_physical_devices[0], &vk_memory_properties ); + vr = p_vkCreateDevice( vk_physical_devices[0], &device_info, NULL, &vk_device ); + if (vr) + { + WARN( "Could not create vulkan device: %d\n", vr ); + return; + } + + p_vkAllocateMemory = (void *)vk_funcs->p_vkGetDeviceProcAddr( vk_device, "vkAllocateMemory" ); + p_vkFreeMemory = (void *)vk_funcs->p_vkGetDeviceProcAddr( vk_device, "vkFreeMemory" ); + p_vkGetMemoryFdKHR = (void *)vk_funcs->p_vkGetDeviceProcAddr( vk_device, "vkGetMemoryFdKHR" ); + p_vkMapMemory2KHR = (void *)vk_funcs->p_vkGetDeviceProcAddr( vk_device, "vkMapMemory2KHR" ); + p_vkUnmapMemory2KHR = (void *)vk_funcs->p_vkGetDeviceProcAddr( vk_device, "vkUnmapMemory2KHR" ); + p_vkFlushMappedMemoryRanges = (void *)vk_funcs->p_vkGetDeviceProcAddr( vk_device, "vkFlushMappedMemoryRanges" ); +} + +static pthread_once_t init_vulkan_once = PTHREAD_ONCE_INIT; + +static void flush_buffer( struct buffer *buffer, size_t offset, size_t length ) +{ + if (buffer->vk_memory) + { + VkMappedMemoryRange memory_range = + { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = buffer->vk_memory, + .offset = (char *)buffer->map_ptr - (char *)buffer->host_ptr + offset, + .size = length, + }; + VkResult vr; + + vr = p_vkFlushMappedMemoryRanges( vk_device, 1, &memory_range ); + if (vr) ERR( "vkFlushMappedMemoryRanges failed: %x\n", vr ); + } +} + +static void unmap_vk_buffer( struct buffer *buffer ) +{ + VkMemoryUnmapInfoKHR unmap_info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_UNMAP_INFO_KHR, + .memory = buffer->vk_memory, + .flags = VK_MEMORY_UNMAP_RESERVE_BIT_EXT, + }; + VkResult vr; + + vr = p_vkUnmapMemory2KHR( vk_device, &unmap_info ); + if (vr) ERR( "VkMemoryUnmapInfoKHR failed: %x\n", vr); +} + +static void buffer_vk_cleanup( const struct opengl_funcs *funcs, struct buffer *buffer ) +{ + if (buffer->vk_memory) + { + if (buffer->map_ptr) unmap_vk_buffer( buffer ); + p_vkFreeMemory( vk_device, buffer->vk_memory, NULL ); + } + if (buffer->gl_memory) funcs->p_glDeleteMemoryObjectsEXT( 1, &buffer->gl_memory ); +} + +static struct buffer *create_buffer_storage( TEB *teb, GLenum target, GLuint name, size_t size, const void *data, GLbitfield flags ) +{ + VkExportMemoryAllocateInfo export_alloc = + { + .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, + }; + VkMemoryAllocateInfo alloc_info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &export_alloc, + .allocationSize = size, + }; + VkMemoryGetFdInfoKHR fd_info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, + }; + struct opengl_funcs *funcs = teb->glTable; + GLuint buffer_name = name ? name : get_target_name( teb, target ); + struct buffer *buffer; + struct context *ctx; + uint32_t i; + int fd; + VkResult vr; + + if (!(ctx = get_current_context( teb, NULL, NULL )) || !buffer_name) return NULL; + + pthread_once( &init_vulkan_once, init_vulkan_instance ); + if (!vk_device) return NULL; + + if (!funcs->p_glCreateMemoryObjectsEXT) + funcs->p_glCreateMemoryObjectsEXT = (void *)funcs->p_wglGetProcAddress( "glCreateMemoryObjectsEXT" ); + if (!funcs->p_glDeleteMemoryObjectsEXT) + funcs->p_glDeleteMemoryObjectsEXT = (void *)funcs->p_wglGetProcAddress( "glDeleteMemoryObjectsEXT" ); + if (!funcs->p_glImportMemoryFdEXT) + funcs->p_glImportMemoryFdEXT = (void *)funcs->p_wglGetProcAddress( "glImportMemoryFdEXT" ); + if (!funcs->p_glBufferStorageMemEXT) + funcs->p_glBufferStorageMemEXT = (void *)funcs->p_wglGetProcAddress( "glBufferStorageMemEXT" ); + if (!funcs->p_glNamedBufferStorageMemEXT) + funcs->p_glNamedBufferStorageMemEXT = (void *)funcs->p_wglGetProcAddress( "glNamedBufferStorageMemEXT" ); + + /* FIXME: For now, just use any host-visible coherent memory type. We can do better and take into account GL flags. */ + for (i = 0; i < vk_memory_properties.memoryTypeCount; i++) + { + static const uint32_t mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + if ((vk_memory_properties.memoryTypes[i].propertyFlags & mask) == mask) break; + } + if (i == vk_memory_properties.memoryTypeCount) + { + WARN( "Could not find memory type\n" ); + return NULL; + } + alloc_info.memoryTypeIndex = i; + + if (!(buffer = calloc( 1, sizeof(*buffer) ))) return NULL; + buffer->cleanup = buffer_vk_cleanup; + buffer->name = buffer_name; + buffer->flags = flags; + buffer->size = size; + + vr = p_vkAllocateMemory( vk_device, &alloc_info, NULL, &buffer->vk_memory ); + if (vr) + { + ERR( "vkAllocateMemory failed: %d\n", vr ); + free_buffer( funcs, buffer ); + return NULL; + } + + if (data) + { + VkMemoryMapInfoKHR map_info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_MAP_INFO_KHR, + .memory = buffer->vk_memory, + .size = VK_WHOLE_SIZE, + }; + VkMemoryUnmapInfoKHR unmap_info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_UNMAP_INFO_KHR, + .memory = buffer->vk_memory, + }; + void *ptr; + + vr = p_vkMapMemory2KHR( vk_device, &map_info, &ptr ); + if (vr) + { + ERR( "vkMapMemory2KHR failed: %d\n", vr ); + free_buffer( funcs, buffer ); + return NULL; + } + + memcpy( ptr, data, size ); + p_vkUnmapMemory2KHR( vk_device, &unmap_info ); + } + + fd_info.memory = buffer->vk_memory; + vr = p_vkGetMemoryFdKHR( vk_device, &fd_info, &fd ); + if (vr) + { + ERR( "vkGetMemoryFdKHR failed: %d\n", vr ); + free_buffer( funcs, buffer ); + return NULL; + } + + funcs->p_glCreateMemoryObjectsEXT( 1, &buffer->gl_memory ); + funcs->p_glImportMemoryFdEXT( buffer->gl_memory, size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd ); + if (name) + funcs->p_glNamedBufferStorageMemEXT( buffer->name, size, buffer->gl_memory, 0 ); + else + funcs->p_glBufferStorageMemEXT( target, size, buffer->gl_memory, 0 ); + rb_put( &ctx->buffers->map, &buffer->name, &buffer->entry ); + TRACE( "imported fd %d using buffer %p\n", fd, buffer ); + return buffer; +} + +static PTR32 wow64_map_buffer( TEB *teb, struct buffer *buffer, GLenum target, GLuint name, GLintptr offset, size_t length, GLbitfield access, void *ptr ) { - struct buffer *buffer = get_target_buffer( teb, target ); + if (buffer && buffer->map_ptr) + { + set_gl_error( teb, GL_INVALID_OPERATION ); + return 0; + } + + if (buffer && buffer->vk_memory) + { + VkMemoryMapPlacedInfoEXT placed_info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_MAP_PLACED_INFO_EXT, + }; + VkMemoryMapInfoKHR map_info = + { + .sType = VK_STRUCTURE_TYPE_MEMORY_MAP_INFO_KHR, + .flags = VK_MEMORY_MAP_PLACED_BIT_EXT, + .pNext = &placed_info, + .memory = buffer->vk_memory, + .size = VK_WHOLE_SIZE, + }; + VkResult vr; + + if (!buffer_vm_alloc( teb, buffer, buffer->size )) return 0; + placed_info.pPlacedAddress = buffer->vm_ptr; + vr = p_vkMapMemory2KHR( vk_device, &map_info, &buffer->host_ptr ); + if (vr) + { + ERR( "vkMapMemory2KHR failed: %d\n", vr ); + return 0; + } + assert( buffer->host_ptr == buffer->vm_ptr ); + buffer->map_ptr = (char *)buffer->vm_ptr + offset; + TRACE( "returning vk mapping %p\n", buffer->map_ptr ); + return PtrToUlong( buffer->map_ptr ); + }
if (!ptr) return 0;
@@ -2319,6 +2628,39 @@ void wow64_glDeleteBuffers( TEB *teb, GLsizei n, const GLuint *buffers ) pthread_mutex_unlock( &wgl_lock ); }
+void wow64_glBufferStorage( TEB *teb, GLenum target, GLsizeiptr size, const void *data, GLbitfield flags ) +{ + const struct opengl_funcs *funcs = teb->glTable; + struct buffer *buffer = NULL; + + if (flags & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) + buffer = create_buffer_storage( teb, target, 0, size, data, flags ); + + if (!buffer) funcs->p_glBufferStorage( target, size, data, flags ); +} + +void wow64_glNamedBufferStorage( TEB *teb, GLuint name, GLsizeiptr size, const void *data, GLbitfield flags ) +{ + const struct opengl_funcs *funcs = teb->glTable; + struct buffer *buffer = NULL; + + if (flags & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) + buffer = create_buffer_storage( teb, 0, name, size, data, flags ); + + if (!buffer) funcs->p_glNamedBufferStorage( name, size, data, flags ); +} + +void wow64_glNamedBufferStorageEXT( TEB *teb, GLuint name, GLsizeiptr size, const void *data, GLbitfield flags ) +{ + const struct opengl_funcs *funcs = teb->glTable; + struct buffer *buffer = NULL; + + if (flags & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) + buffer = create_buffer_storage( teb, 0, name, size, data, flags ); + + if (!buffer) funcs->p_glNamedBufferStorageEXT( name, size, data, flags ); +} + static BOOL wow64_gl_get_buffer_pointer_v( TEB *teb, GLenum target, GLuint name, GLenum pname, PTR32 *wow_ptr ) { struct buffer *buffer; @@ -2376,12 +2718,14 @@ void wow64_glGetNamedBufferPointervEXT( TEB *teb, GLuint buffer, GLenum pname, P static PTR32 wow64_gl_map_buffer( TEB *teb, GLenum target, GLenum access, PFN_glMapBuffer gl_map_buffer64 ) { GLbitfield range_access = map_range_flags_from_map_flags( access ); - void *ptr; - PTR32 ret; + struct buffer *buffer; + void *ptr = NULL; + PTR32 ret ;
pthread_mutex_lock( &wgl_lock ); - ptr = gl_map_buffer64( target, access ); - ret = wow64_map_buffer( teb, target, 0, 0, 0, range_access, ptr ); + buffer = get_target_buffer( teb, target ); + if (!buffer || !buffer->vk_memory) ptr = gl_map_buffer64( target, access ); + ret = wow64_map_buffer( teb, buffer, target, 0, 0, 0, range_access, ptr ); pthread_mutex_unlock( &wgl_lock ); return ret; } @@ -2401,12 +2745,14 @@ PTR32 wow64_glMapBufferARB( TEB *teb, GLenum target, GLenum access ) PTR32 wow64_glMapBufferRange( TEB *teb, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access ) { const struct opengl_funcs *funcs = teb->glTable; - void *ptr; + struct buffer *buffer; + void *ptr = NULL; PTR32 ret;
pthread_mutex_lock( &wgl_lock ); - ptr = funcs->p_glMapBufferRange( target, offset, length, access ); - ret = wow64_map_buffer( teb, target, 0, offset, length, access, ptr ); + buffer = get_target_buffer( teb, target ); + if (!buffer || !buffer->vk_memory) ptr = funcs->p_glMapBufferRange( target, offset, length, access ); + ret = wow64_map_buffer( teb, buffer, target, 0, offset, length, access, ptr ); pthread_mutex_unlock( &wgl_lock ); return ret; } @@ -2414,12 +2760,14 @@ PTR32 wow64_glMapBufferRange( TEB *teb, GLenum target, GLintptr offset, GLsizeip static PTR32 wow64_gl_map_named_buffer( TEB *teb, GLuint name, GLenum access, PFN_glMapNamedBuffer gl_map_named_buffer64 ) { GLbitfield range_access = map_range_flags_from_map_flags( access ); - void *ptr; + struct buffer *buffer; + void *ptr = NULL; PTR32 ret;
pthread_mutex_lock( &wgl_lock ); - ptr = gl_map_named_buffer64( name, access ); - ret = wow64_map_buffer( teb, 0, name, 0, 0, range_access, ptr ); + buffer = get_named_buffer( teb, name ); + if (!buffer || !buffer->vk_memory) ptr = gl_map_named_buffer64( name, access ); + ret = wow64_map_buffer( teb, buffer, 0, name, 0, 0, range_access, ptr ); pthread_mutex_unlock( &wgl_lock ); return ret; } @@ -2439,12 +2787,14 @@ PTR32 wow64_glMapNamedBufferEXT( TEB *teb, GLuint buffer, GLenum access ) static NTSTATUS wow64_gl_map_named_buffer_range( TEB *teb, GLuint name, GLintptr offset, GLsizeiptr length, GLbitfield access, PFN_glMapNamedBufferRange gl_map_named_buffer_range64 ) { - void *ptr; + struct buffer *buffer; + void *ptr = NULL; PTR32 ret;
pthread_mutex_lock( &wgl_lock ); - ptr = gl_map_named_buffer_range64( name, offset, length, access ); - ret = wow64_map_buffer( teb, 0, name, offset, length, access, ptr ); + buffer = get_named_buffer( teb, name ); + if (!buffer || !buffer->vk_memory) ptr = gl_map_named_buffer_range64( name, offset, length, access ); + ret = wow64_map_buffer( teb, buffer, 0, name, offset, length, access, ptr ); pthread_mutex_unlock( &wgl_lock ); return ret; } @@ -2461,9 +2811,10 @@ PTR32 wow64_glMapNamedBufferRangeEXT( TEB *teb, GLuint buffer, GLintptr offset, return wow64_gl_map_named_buffer_range( teb, buffer, offset, length, access, funcs->p_glMapNamedBufferRangeEXT ); }
-static void wow64_unmap_buffer( struct buffer *buffer ) +static BOOL wow64_unmap_buffer( struct buffer *buffer ) { - if (!buffer->host_ptr) return; + if (!buffer->map_ptr) return FALSE; + if (buffer->vk_memory) unmap_vk_buffer( buffer );
if (buffer->host_ptr != buffer->map_ptr) { @@ -2476,6 +2827,7 @@ static void wow64_unmap_buffer( struct buffer *buffer ) }
buffer->host_ptr = buffer->map_ptr = NULL; + return TRUE; }
static GLboolean wow64_unmap_target_buffer( TEB *teb, GLenum target, PFN_glUnmapBuffer gl_unmap ) @@ -2484,8 +2836,8 @@ static GLboolean wow64_unmap_target_buffer( TEB *teb, GLenum target, PFN_glUnmap GLboolean ret;
pthread_mutex_lock( &wgl_lock ); - if ((buffer = get_target_buffer( teb, target ))) wow64_unmap_buffer( buffer ); - ret = gl_unmap( target ); + if ((buffer = get_target_buffer( teb, target ))) ret = wow64_unmap_buffer( buffer ); + if (!buffer || !buffer->vk_memory) ret = gl_unmap( target ); pthread_mutex_unlock( &wgl_lock ); return ret; } @@ -2508,8 +2860,8 @@ static GLboolean wow64_gl_unmap_named_buffer( TEB *teb, GLuint name, PFN_glUnmap GLboolean ret;
pthread_mutex_lock( &wgl_lock ); - if ((buffer = get_named_buffer( teb, name ))) wow64_unmap_buffer( buffer ); - ret = gl_unmap( name ); + if ((buffer = get_named_buffer( teb, name ))) ret = wow64_unmap_buffer( buffer ); + if (!buffer || !buffer->vk_memory) ret = gl_unmap( name ); pthread_mutex_unlock( &wgl_lock ); return ret; } @@ -2526,6 +2878,39 @@ GLboolean wow64_glUnmapNamedBufferEXT( TEB *teb, GLuint buffer ) return wow64_gl_unmap_named_buffer( teb, buffer, funcs->p_glUnmapNamedBufferEXT ); }
+void wow64_glFlushMappedBufferRange( TEB *teb, GLenum target, GLintptr offset, GLsizeiptr length ) +{ + const struct opengl_funcs *funcs = teb->glTable; + struct buffer *buffer; + + pthread_mutex_lock( &wgl_lock ); + if ((buffer = get_target_buffer( teb, target ))) flush_buffer( buffer, offset, length ); + if (!buffer || !buffer->vk_memory) funcs->p_glFlushMappedBufferRange( target, offset, length ); + pthread_mutex_unlock( &wgl_lock ); +} + +void wow64_glFlushMappedNamedBufferRange( TEB *teb, GLuint name, GLintptr offset, GLsizeiptr length ) +{ + const struct opengl_funcs *funcs = teb->glTable; + struct buffer *buffer; + + pthread_mutex_lock( &wgl_lock ); + if ((buffer = get_named_buffer( teb, name ))) flush_buffer( buffer, offset, length ); + if (!buffer || !buffer->vk_memory) funcs->p_glFlushMappedNamedBufferRange( name, offset, length ); + pthread_mutex_unlock( &wgl_lock ); +} + +void wow64_glFlushMappedNamedBufferRangeEXT( TEB *teb, GLuint name, GLintptr offset, GLsizeiptr length ) +{ + const struct opengl_funcs *funcs = teb->glTable; + struct buffer *buffer; + + pthread_mutex_lock( &wgl_lock ); + if ((buffer = get_named_buffer( teb, name ))) flush_buffer( buffer, offset, length ); + if (!buffer || !buffer->vk_memory) funcs->p_glFlushMappedNamedBufferRangeEXT( name, offset, length ); + pthread_mutex_unlock( &wgl_lock ); +} + NTSTATUS wow64_thread_attach( void *args ) { return thread_attach( get_teb64( (ULONG_PTR)args ));
Looking at it a bit it seems to me that the [GL_AMD_pinned_memory](https://registry.khronos.org/OpenGL/extensions/AMD/AMD_pinned_memory.txt) extension could be used to do what we want. It's implemented in Mesa (including for Intel), but apparently not on Nvidia.
The approach here would work for every vendor that supports Vulkan host external memory, so probably more widespread, but it's IMO a bit awkward as it requires to have Vulkan in the first place. If we have Vulkan then it would probably be better to use the Vulkan D3D backend. Then sure, it's not yet covering the same surface as the OpenGL backend. For native OpenGL games, although I don't know any which suffer from the same performance issues as WineD3D, then the Zink route is perhaps another choice.
This is mostly for the sake of the discussion, and I'm not saying that this approach is bad or unnecessary, it seems simple enough to be acceptable to me.
Looking at it a bit it seems to me that the [GL_AMD_pinned_memory](https://registry.khronos.org/OpenGL/extensions/AMD/AMD_pinned_memory.txt) extension could be used to do what we want. It's implemented in Mesa (including for Intel), but apparently not on Nvidia.
Good point, thanks. It does seem possible to use that extension, but I think we also need to support Nvidia. We could maybe have it as an alternative, though I’m not sure there are cases where it would actually help (that is, cases where the extension is available but a sufficient Vulkan driver isn’t).
It also appears to force the driver to use system memory for the buffer, while with Vulkan we can control and tune that ourselves.
If we have Vulkan then it would probably be better to use the Vulkan D3D backend.
Yes, ideally we’d always use Vulkan for D3D, I believe that’s a work in progress. While D3D performance is the most visible effect of the lack of persistent memory support, I think we'd still want it for pure OpenGL applications as well.
For native OpenGL games, although I don't know any which suffer from the same performance issues as WineD3D
Note that it’s not just about performance, we’re also missing features that some applications may require. Unlike WineD3D, which has a fallback, persistent mappings can be a required feature for an application. We also currently need to limit the OpenGL version, which may itself be a requirement.
the Zink route is perhaps another choice.
Yes, that’s an interesting alternative. With PE Zink we wouldn’t need any of that. However, I don’t think we can bundle Zink with Wine itself, so it wouldn’t be an out-of-the-box solution.