From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/opengl32/make_opengl | 72 +++++++++++++++++++- dlls/opengl32/private.h | 17 +++++ dlls/opengl32/thunks.c | 18 ++++- dlls/opengl32/wgl.c | 137 +++++++++++++++++++++++++++++++++++++- 4 files changed, 237 insertions(+), 7 deletions(-) diff --git a/dlls/opengl32/make_opengl b/dlls/opengl32/make_opengl index 01dd4081ec5..f1741033d62 100755 --- a/dlls/opengl32/make_opengl +++ b/dlls/opengl32/make_opengl @@ -678,19 +678,40 @@ sub get_handle_function($) return 0; } +sub get_object_type($$$$) +{ + my ($func, $ptype, $pname, $class) = @_; + + return "OBJ_TYPE_BUFFER" if $class eq "buffer"; + return "OBJ_TYPE_BUFFER" if $func eq "glDeleteObjectBufferATI" and $pname eq "buffer"; + return "OBJ_TYPE_BUFFER" if $func eq "glDrawCommandsNV" and $pname eq "buffer"; + return 0 if $class eq "framebuffer"; + return 0 if $class eq "renderbuffer"; + + return 0 if $class eq "SelectName"; + return 0 if $func eq "glGetActiveAtomicCounterBufferiv" and $pname eq "bufferIndex"; + return 0 if $func =~ /^glBlend(Equation|Func)/ and $pname eq "buf"; + return 0 if $func =~ /^gl(Signal|Wait)Semaphore/ and $pname eq "numBufferBarriers"; + return 0 if $ptype !~ /GLuint|GLhandleARB/; + + print "Missing possible buffer: $func $ptype $pname $class\n" if lc( $pname ) =~ /buffer/; + return 0; +} + sub generate_win_thunk($$) { my ($name, $func) = @_; my $get_integer = get_integer_call( $name, $func ); my $decl_args = get_func_args( $func ); my $func_ret = get_func_ret( $func ); + my $post_call = ""; + my $map_args = ""; my $params = ""; my $checks = ""; my $ret = ""; $ret .= "$func_ret WINAPI $name($decl_args)\n"; $ret .= "{\n"; - $ret .= " struct $name\_params args"; $params .= " .teb = NtCurrentTeb()"; foreach my $arg (@{$func->[1]}) { @@ -702,19 +723,52 @@ sub generate_win_thunk($$) $retval = "{ set_gl_error( GL_INVALID_VALUE ); $retval }" if $ptype =~ /GLsync/ && $name !~ /glIsSync/; $checks .= " if (!$get_handle( $pname, &args.$pname )) $retval\n"; } + elsif (my $map_type = get_object_type( $name, $ptype, $pname, get_arg_class( $arg ) )) + { + my $len = get_arg_len( $arg ); + my $addr = $len eq "1" ? "&" : ""; + my $deref = $len eq "1" ? "*" : ""; + + if ($name =~ /^(glGen|glCreate)/) + { + $params .= ", .$pname = $pname"; + $post_call .= " if ($len > 0) put_context_objects( $map_type, $len, $addr$pname );\n"; + } + elsif ($name =~ /^glDelete/) + { + if ($len eq "1") + { + $map_args .= " args.$pname = *del_context_objects( $map_type, 1, &$pname );\n"; + } + else + { + my ($buf, $tmp) = ($pname . "_buf", $pname . "_tmp"); + $ret .= " GLuint $buf\[64\], *$tmp;\n"; + + $map_args .= " $tmp = $len > 0 ? memdup_objects( $len, $pname, $buf, ARRAY_SIZE($buf) ) : NULL;\n"; + $map_args .= " args.$pname = $len > 0 ? del_context_objects( $map_type, $len, $tmp ) : NULL;\n"; + $post_call .= " if ($tmp != $buf) free( $tmp );\n"; + } + } + else + { + $params .= ", .$pname = $pname"; + } + } else { $params .= ", .$pname = $pname"; } } - $ret .= " = {$params }"; - $ret .= ";\n"; + $ret .= " struct $name\_params args = {$params };\n"; $ret .= " NTSTATUS status;\n"; $ret .= " int integer;\n" if $get_integer; $ret .= " " . get_func_trace( $name, $func, 1 ); $ret .= $checks; + $ret .= $map_args; $ret .= $get_integer ? " $get_integer\n else " : " "; $ret .= "if ((status = UNIX_CALL( $name, &args ))) WARN( \"$name returned %#lx\\n\", status );\n"; + $ret .= $post_call; $ret .= " return args.ret;\n" unless is_void_func($func); $ret .= "}\n"; @@ -818,6 +872,18 @@ sub get_arg_name($) return $name[0]->textContent(); } +sub get_arg_class($) +{ + my $p = shift; + return $p->{class} || $p->{kind} || ""; +} + +sub get_arg_len($) +{ + my $p = shift; + return $p->{len} || "1"; +} + # # Fetch the registry files # diff --git a/dlls/opengl32/private.h b/dlls/opengl32/private.h index aa14924d1a8..9bb9e8b77a9 100644 --- a/dlls/opengl32/private.h +++ b/dlls/opengl32/private.h @@ -46,4 +46,21 @@ extern void set_gl_error( GLenum error ); extern struct registry_entry *get_function_entry( const char *name ); extern BOOL get_integer( GLenum name, GLint *data ); +enum object_type +{ + OBJ_TYPE_BUFFER, + OBJ_TYPE_COUNT, +}; + +static inline GLuint *memdup_objects( UINT n, const GLuint *handles, GLuint *buf, UINT max ) +{ + GLuint *tmp = buf; + if (n > max && !(tmp = malloc( n * sizeof(*handles) ))) return NULL; + memcpy( tmp, handles, n * sizeof(*handles) ); + return tmp; +} + +extern void put_context_objects( enum object_type type, UINT n, GLuint *handles ); +extern GLuint *del_context_objects( enum object_type type, UINT n, GLuint *handles ); + #endif /* __WINE_OPENGL32_PRIVATE_H */ diff --git a/dlls/opengl32/thunks.c b/dlls/opengl32/thunks.c index 134aecb3221..774c894d91c 100644 --- a/dlls/opengl32/thunks.c +++ b/dlls/opengl32/thunks.c @@ -5322,6 +5322,7 @@ static void WINAPI glCreateBuffers( GLsizei n, GLuint *buffers ) NTSTATUS status; TRACE( "n %d, buffers %p\n", n, buffers ); if ((status = UNIX_CALL( glCreateBuffers, &args ))) WARN( "glCreateBuffers returned %#lx\n", status ); + if (n > 0) put_context_objects( OBJ_TYPE_BUFFER, n, buffers ); } static void WINAPI glCreateCommandListsNV( GLsizei n, GLuint *lists ) @@ -5629,18 +5630,26 @@ static void WINAPI glDeleteBufferRegion( GLenum region ) static void WINAPI glDeleteBuffers( GLsizei n, const GLuint *buffers ) { - struct glDeleteBuffers_params args = { .teb = NtCurrentTeb(), .n = n, .buffers = buffers }; + GLuint buffers_buf[64], *buffers_tmp; + struct glDeleteBuffers_params args = { .teb = NtCurrentTeb(), .n = n }; NTSTATUS status; TRACE( "n %d, buffers %p\n", n, buffers ); + buffers_tmp = n > 0 ? memdup_objects( n, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL; + args.buffers = n > 0 ? del_context_objects( OBJ_TYPE_BUFFER, n, buffers_tmp ) : NULL; if ((status = UNIX_CALL( glDeleteBuffers, &args ))) WARN( "glDeleteBuffers returned %#lx\n", status ); + if (buffers_tmp != buffers_buf) free( buffers_tmp ); } static void WINAPI glDeleteBuffersARB( GLsizei n, const GLuint *buffers ) { - struct glDeleteBuffersARB_params args = { .teb = NtCurrentTeb(), .n = n, .buffers = buffers }; + GLuint buffers_buf[64], *buffers_tmp; + struct glDeleteBuffersARB_params args = { .teb = NtCurrentTeb(), .n = n }; NTSTATUS status; TRACE( "n %d, buffers %p\n", n, buffers ); + buffers_tmp = n > 0 ? memdup_objects( n, buffers, buffers_buf, ARRAY_SIZE(buffers_buf) ) : NULL; + args.buffers = n > 0 ? del_context_objects( OBJ_TYPE_BUFFER, n, buffers_tmp ) : NULL; if ((status = UNIX_CALL( glDeleteBuffersARB, &args ))) WARN( "glDeleteBuffersARB returned %#lx\n", status ); + if (buffers_tmp != buffers_buf) free( buffers_tmp ); } static void WINAPI glDeleteCommandListsNV( GLsizei n, const GLuint *lists ) @@ -5725,9 +5734,10 @@ static void WINAPI glDeleteObjectARB( GLhandleARB obj ) static void WINAPI glDeleteObjectBufferATI( GLuint buffer ) { - struct glDeleteObjectBufferATI_params args = { .teb = NtCurrentTeb(), .buffer = buffer }; + struct glDeleteObjectBufferATI_params args = { .teb = NtCurrentTeb() }; NTSTATUS status; TRACE( "buffer %d\n", buffer ); + args.buffer = *del_context_objects( OBJ_TYPE_BUFFER, 1, &buffer ); if ((status = UNIX_CALL( glDeleteObjectBufferATI, &args ))) WARN( "glDeleteObjectBufferATI returned %#lx\n", status ); } @@ -7475,6 +7485,7 @@ static void WINAPI glGenBuffers( GLsizei n, GLuint *buffers ) NTSTATUS status; TRACE( "n %d, buffers %p\n", n, buffers ); if ((status = UNIX_CALL( glGenBuffers, &args ))) WARN( "glGenBuffers returned %#lx\n", status ); + if (n > 0) put_context_objects( OBJ_TYPE_BUFFER, n, buffers ); } static void WINAPI glGenBuffersARB( GLsizei n, GLuint *buffers ) @@ -7483,6 +7494,7 @@ static void WINAPI glGenBuffersARB( GLsizei n, GLuint *buffers ) NTSTATUS status; TRACE( "n %d, buffers %p\n", n, buffers ); if ((status = UNIX_CALL( glGenBuffersARB, &args ))) WARN( "glGenBuffersARB returned %#lx\n", status ); + if (n > 0) put_context_objects( OBJ_TYPE_BUFFER, n, buffers ); } static void WINAPI glGenFencesAPPLE( GLsizei n, GLuint *fences ) diff --git a/dlls/opengl32/wgl.c b/dlls/opengl32/wgl.c index e698a384c99..da35e355332 100644 --- a/dlls/opengl32/wgl.c +++ b/dlls/opengl32/wgl.c @@ -101,6 +101,16 @@ static void cleanup_wow64_strings(void) #endif +static const char *debugstr_object_type( enum object_type type ) +{ + switch (type) + { + case OBJ_TYPE_BUFFER: return "buffer"; + case OBJ_TYPE_COUNT: break; + } + return wine_dbg_sprintf( "object (type %u)", type ); +} + static void init_wgl_extensions( const BOOLEAN extensions[GL_EXTENSION_COUNT] ) { UINT pos = 0, len = 0, ext; @@ -259,9 +269,92 @@ BOOL WINAPI wglDestroyPbufferARB( HPBUFFERARB handle ) return args.ret; } +#define L1_COUNT 0x80 +#define L2_COUNT 0x400 +#define L3_COUNT 0x8000 + +struct object_table +{ + enum object_type type; /* object type of the id table */ + SRWLOCK lock; /* lock for accessing the table */ + GLuint **host_ids[L1_COUNT]; /* client -> host id mapping sparse array */ +}; + +static GLuint *find_object_id( GLuint **ids[L1_COUNT], GLuint client_id ) +{ + GLuint i = client_id / L3_COUNT / L2_COUNT, j = (client_id / L3_COUNT) % L2_COUNT, k = client_id % L3_COUNT; + return ids[i] ? ids[i][j] ? ids[i][j] + k : NULL : NULL; +} + +static GLuint *alloc_object_ids( GLuint **ids[L1_COUNT], GLuint client_id ) +{ + GLuint i = client_id / L3_COUNT / L2_COUNT, j = (client_id / L3_COUNT) % L2_COUNT; + GLuint **ptr; + + if (!(ptr = ids[i]) && !(ptr = ids[i] = calloc( L2_COUNT, sizeof(*ptr) ))) return NULL; + if (!ptr[j] && !(ptr[j] = calloc( L3_COUNT, sizeof(*ptr[j]) ))) return NULL; + return ptr[j]; +} + +static void free_object_ids( struct object_table *table, GLuint **ids[L1_COUNT] ) +{ + GLuint **l1_block, *l2_block; + + for (int i = 0; i < L1_COUNT; i++) + { + if (!(l1_block = ids[i])) continue; + for (int j = 0; j < L2_COUNT; j++) + { + if (!(l2_block = l1_block[j])) continue; + free( l2_block ); + } + free( l1_block ); + } +} + +static GLuint set_object( struct object_table *table, GLuint client_id, GLuint host_id ) +{ + GLuint *ids; + + if (!(ids = alloc_object_ids( table->host_ids, client_id ))) goto failed; + ids[client_id % L3_COUNT] = host_id; + + TRACE( "Inserted %s client %#x, host %#x\n", debugstr_object_type( table->type ), client_id, host_id ); + return client_id; + +failed: + ERR( "Failed to allocate object id block\n" ); + return -1; +} + +static GLuint del_object( struct object_table *table, GLuint client_id ) +{ + GLuint *object, host_id = 0; + + if (!client_id || !(object = find_object_id( table->host_ids, client_id ))) return client_id; + host_id = *object; + *object = 0; + + TRACE( "Deleting %s client %#x, host %#x\n", debugstr_object_type( table->type ), client_id, host_id ); + return host_id ? host_id : client_id; +} + +static void free_object_table( struct object_table *table ) +{ + free_object_ids( table, table->host_ids ); +} + +static void init_object_table( struct object_table *table, enum object_type type ) +{ + InitializeSRWLock( &table->lock ); + table->type = type; +} + struct display_lists { - LONG refcount; + LONG refcount; + LONG modified; + struct object_table tables[OBJ_TYPE_COUNT]; }; static struct display_lists *display_lists_create(void) @@ -271,6 +364,9 @@ static struct display_lists *display_lists_create(void) if (!(lists = calloc( 1, sizeof(*lists) ))) return NULL; lists->refcount = 1; + for (UINT i = 0; i < OBJ_TYPE_COUNT; i++) + init_object_table( lists->tables + i, i ); + return lists; } @@ -283,6 +379,10 @@ static struct display_lists *display_lists_acquire( struct display_lists *lists static void display_lists_release( struct display_lists *lists ) { if (InterlockedDecrement( &lists->refcount )) return; + + for (UINT i = 0; i < OBJ_TYPE_COUNT; i++) + free_object_table( lists->tables + i ); + free( lists ); } @@ -365,6 +465,40 @@ void set_gl_error( GLenum error ) if (!context->last_error && !(context->last_error = glGetError())) context->last_error = error; } +static struct object_table *get_object_table( struct context *ctx, enum object_type type, BOOL write ) +{ + if (write) InterlockedExchange( &ctx->lists->modified, 1 ); + return type < OBJ_TYPE_COUNT ? ctx->lists->tables + type : NULL; +} + +void put_context_objects( enum object_type type, UINT n, GLuint *handles ) +{ + struct object_table *table; + struct context *ctx; + + if (!(ctx = context_from_handle( NtCurrentTeb()->glCurrentRC ))) return; + if (!(table = get_object_table( ctx, type, TRUE ))) return; + + AcquireSRWLockExclusive( &table->lock ); + for (UINT i = 0; i < n; i++) handles[i] = handles[i] ? set_object( table, handles[i], handles[i] ) : 0; + ReleaseSRWLockExclusive( &table->lock ); +} + +GLuint *del_context_objects( enum object_type type, UINT n, GLuint *handles ) +{ + struct object_table *table; + struct context *ctx; + + if (!(ctx = context_from_handle( NtCurrentTeb()->glCurrentRC ))) return handles; + if (!(table = get_object_table( ctx, type, FALSE ))) return handles; + + AcquireSRWLockExclusive( &table->lock ); + for (UINT i = 0; i < n; i++) handles[i] = del_object( table, handles[i] ); + ReleaseSRWLockExclusive( &table->lock ); + + return handles; +} + HGLRC WINAPI wglCreateContext( HDC hdc ) { static const int attribs[] = { WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, 0, 0 }; @@ -482,6 +616,7 @@ BOOL WINAPI wglShareLists( HGLRC src_handle, HGLRC dst_handle ) if (!(src_context = context_from_handle( src_handle ))) return FALSE; if (!(dst_context = context_from_handle( dst_handle ))) return FALSE; + if (ReadNoFence( &dst_context->lists->modified )) return FALSE; args.hrcSrvShare = &src_context->base.obj; args.hrcSrvSource = &dst_context->base.obj; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10870